From 02b09d09fdbe9e32f50d694905641a7c411dab3a Mon Sep 17 00:00:00 2001 From: Konstantinos Paparas Date: Wed, 29 Apr 2026 15:06:46 +0200 Subject: [PATCH 1/2] fix(text-field): prevent floated label overshoot on blur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the active+outlined compound's percentage translate (`-translate-y-1/2`) with a fixed-rem translate (`-translate-y-[0.5rem]`, ≈half the active label's natural height of `text-[0.75rem] × leading-tight`). The percentage translate was recomputed against the *current* label height. When `active` flips to false on blur, the class swap removes `!h-auto` instantly so `label.height` jumps from ~15px (h-auto) to the input's full height (~56px), and 50% of the new height (~28px) becomes the new starting transform. The transition then animates from -28px to 0 over duration-75, producing a visible upward overshoot before easing back. Anchoring the translate in rem keeps the start/end values consistent across the height swap and scales with the root font-size. Refs #527 --- .../components/forms/text-field/text-field-styles.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/ui-library/src/components/forms/text-field/text-field-styles.ts b/packages/ui-library/src/components/forms/text-field/text-field-styles.ts index 1d7c1254..1f91be4c 100644 --- a/packages/ui-library/src/components/forms/text-field/text-field-styles.ts +++ b/packages/ui-library/src/components/forms/text-field/text-field-styles.ts @@ -145,7 +145,16 @@ export const textFieldStyles = tv({ // --- Outlined variant --- { variant: 'outlined', active: true, class: { input: 'border-t-transparent', - label: '!h-auto -translate-y-1/2 pl-4', + // Use a fixed translate in rem (≈50% of the active label's + // 0.9375rem height — text-[0.75rem] × leading-tight). A percentage + // translate gets recomputed against the *current* label height, + // which jumps from 0.9375rem to the input's full height the moment + // !h-auto is removed on blur — producing a transient transform far + // larger than the floated value and a visible upward overshoot + // before the transition settles. Anchoring in rem (rather than %) + // keeps the start/end values consistent across the height swap and + // scales with the root font-size. + label: '!h-auto -translate-y-[0.5rem] pl-4', } }, { variant: 'outlined', dense: true, class: { input: 'py-2', label: 'leading-[2.5]' } }, From 34a0ef1803a223852c8eeb8fbf291aed3f6220dc Mon Sep 17 00:00:00 2001 From: Konstantinos Paparas Date: Wed, 29 Apr 2026 15:07:18 +0200 Subject: [PATCH 2/2] fix(text-field): eliminate sub-pixel AA splitting of focused border MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `transform-gpu` to the fieldset slot so the focus border is rasterized on integer pixel boundaries. At fractional y-coordinates with non-integer device pixel ratios (e.g. DPR 1.25), Chromium anti-aliases the 1.6–2px focus border across two physical pixel rows behind the floated label, appearing as "two thin blue lines crossing the label". Promoting the fieldset to its own GPU compositing layer snaps the layer's raster to integer pixels and eliminates the splitting. No layout or geometry change — only forces a layer. Refs #527 --- .../ui-library/src/components/forms/text-input-styles.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/ui-library/src/components/forms/text-input-styles.ts b/packages/ui-library/src/components/forms/text-input-styles.ts index ba33f8fb..2c4b8a91 100644 --- a/packages/ui-library/src/components/forms/text-input-styles.ts +++ b/packages/ui-library/src/components/forms/text-input-styles.ts @@ -45,6 +45,14 @@ export const textInputBase = tv({ 'rounded pointer-events-none px-2 transition-all -mt-2', 'border border-black/[0.23]', 'dark:border-white/[0.23]', + // Force the fieldset onto its own GPU compositing layer so the + // focused border is rasterized on integer pixel boundaries. Without + // this, at fractional y-coordinates with non-integer device pixel + // ratios (e.g. DPR 1.25), Chromium anti-aliases the 1.6px-2px focus + // border across two physical pixel rows behind the floated label, + // appearing as "two thin lines crossing the label". translateZ(0) + // promotes to a layer; the layer's raster snaps to integer pixels. + 'transform-gpu', ].join(' '), legend: 'invisible text-[0.75rem] truncate [max-width:calc(100%-1rem)] leading-[0]', },