mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(ui): give date and time fields the standard focus ring
DateField and TimeField carried a divergent focus vocabulary — a hard `ring-2 ring-offset-2` that painted a 4 px ring outside the field's border box, so the highlight was visibly wider than the div and shifted on focus. Pull both onto the Input vocabulary: a soft 3 px ring hugging the box, no offset. Also match Input's `text-base md:text-sm`, so a sub-16 px input no longer triggers iOS Safari's focus auto-zoom. Every DateField and TimeField in the app inherits the fix.
This commit is contained in:
1 parent
271e6f5db6
commit
2e5061337b
4 files changed
+33
-2
No files matched your search
@@ -97,4 +97,15 @@ describe("<DateField>", () => {
|
||||
const html = render(<DateField value="2026-02-19" />);
|
||||
expect(html).toContain('aria-label="Open date picker"');
|
||||
});
|
||||
|
||||
it("uses the Input focus vocabulary — a ring that hugs the box, no offset", () => {
|
||||
// Drift guard: the field must share the app's focus ring (soft 3 px, no
|
||||
// offset) so the highlight never paints wider than the div or jumps on
|
||||
// focus. The old hard `ring-offset-2` ring is banned here.
|
||||
const html = render(<DateField value="2026-02-19" />);
|
||||
expect(html).toContain("focus-within:ring-ring/50");
|
||||
expect(html).toContain("focus-within:ring-[3px]");
|
||||
expect(html).not.toContain("ring-offset-2");
|
||||
expect(html).not.toContain("ring-offset-background");
|
||||
});
|
||||
});
|
||||
@@ -120,4 +120,13 @@ describe("<TimeField> SSR", () => {
|
||||
expect(html).toContain("min-h-11");
|
||||
expect(html).toContain("sm:h-10");
|
||||
});
|
||||
it("uses the Input focus vocabulary — a ring that hugs the box, no offset", () => {
|
||||
// Drift guard shared with <DateField>: soft 3 px ring, no offset, so the
|
||||
// highlight never overflows the div or jumps on focus.
|
||||
const html = render(<TimeField value="08:30" />, "de");
|
||||
expect(html).toContain("focus-within:ring-ring/50");
|
||||
expect(html).toContain("focus-within:ring-[3px]");
|
||||
expect(html).not.toContain("ring-offset-2");
|
||||
expect(html).not.toContain("ring-offset-background");
|
||||
});
|
||||
});
|
||||
@@ -56,8 +56,15 @@ const FIELD_HEIGHT_CLASSES = "min-h-11 h-11 sm:min-h-10 sm:h-10";
|
||||
// ~230 px no matter what width the parent hands it, and the surplus spills out
|
||||
// of whatever dialog or sheet it sits in. See the matching note on the overlay
|
||||
// input below; both ends are needed, one alone still floors the box.
|
||||
// The focus vocabulary matches `<Input>` exactly: a soft translucent 3 px ring
|
||||
// that hugs the border box with no offset. The earlier `ring-2 ring-offset-2`
|
||||
// painted a hard 4 px ring OUTSIDE the box — wider than the field's div and
|
||||
// visibly mismatched next to the app's other inputs, and it shifted the
|
||||
// highlight box on focus. `text-base md:text-sm` mirrors `<Input>` too: a
|
||||
// sub-16 px input triggers iOS Safari's focus auto-zoom, which reads as a
|
||||
// layout jump; 16 px below `md:` suppresses it.
|
||||
const FIELD_BASE_CLASSES =
|
||||
"border-input bg-background text-foreground ring-offset-background placeholder:text-muted-foreground focus-within:ring-ring relative flex w-full min-w-0 items-center rounded-md border ps-3 pe-2 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-2 focus-within:ring-offset-2 focus-within:outline-none";
|
||||
"border-input bg-background text-foreground placeholder:text-muted-foreground relative flex w-full min-w-0 items-center rounded-md border ps-3 pe-2 text-base md:text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px] focus-within:outline-none";
|
||||
|
||||
export interface DateFieldProps {
|
||||
id?: string;
|
||||
|
||||
@@ -42,8 +42,12 @@ const FIELD_HEIGHT_CLASSES = "min-h-11 h-11 sm:min-h-10 sm:h-10";
|
||||
// automatic minimum size would otherwise be the 20-character intrinsic width of
|
||||
// the text input inside, which floors the whole field at ~230 px and overflows
|
||||
// the dialog that hosts it. See the note in `date-field.tsx`.
|
||||
// Focus vocabulary and font-size parity with `<Input>` / `<DateField>`: a soft
|
||||
// 3 px ring hugging the box (not the old hard `ring-2 ring-offset-2`, which
|
||||
// painted 4 px outside the div and jumped on focus) and `text-base md:text-sm`
|
||||
// to keep iOS Safari from auto-zooming a sub-16 px input.
|
||||
const FIELD_BASE_CLASSES =
|
||||
"border-input bg-background text-foreground ring-offset-background placeholder:text-muted-foreground focus-within:ring-ring relative flex w-full min-w-0 items-center rounded-md border ps-3 pe-2 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-2 focus-within:ring-offset-2 focus-within:outline-none";
|
||||
"border-input bg-background text-foreground placeholder:text-muted-foreground relative flex w-full min-w-0 items-center rounded-md border ps-3 pe-2 text-base md:text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px] focus-within:outline-none";
|
||||
|
||||
const TIME_RE = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
||||
|
||||
|
||||
Reference in new issue
Block a user