sherick-ui 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +57 -3
  2. package/dist/cjs/components/CheckboxGroup.cjs +41 -0
  3. package/dist/cjs/components/CheckboxGroup.cjs.map +1 -0
  4. package/dist/cjs/components/Combobox.cjs +190 -14
  5. package/dist/cjs/components/Combobox.cjs.map +1 -1
  6. package/dist/cjs/components/Form.cjs +27 -0
  7. package/dist/cjs/components/Form.cjs.map +1 -0
  8. package/dist/cjs/components/Input.cjs +20 -2
  9. package/dist/cjs/components/Input.cjs.map +1 -1
  10. package/dist/cjs/components/NumberField.cjs +30 -4
  11. package/dist/cjs/components/NumberField.cjs.map +1 -1
  12. package/dist/cjs/components/RadioGroup.cjs +7 -3
  13. package/dist/cjs/components/RadioGroup.cjs.map +1 -1
  14. package/dist/cjs/components/Search.cjs +1 -1
  15. package/dist/cjs/components/Search.cjs.map +1 -1
  16. package/dist/cjs/components/Select.cjs +4 -2
  17. package/dist/cjs/components/Select.cjs.map +1 -1
  18. package/dist/cjs/components/Tabs.cjs +1 -1
  19. package/dist/cjs/components/Tabs.cjs.map +1 -1
  20. package/dist/cjs/components/Textarea.cjs +6 -1
  21. package/dist/cjs/components/Textarea.cjs.map +1 -1
  22. package/dist/cjs/components/ToggleGroup.cjs +1 -1
  23. package/dist/cjs/components/ToggleGroup.cjs.map +1 -1
  24. package/dist/cjs/components/choice-row.cjs +51 -0
  25. package/dist/cjs/components/choice-row.cjs.map +1 -0
  26. package/dist/cjs/components/date-field.cjs +3 -3
  27. package/dist/cjs/components/date-field.cjs.map +1 -1
  28. package/dist/cjs/components/option-groups.cjs +35 -0
  29. package/dist/cjs/components/option-groups.cjs.map +1 -0
  30. package/dist/cjs/components/ui.common.cjs +100 -50
  31. package/dist/cjs/components/ui.common.cjs.map +1 -1
  32. package/dist/cjs/dev.cjs +2 -0
  33. package/dist/cjs/dev.cjs.map +1 -1
  34. package/dist/cjs/index.cjs +4 -0
  35. package/dist/cjs/index.cjs.map +1 -1
  36. package/dist/esm/components/CheckboxGroup.js +36 -0
  37. package/dist/esm/components/CheckboxGroup.js.map +1 -0
  38. package/dist/esm/components/Combobox.js +195 -19
  39. package/dist/esm/components/Combobox.js.map +1 -1
  40. package/dist/esm/components/Form.js +22 -0
  41. package/dist/esm/components/Form.js.map +1 -0
  42. package/dist/esm/components/Input.js +21 -3
  43. package/dist/esm/components/Input.js.map +1 -1
  44. package/dist/esm/components/NumberField.js +31 -5
  45. package/dist/esm/components/NumberField.js.map +1 -1
  46. package/dist/esm/components/RadioGroup.js +10 -6
  47. package/dist/esm/components/RadioGroup.js.map +1 -1
  48. package/dist/esm/components/Search.js +1 -1
  49. package/dist/esm/components/Search.js.map +1 -1
  50. package/dist/esm/components/Select.js +6 -4
  51. package/dist/esm/components/Select.js.map +1 -1
  52. package/dist/esm/components/Tabs.js +2 -2
  53. package/dist/esm/components/Tabs.js.map +1 -1
  54. package/dist/esm/components/Textarea.js +6 -1
  55. package/dist/esm/components/Textarea.js.map +1 -1
  56. package/dist/esm/components/ToggleGroup.js +2 -2
  57. package/dist/esm/components/ToggleGroup.js.map +1 -1
  58. package/dist/esm/components/choice-row.js +46 -0
  59. package/dist/esm/components/choice-row.js.map +1 -0
  60. package/dist/esm/components/date-field.js +3 -3
  61. package/dist/esm/components/date-field.js.map +1 -1
  62. package/dist/esm/components/option-groups.js +30 -0
  63. package/dist/esm/components/option-groups.js.map +1 -0
  64. package/dist/esm/components/ui.common.js +100 -52
  65. package/dist/esm/components/ui.common.js.map +1 -1
  66. package/dist/esm/dev.js +1 -1
  67. package/dist/esm/index.js +2 -0
  68. package/dist/esm/index.js.map +1 -1
  69. package/dist/styles.css +271 -205
  70. package/dist/theme.css +160 -157
  71. package/dist/types/dev.d.cts +31 -25
  72. package/dist/types/dev.d.ts +31 -25
  73. package/dist/types/index.d.cts +184 -20
  74. package/dist/types/index.d.ts +184 -20
  75. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { cx } from '../libs/utils.js';
2
- import { motionRim, motionFeedback, motionRowLayer, motionStateLayer, motionDisclose } from './ui.motion.js';
2
+ import { motionRim, motionFeedback, motionStateLayer, motionRowLayer, motionDisclose } from './ui.motion.js';
3
3
 
4
4
  /* Sherick UI design primitives
5
5
  ==========================================================================
@@ -121,10 +121,10 @@ const recessedTop = "shadow-sherick-recessed-top";
121
121
  the size of the object and the emphasis it carries, and silhouette says what an object does:
122
122
  a **capsule acts** (buttons, chips, badges, toggles are `pill`) and a **rounded rectangle
123
123
  holds** (fields, alerts, tables, sheets). Container roles therefore stay clearly short of a
124
- capsule at their own height — `control` is a third of a 48px field — so a field never reads as
124
+ capsule at their own height — `control` is a quarter of a 48px field — so a field never reads as
125
125
  an almost-button beside a real one. Nested roles stay concentric: an inner corner is the outer
126
- corner minus the inset between them (a 20px track around a 16px indicator at 4px, a 24px list
127
- sheet around 16px options at 8px).
126
+ corner minus the inset between them (a 16px track around a 12px indicator at 4px, a 20px list
127
+ sheet around 12px options at 8px).
128
128
  - control: ordinary controls and dense data regions — fields, rows, options,
129
129
  chips, tables.
130
130
  - mark: a compact square selection mark — a 20px checkbox box. `control` is a role
@@ -147,13 +147,13 @@ const recessedTop = "shadow-sherick-recessed-top";
147
147
  - pill: fully rounded controls whose width follows their content.
148
148
  - circle: fully rounded square targets. */
149
149
  const shape = {
150
- control: "rounded-[1rem]",
150
+ control: "rounded-[0.75rem]",
151
151
  mark: "rounded-[0.375rem]",
152
- row: "rounded-[0.75rem]",
153
- prominent: "rounded-[1.25rem]",
154
- surface: "rounded-[1.5rem]",
155
- sheet: "rounded-[1rem]",
156
- expressive: "rounded-[1.75rem]",
152
+ row: "rounded-[0.5rem]",
153
+ prominent: "rounded-[1rem]",
154
+ surface: "rounded-[1.25rem]",
155
+ sheet: "rounded-[0.75rem]",
156
+ expressive: "rounded-[1.5rem]",
157
157
  pill: "rounded-full",
158
158
  circle: "rounded-full",
159
159
  };
@@ -179,19 +179,23 @@ const edgeTone = {
179
179
  inside such a control says what it is — an empty field holds at most a placeholder, an empty mark
180
180
  holds nothing — and it still has to be identifiable at 3:1, which no neutral fill step and no
181
181
  depth the light model can draw achieves on its own. So a hollow control carries one rim in the
182
- `rim` tone, tuned to clear 3:1 on every surface by a small margin and no more, and that rim is
183
- the only border a control has. A filled control — a button, a chip, a selected mark — is identified by its fill and
182
+ `rim` tone, tuned to clear 3:1 by a small margin and no more, and that rim is the only border a
183
+ control has. A filled control — a button, a chip, a selected mark — is identified by its fill and
184
184
  never carries one.
185
- The field rim is a real border: forced colors repaints a border in a system colour, where it
186
- would remove an inset shadow, and the scoped baseline sizes boxes by their border edge, so the
187
- rim never changes a field's footprint. Its states ride on the field ladder in `state.field`.
185
+ A text field's rim is its **baseline**: a line along the bottom edge only, which is the one part
186
+ of a field's boundary WCAG needs to see at 3:1 — the line the text is written on. The other three
187
+ sides are transparent, so the field reads as a quiet tray rather than a drawn box, and the
188
+ field's `control` corner bends the baseline up into a soft lip at each end. The rim is a real
189
+ border on all four sides: forced colors repaints even a transparent border in a system colour, so
190
+ a field there keeps its whole outline, and the scoped baseline sizes boxes by their border edge,
191
+ so the rim never changes a field's footprint. Its states ride on the field ladder in `state.field`.
188
192
  A drop target's rim is dashed, and the dashes are a mask over an overlay rather than the native
189
193
  `dashed` style, whose dash length and corner joins the platform decides: the mask fixes the dash
190
194
  rhythm and follows the `control` corner exactly. The mask is authored in `tokens.ts`. */
191
195
  const dashedRimMask = "after:[mask-image:var(--sui-rim-dashed)] after:[mask-size:100%_100%] after:[mask-repeat:no-repeat]";
192
196
  const rim = {
193
- field: "border-[1.5px] border-sherick-rim",
194
- fieldError: "border-[1.5px] border-sherick-danger",
197
+ field: "border-[1.5px] border-transparent border-b-sherick-rim",
198
+ fieldError: "border-[1.5px] border-transparent border-b-sherick-danger",
195
199
  dashed: /* @__PURE__ */ cx("after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:content-['']", "after:bg-sherick-rim", dashedRimMask, motionRim),
196
200
  /* The dashes step up under the pointer and take the primary tone while a file is held over them. */
197
201
  dashedHover: "hover:after:bg-sherick-ink-muted",
@@ -251,26 +255,29 @@ const state = {
251
255
  composite field focuses its outer surface while the input inside stays
252
256
  borderless; an expanded control holds the engaged step while its popup is
253
257
  open. Tonality carries the state, so no border appears and disappears. */
254
- /* A field is a well: its fill sits below the surface around it, and engaging it sinks it further
255
- while its rim steps up. An invalid field is the same well with a danger rim: the rim, the
256
- danger placeholder and the field's message carry the error, so the fill never trades its
257
- depth for a tinted wash. The error steps keep the well's depth ladder and leave the rim alone. */
258
+ /* A field is a tray: the `matteHigh` tone a groove holds, so it reads as a quiet recess in light
259
+ and a quiet step up in dark, exactly as a segmented control's track does. Engaging it deepens
260
+ the tray and steps its baseline up to `ink-muted`. An invalid field is the danger tray — the
261
+ role's soft surface with a danger baseline — because a baseline alone is a thin signal to carry
262
+ an error on; the tray, the danger placeholder and the field's message carry it together. The
263
+ soft surface is laid at about half strength, so an error reads as a warm tray rather than a
264
+ red block, and it holds still under the pointer: its colour is already what it has to say. */
258
265
  field: {
259
- hover: "hover:bg-sherick-canvas/[0.85] hover:border-sherick-ink-muted",
260
- focus: "focus:bg-sherick-canvas focus:border-sherick-ink-muted",
266
+ hover: "hover:bg-sherick-surface-high/[0.82] hover:border-b-sherick-ink-muted",
267
+ focus: "focus:bg-sherick-surface-high focus:border-b-sherick-ink-muted",
261
268
  /* A composite's engaged tone outranks hover, independent of variant emission order. */
262
- focusWithin: "[&&]:focus-within:bg-sherick-canvas [&&]:focus-within:border-sherick-ink-muted",
263
- engaged: "bg-sherick-canvas border-sherick-ink-muted",
264
- errorHover: "hover:bg-sherick-canvas/[0.85]",
265
- errorFocus: "focus:bg-sherick-canvas",
266
- errorEngaged: "bg-sherick-canvas",
269
+ focusWithin: "[&&]:focus-within:bg-sherick-surface-high [&&]:focus-within:border-b-sherick-ink-muted",
270
+ engaged: "bg-sherick-surface-high border-b-sherick-ink-muted",
271
+ errorHover: "hover:bg-sherick-danger-soft/[0.55]",
272
+ errorFocus: "focus:bg-sherick-danger-soft/[0.55]",
273
+ errorEngaged: "bg-sherick-danger-soft/[0.55]",
267
274
  /* The error ladder again, keyed on Base UI's validity attribute for a part that learns its
268
275
  validity from the field it sits in. Two variants outrank the single-variant ladder above,
269
276
  so neither ladder has to be conditional. */
270
- invalid: "data-[invalid]:border-sherick-danger",
271
- invalidHover: "data-[invalid]:hover:border-sherick-danger",
272
- invalidEngaged: "data-[invalid]:border-sherick-danger",
273
- invalidFocusWithin: "[&&]:data-[invalid]:focus-within:border-sherick-danger",
277
+ invalid: "data-[invalid]:bg-sherick-danger-soft/[0.55] data-[invalid]:border-b-sherick-danger",
278
+ invalidHover: "data-[invalid]:hover:bg-sherick-danger-soft/[0.55] data-[invalid]:hover:border-b-sherick-danger",
279
+ invalidEngaged: "data-[invalid]:bg-sherick-danger-soft/[0.55] data-[invalid]:border-b-sherick-danger",
280
+ invalidFocusWithin: "[&&]:data-[invalid]:focus-within:bg-sherick-danger-soft/[0.55] [&&]:data-[invalid]:focus-within:border-b-sherick-danger",
274
281
  },
275
282
  };
276
283
  /* State layers.
@@ -354,6 +361,13 @@ const stateLayer = {
354
361
  keyboard so it can be discovered and announced as unavailable, and a reader still has to see
355
362
  which row the navigation is on when it lands there. */
356
363
  activeRow: "data-[highlighted]:before:opacity-[0.05]",
364
+ /* A control that is one part of a field — a combobox's clear and trigger, a number field's
365
+ steppers, a search field's submit, a date field's calendar glyph. Its target keeps the height
366
+ the field's row needs, but its surface does not: a hover layer that filled a 44px target painted
367
+ a column the full height of a 48px field. The layer is a 32px circle centred in whatever target
368
+ the part has, which leaves the field's own air above and below it and keeps every part in every
369
+ field the same size under the pointer. */
370
+ fieldPart: /* @__PURE__ */ cx("relative", stateLayerBase, "before:m-auto before:size-8 before:rounded-full before:bg-current", hoverQuiet, pressQuiet),
357
371
  };
358
372
  /* Hit area — the interactive region of a control whose visible mark is smaller than a comfortable
359
373
  target, drawn outside the layout box so a column, a row gap and the space beside a label are all
@@ -371,10 +385,10 @@ const hitArea = "relative after:pointer-events-auto after:absolute after:inset-[
371
385
  - matteHigh: the second matte step, for nesting inside another matte surface.
372
386
  - matteInset: a passive sheet tucked under a stronger control; subdued without
373
387
  blending into the canvas or needing a drawn rim.
374
- - control: the fill every text control shares, plus its placeholder tone. It is a well:
375
- the canvas tone, which sits below a card in both themes, so a field reads as
376
- sunk into the surface rather than as a pad raised from it. Its rim, not its
377
- fill, is what separates it (see `rim`).
388
+ - control: the fill every text control shares, plus its placeholder tone. It is a tray in
389
+ the `matteHigh` tone a groove holds, so a field and a track are one family. The
390
+ tray says "something goes here"; its baseline, not its fill, is what carries the
391
+ 3:1 boundary (see `rim`).
378
392
  - handle: the fill of a small part the user has to find — a value control's handle.
379
393
  Surface steps sit within a few percent of their neighbours, so this role
380
394
  takes the mid-emphasis tone, which inverts with the theme and separates in
@@ -407,7 +421,7 @@ const material = {
407
421
  matteQuiet: "bg-sherick-surface/[0.42] text-sherick-ink",
408
422
  matte: "bg-sherick-surface/[0.78] text-sherick-ink",
409
423
  matteHigh: "bg-sherick-surface-high/[0.72] text-sherick-ink",
410
- control: "bg-sherick-canvas/[0.7] text-sherick-ink placeholder:text-sherick-ink-muted",
424
+ control: "bg-sherick-surface-high/[0.6] text-sherick-ink placeholder:text-sherick-ink-muted",
411
425
  /* The invalid fill carries its placeholder at the *full* danger tone, not at a fraction of it. A
412
426
  placeholder is text, so it answers to the same 4.5:1 the copy beside it does, and a partial
413
427
  opacity of an accent is a different colour from the accent: the 72%-opacity placeholder
@@ -415,7 +429,7 @@ const material = {
415
429
  measures 4.1–4.7:1 light and 5.2–6.2:1 dark on it. There is no dimmer step of a semantic tone
416
430
  that is still readable, and a normal field's placeholder is `ink-muted` at full strength
417
431
  already — so the error form is the same rule, in the tone the field is in. */
418
- controlError: "bg-sherick-canvas/[0.7] text-sherick-ink placeholder:text-sherick-danger",
432
+ controlError: "bg-sherick-danger-soft/[0.55] text-sherick-ink placeholder:text-sherick-danger",
419
433
  handle: "bg-sherick-ink-muted text-sherick-canvas",
420
434
  acrylic: "bg-sherick-surface-float/[var(--sui-glass-fill)] bg-[image:var(--sui-glass-gradient)] text-sherick-ink backdrop-blur-[var(--sui-glass-blur)] backdrop-saturate-[var(--sui-glass-saturation)] backdrop-brightness-[var(--sui-glass-brightness)]",
421
435
  acrylicDense: "bg-sherick-surface-float/[var(--sui-glass-dense-fill)] bg-[image:var(--sui-glass-gradient-dense)] text-sherick-ink backdrop-blur-[var(--sui-glass-dense-blur)] backdrop-saturate-[var(--sui-glass-dense-saturation)] backdrop-brightness-[var(--sui-glass-dense-brightness)]",
@@ -443,7 +457,7 @@ const type = {
443
457
  },
444
458
  supporting: "text-[0.8125rem] leading-5",
445
459
  caption: "text-xs leading-4",
446
- title: "text-[0.9375rem] leading-[1.375rem] font-semibold",
460
+ title: "text-[0.9375rem] leading-[1.375rem] font-semibold tracking-[-0.011em]",
447
461
  heading: "text-xl leading-7 font-semibold tracking-[-0.02em]",
448
462
  numeric: "tabular-nums",
449
463
  };
@@ -463,12 +477,13 @@ const density = {
463
477
  target: "min-h-11 min-w-11",
464
478
  /* A control that is one part of a composite field — the trailing controls a Combobox owns. It
465
479
  keeps the floor's height, because it has to sit in the field's row, and takes only the width
466
- its glyph needs: the two of them are gripped as one cluster at the field's edge, and two full
467
- squares there read as two controls beside a field rather than as parts of it.
480
+ of its 32px hover surface (`stateLayer.fieldPart`): two parts side by side are gripped as one
481
+ cluster at the field's edge, their glyphs 12px apart, and any wider step spread a clear control
482
+ and a chevron into two controls beside a field rather than parts of it.
468
483
  The narrower step still clears the 24px pointer-target minimum WCAG AA asks for. It is below
469
484
  this system's own 44px floor on purpose: that floor belongs to a control that is the whole
470
485
  target of its own action, and this one is a part of a field the pointer is already in. */
471
- part: "min-h-11 min-w-9",
486
+ part: "min-h-11 min-w-8",
472
487
  };
473
488
  /* Icon slot — the fixed box a leading or trailing mark occupies, sized by the type step it sits
474
489
  beside so the icon-to-label ratio is the same at every size: a 14px label takes a 16px mark, a
@@ -507,7 +522,7 @@ const stacking = {
507
522
  alert dialog separate from the page identically. */
508
523
  const overlay = {
509
524
  /* The plane behind a surface that owns the viewport. */
510
- scrim: /* @__PURE__ */ cx("fixed inset-0", stacking.float, "bg-sherick-scrim/[0.38] backdrop-blur-[var(--sui-scrim-blur)]"),
525
+ scrim: /* @__PURE__ */ cx("fixed inset-0", stacking.float, "bg-sherick-scrim/[0.30] backdrop-blur-[var(--sui-scrim-blur)]"),
511
526
  /* An anchored surface with room to breathe: a selection list (Select, Combobox) or
512
527
  structured content (Popover). It grows out of its trigger and carries the softer
513
528
  surface corner, because what it holds is read rather than scanned.
@@ -597,6 +612,13 @@ const list = {
597
612
  row still shows where the navigation is while saying it cannot be used. The ring is inset
598
613
  because the row lives inside the sheet it belongs to, as a segment does in its track. */
599
614
  option: /* @__PURE__ */ cx("flex w-full items-center justify-between gap-4 px-4 py-3 text-start outline-none", type.control.normal, shape.control, motionFeedback, focusRingInset, text.high, stateLayer.quiet, stateLayer.activeRow, motionRowLayer, state.effectiveDisabled),
615
+ /* The heading of a group of options — a category above the channels filed under it. It is a
616
+ caption, not a row: it takes no state layer, no highlight and no focus, because the reader never
617
+ lands on it; the primitive names the group from it, so a screen reader hears the category as it
618
+ enters the group. Its inline padding is the option's, so a heading and the labels beneath it
619
+ share one start edge, and the space above it is what separates one group from the last: a
620
+ group inside a sheet is grouped by spacing and a type role, never by a rule or a nested card. */
621
+ groupLabel: /* @__PURE__ */ cx("px-4 pb-1 pt-3 font-medium", type.caption, text.medium),
600
622
  /* The same object at the density a list of short actions wants, where the row is scanned
601
623
  rather than read and the list is a control rather than a page. `shape.row` keeps the
602
624
  command's corner proportional to its own height, so its highlight nests in the tighter
@@ -692,7 +714,28 @@ const currentDestination = "bg-sherick-primary-soft text-sherick-ink font-medium
692
714
  /* A radio's opt-in full row follows the Base control it labels. `:has` keeps uncontrolled
693
715
  selection authoritative at the primitive rather than mirroring it in component state.
694
716
  Keep this independent so unrelated consumers of the mark recipes can tree-shake it. */
695
- const selectableRowSurface = "has-[[data-checked]]:bg-sherick-primary-selected has-[[data-checked]]:text-sherick-ink has-[[data-checked]]:shadow-sherick-control";
717
+ /* The puck — a held choice that rides in a track: a selected segment, the tab indicator. A choice
718
+ held *in a groove* is a part lifted out of it, so it is the brightest surface in the view, raised
719
+ at `elevation.control`, and its label takes the role's text tone (`tone.text`): the lift says
720
+ "this one" and the label's colour says which family it belongs to. A held choice in a *sheet* —
721
+ an option, a tree row, a date — keeps `tone.selected`, because a list sheet is already the
722
+ brightest surface and a white row on it would vanish. */
723
+ const puck = "bg-sherick-puck";
724
+ /* A choice row that holds the selection — an opt-in `surface` row in a RadioGroup or CheckboxGroup —
725
+ takes the role's *soft* surface and stays flat. The row is a large surface and its mark already
726
+ carries the strong accent edge to edge, so the row only has to say "this one" quietly: the
727
+ selected step and a lift turned a whole card into an accent block, which outshouted the
728
+ priority action on the same view. */
729
+ const selectableRowSurface = "has-[[data-checked]]:bg-sherick-primary-soft has-[[data-checked]]:text-sherick-ink";
730
+ /* Supporting copy inside such a row — a choice's description. `text.medium` clears 4.5:1 on the
731
+ soft step at rest (5.7:1 light, 6.2:1 dark), but the row's press layer composites toward the label
732
+ and takes that margin away, so while the row holds the selection its supporting line takes full
733
+ ink; the supporting type step keeps it subordinate to the label. Keyed on the same primitive
734
+ marker as the surface itself. */
735
+ const selectableRowSupporting = "group-has-[[data-checked]]:text-sherick-ink";
736
+ /* An empty mark's interior: a canvas-toned well. Named on its own so the contrast model can read it
737
+ without retaining the whole `selectable` table in bundles that never draw a mark. */
738
+ const markWellFill = "bg-sherick-canvas/[0.7]";
696
739
  const selectable = {
697
740
  surface: /* @__PURE__ */ cx("relative", elevation.recessed, motionFeedback),
698
741
  /* A selection mark — a checkbox box, a radio socket — rests in the same groove a Switch track
@@ -715,7 +758,7 @@ const selectable = {
715
758
  recess all reach the mark's real edge: a border would leave a 1.5px frame of untinted fill
716
759
  around a hovered selected box. Hover steps the rim to the mid-emphasis tone — darker in light
717
760
  mode, lighter in dark — which is a visible answer on a 20px mark where a tint alone is not. */
718
- mark: /* @__PURE__ */ cx("bg-sherick-canvas/[0.7]", text.medium, "after:pointer-events-none after:absolute after:inset-0 after:box-border after:rounded-[inherit] after:border-[1.5px] after:content-['']", "after:border-sherick-rim", "group-[:not([data-disabled]):not([data-readonly]):not(:disabled):hover]:after:border-sherick-ink-muted", motionRim),
761
+ mark: /* @__PURE__ */ cx(markWellFill, text.medium, "after:pointer-events-none after:absolute after:inset-0 after:box-border after:rounded-[inherit] after:border-[1.5px] after:content-['']", "after:border-sherick-rim", "group-[:not([data-disabled]):not([data-readonly]):not(:disabled):hover]:after:border-sherick-ink-muted", motionRim),
719
762
  /* A selected mark is the strong accent, edge to edge, and it settles flat: it is identified by
720
763
  its fill and its glyph, and the rim fades out as the fill arrives. The rim fades by opacity
721
764
  rather than by colour, so forced colors cannot repaint a transparent rim as a visible one. */
@@ -738,13 +781,16 @@ const recipeAlpha = (recipe, pattern, label) => {
738
781
  };
739
782
  const stateAlpha = (recipe, label) => recipeAlpha(recipe, /opacity-\[(0?\.\d+)\]/, label);
740
783
  const fillAlpha = (recipe, label) => recipeAlpha(recipe, /\/\[(0?\.\d+)\]/, label);
741
- /* A field well's canvas step: written without an alpha, the step is the opaque canvas. */
742
- const wellAlpha = (recipe, label) => {
743
- const match = recipe.match(/bg-sherick-canvas(?:\/\[(0?\.\d+)\])?(?:\s|$)/);
784
+ /* A tone's step written with or without an alpha: without one, the step is the opaque tone. */
785
+ const toneAlpha = (tone) => (recipe, label) => {
786
+ const match = recipe.match(new RegExp(`bg-sherick-${tone}(?:\\/\\[(0?\\.\\d+)\\])?(?:\\s|$)`));
744
787
  if (!match)
745
788
  throw new Error(`recipe alpha missing for ${label}`);
746
789
  return match[1] === undefined ? 1 : Number(match[1]);
747
790
  };
791
+ /* A field tray's `surface-high` step, and the canvas step an empty mark's interior holds. */
792
+ const trayAlpha = toneAlpha("surface-high");
793
+ const markAlpha = toneAlpha("canvas");
748
794
  const recipeAlphas = {
749
795
  state: {
750
796
  quiet: { hover: stateAlpha(hoverQuiet, "state.quiet.hover"), press: stateAlpha(pressQuiet, "state.quiet.press") },
@@ -761,9 +807,11 @@ const recipeAlphas = {
761
807
  quiet: fillAlpha(material.matteQuiet, "field.quiet"),
762
808
  card: fillAlpha(material.matte, "field.card"),
763
809
  groove: fillAlpha(material.matteHigh, "field.groove"),
764
- control: wellAlpha(material.control, "field.control"),
765
- hover: wellAlpha(state.field.hover, "field.hover"),
766
- engaged: wellAlpha(state.field.engaged, "field.engaged"),
810
+ control: trayAlpha(material.control, "field.control"),
811
+ hover: trayAlpha(state.field.hover, "field.hover"),
812
+ engaged: trayAlpha(state.field.engaged, "field.engaged"),
813
+ mark: markAlpha(markWellFill, "field.mark"),
814
+ error: fillAlpha(material.controlError, "field.error"),
767
815
  },
768
816
  };
769
817
  /* A labelled field is a shrinkable column. Its explanatory copy wraps, including identifiers;
@@ -776,5 +824,5 @@ const fieldMessage = {
776
824
  error: /* @__PURE__ */ cx("mt-2", type.supporting, tone.text.danger),
777
825
  };
778
826
 
779
- export { currentDestination, density, detail, disclosure, edge, edgeTone, elevation, fieldLayout, fieldMessage, focusRing, focusRingDrag, focusRingHeld, focusRingInset, focusRingWithin, groupFocusRing, hitArea, iconSlot, list, material, matteInset, mediaCanvas, overlay, parentFocusRingInset, recessedTop, recipeAlphas, rim, selectable, selectableRowSurface, shape, stacking, state, stateLayer, stateLayerQuietComposite, text, tone, type };
827
+ export { currentDestination, density, detail, disclosure, edge, edgeTone, elevation, fieldLayout, fieldMessage, focusRing, focusRingDrag, focusRingHeld, focusRingInset, focusRingWithin, groupFocusRing, hitArea, iconSlot, list, material, matteInset, mediaCanvas, overlay, parentFocusRingInset, puck, recessedTop, recipeAlphas, rim, selectable, selectableRowSupporting, selectableRowSurface, shape, stacking, state, stateLayer, stateLayerQuietComposite, text, tone, type };
780
828
  //# sourceMappingURL=ui.common.js.map