feat(design): the header search is a 40 glass pill; a wrapper field dims for its input only (E28 S3c)
This commit is contained in:
@@ -6,6 +6,7 @@ import clsx from "clsx";
|
||||
// itself. Its own file so `Input.tsx` exports components only (react-refresh).
|
||||
|
||||
export type FieldSize =
|
||||
| "lg" // 40 — the page's one hero field: the header search, the command palette
|
||||
| "md" // 32 — a form field, lines up with a `md` Button
|
||||
| "sm" // 28 — a toolbar search, a field in a popover or an inline rename, lines up with the toolbar row
|
||||
| "xs" // 24 — inside a table row or a pager
|
||||
@@ -15,6 +16,7 @@ export type FieldSize =
|
||||
// On a coarse pointer the text is 1rem, so a tablet browser does not zoom the page on focus (not on a
|
||||
// heading, whose type is already larger).
|
||||
const SIZES: Record<FieldSize, string> = {
|
||||
lg: "h-10 px-3 text-sm pointer-coarse:text-base",
|
||||
md: "h-8 px-3 text-sm pointer-coarse:text-base",
|
||||
sm: "h-7 px-2.5 text-sm pointer-coarse:text-base",
|
||||
xs: "h-6 px-2 text-xs pointer-coarse:text-base",
|
||||
@@ -24,18 +26,31 @@ const SIZES: Record<FieldSize, string> = {
|
||||
|
||||
// The edge is --edge-strong (the fg mixed 55% into the bg, ≥ 3:1 on every scheme — WCAG 1.4.11); focus
|
||||
// turns it into a 2px accent edge (the border plus a 1px ring), the outlined-field convention. Invalid
|
||||
// is the danger edge, focused or not. On a coarse pointer the box grows to the 44 touch target.
|
||||
// is the danger edge, focused or not. A wrapper frame keys both states to its own INPUT, not to any
|
||||
// descendant: the header search's Go button is disabled while the query is empty, and a bare
|
||||
// `has-disabled:` faded the whole box. On a coarse pointer the box grows to the 44 touch target.
|
||||
const BASE =
|
||||
"min-w-0 border edge-strong bg-transparent text-fg placeholder:text-muted transition-colors " +
|
||||
"min-w-0 border edge-strong text-fg placeholder:text-muted transition-colors " +
|
||||
"focus-within:border-accent focus-within:ring-1 focus-within:ring-accent focus-within:outline-hidden " +
|
||||
"aria-invalid:border-danger aria-invalid:ring-danger has-aria-invalid:border-danger has-aria-invalid:ring-danger " +
|
||||
"disabled:cursor-not-allowed disabled:opacity-50 has-disabled:cursor-not-allowed has-disabled:opacity-50 " +
|
||||
"aria-invalid:border-danger aria-invalid:ring-danger " +
|
||||
"has-[input[aria-invalid=true]]:border-danger has-[input[aria-invalid=true]]:ring-danger " +
|
||||
"disabled:cursor-not-allowed disabled:opacity-50 " +
|
||||
"has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50 " +
|
||||
"pointer-coarse:min-h-(--touch-target)";
|
||||
|
||||
// `floating`: the field hovers over the scrolling page (the header search), so it keeps the glass-menu
|
||||
// surface under its edge instead of a see-through one (A2: what floats over content is glass). The
|
||||
// shadow is a utility so the focus ring's box-shadow composes with it instead of dropping it.
|
||||
export function fieldClass({
|
||||
size = "md",
|
||||
pill = false,
|
||||
}: { size?: FieldSize; pill?: boolean } = {}) {
|
||||
// A4: a toolbar search is a pill; every other field r8.
|
||||
return clsx(BASE, SIZES[size], pill ? "rounded-full" : "rounded-lg");
|
||||
floating = false,
|
||||
}: { size?: FieldSize; pill?: boolean; floating?: boolean } = {}) {
|
||||
// A4: a search is a pill; every other field r8.
|
||||
return clsx(
|
||||
BASE,
|
||||
SIZES[size],
|
||||
pill ? "rounded-full" : "rounded-lg",
|
||||
floating ? "glass-menu shadow-[0_18px_44px_-16px_rgba(0,0,0,0.6)]" : "bg-transparent",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user