@verdify/ui 0.2.1 → 0.3.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 (146) hide show
  1. package/LICENSE +12 -0
  2. package/dist/components/accordion/accordion.variants.d.ts.map +1 -1
  3. package/dist/components/accordion/accordion.variants.js +2 -1
  4. package/dist/components/accordion/accordion.variants.js.map +1 -1
  5. package/dist/components/agent-badge/agent-badge.variants.d.ts.map +1 -1
  6. package/dist/components/agent-badge/agent-badge.variants.js.map +1 -1
  7. package/dist/components/alert/alert.variants.d.ts.map +1 -1
  8. package/dist/components/alert/alert.variants.js +3 -2
  9. package/dist/components/alert/alert.variants.js.map +1 -1
  10. package/dist/components/breadcrumb/breadcrumb.variants.d.ts.map +1 -1
  11. package/dist/components/breadcrumb/breadcrumb.variants.js +2 -1
  12. package/dist/components/breadcrumb/breadcrumb.variants.js.map +1 -1
  13. package/dist/components/button/button.variants.d.ts.map +1 -1
  14. package/dist/components/button/button.variants.js +2 -1
  15. package/dist/components/button/button.variants.js.map +1 -1
  16. package/dist/components/card/card.variants.d.ts.map +1 -1
  17. package/dist/components/card/card.variants.js +2 -1
  18. package/dist/components/card/card.variants.js.map +1 -1
  19. package/dist/components/checkbox/checkbox.js +1 -1
  20. package/dist/components/checkbox/checkbox.js.map +1 -1
  21. package/dist/components/checkbox/checkbox.variants.d.ts.map +1 -1
  22. package/dist/components/checkbox/checkbox.variants.js +2 -1
  23. package/dist/components/checkbox/checkbox.variants.js.map +1 -1
  24. package/dist/components/command-palette/command-palette.variants.d.ts +1 -1
  25. package/dist/components/command-palette/command-palette.variants.d.ts.map +1 -1
  26. package/dist/components/command-palette/command-palette.variants.js +5 -3
  27. package/dist/components/command-palette/command-palette.variants.js.map +1 -1
  28. package/dist/components/consent-toggle/consent-toggle.variants.d.ts +1 -1
  29. package/dist/components/consent-toggle/consent-toggle.variants.d.ts.map +1 -1
  30. package/dist/components/consent-toggle/consent-toggle.variants.js +1 -1
  31. package/dist/components/consent-toggle/consent-toggle.variants.js.map +1 -1
  32. package/dist/components/credential-card/credential-card.variants.d.ts +3 -3
  33. package/dist/components/credential-card/credential-card.variants.d.ts.map +1 -1
  34. package/dist/components/credential-card/credential-card.variants.js +3 -3
  35. package/dist/components/credential-card/credential-card.variants.js.map +1 -1
  36. package/dist/components/data-grid/data-grid.variants.d.ts +1 -1
  37. package/dist/components/data-grid/data-grid.variants.d.ts.map +1 -1
  38. package/dist/components/data-grid/data-grid.variants.js +11 -10
  39. package/dist/components/data-grid/data-grid.variants.js.map +1 -1
  40. package/dist/components/dialog/dialog.variants.d.ts.map +1 -1
  41. package/dist/components/dialog/dialog.variants.js +3 -2
  42. package/dist/components/dialog/dialog.variants.js.map +1 -1
  43. package/dist/components/identity-chip/identity-chip.variants.d.ts.map +1 -1
  44. package/dist/components/identity-chip/identity-chip.variants.js +3 -2
  45. package/dist/components/identity-chip/identity-chip.variants.js.map +1 -1
  46. package/dist/components/input/input.variants.d.ts.map +1 -1
  47. package/dist/components/input/input.variants.js +3 -2
  48. package/dist/components/input/input.variants.js.map +1 -1
  49. package/dist/components/label/label.variants.js +1 -1
  50. package/dist/components/label/label.variants.js.map +1 -1
  51. package/dist/components/menu/menu.d.ts.map +1 -1
  52. package/dist/components/menu/menu.js +1 -1
  53. package/dist/components/menu/menu.js.map +1 -1
  54. package/dist/components/menu/menu.variants.d.ts +1 -1
  55. package/dist/components/menu/menu.variants.d.ts.map +1 -1
  56. package/dist/components/menu/menu.variants.js +3 -2
  57. package/dist/components/menu/menu.variants.js.map +1 -1
  58. package/dist/components/pagination/pagination.variants.d.ts.map +1 -1
  59. package/dist/components/pagination/pagination.variants.js +2 -1
  60. package/dist/components/pagination/pagination.variants.js.map +1 -1
  61. package/dist/components/popover/popover.variants.d.ts.map +1 -1
  62. package/dist/components/popover/popover.variants.js +4 -3
  63. package/dist/components/popover/popover.variants.js.map +1 -1
  64. package/dist/components/progress/progress.variants.d.ts +1 -1
  65. package/dist/components/progress/progress.variants.d.ts.map +1 -1
  66. package/dist/components/progress/progress.variants.js +1 -1
  67. package/dist/components/progress/progress.variants.js.map +1 -1
  68. package/dist/components/radio/radio.d.ts.map +1 -1
  69. package/dist/components/radio/radio.js +2 -1
  70. package/dist/components/radio/radio.js.map +1 -1
  71. package/dist/components/select/select.variants.d.ts +3 -3
  72. package/dist/components/select/select.variants.d.ts.map +1 -1
  73. package/dist/components/select/select.variants.js +5 -4
  74. package/dist/components/select/select.variants.js.map +1 -1
  75. package/dist/components/sheet/sheet.variants.d.ts.map +1 -1
  76. package/dist/components/sheet/sheet.variants.js +3 -2
  77. package/dist/components/sheet/sheet.variants.js.map +1 -1
  78. package/dist/components/sidebar/sidebar.variants.d.ts +1 -1
  79. package/dist/components/sidebar/sidebar.variants.d.ts.map +1 -1
  80. package/dist/components/sidebar/sidebar.variants.js +4 -3
  81. package/dist/components/sidebar/sidebar.variants.js.map +1 -1
  82. package/dist/components/switch/switch.variants.d.ts.map +1 -1
  83. package/dist/components/switch/switch.variants.js +2 -1
  84. package/dist/components/switch/switch.variants.js.map +1 -1
  85. package/dist/components/table/table.d.ts.map +1 -1
  86. package/dist/components/table/table.js +1 -1
  87. package/dist/components/table/table.js.map +1 -1
  88. package/dist/components/table/table.variants.d.ts.map +1 -1
  89. package/dist/components/table/table.variants.js +9 -7
  90. package/dist/components/table/table.variants.js.map +1 -1
  91. package/dist/components/tabs/tabs.variants.d.ts.map +1 -1
  92. package/dist/components/tabs/tabs.variants.js +3 -2
  93. package/dist/components/tabs/tabs.variants.js.map +1 -1
  94. package/dist/components/textarea/textarea.js +2 -2
  95. package/dist/components/textarea/textarea.js.map +1 -1
  96. package/dist/components/textarea/textarea.variants.d.ts.map +1 -1
  97. package/dist/components/textarea/textarea.variants.js +2 -1
  98. package/dist/components/textarea/textarea.variants.js.map +1 -1
  99. package/dist/components/toast/toast.variants.d.ts.map +1 -1
  100. package/dist/components/toast/toast.variants.js +3 -2
  101. package/dist/components/toast/toast.variants.js.map +1 -1
  102. package/dist/components/trust-score/trust-score.variants.d.ts +1 -1
  103. package/dist/components/trust-score/trust-score.variants.d.ts.map +1 -1
  104. package/dist/components/trust-score/trust-score.variants.js +1 -1
  105. package/dist/components/trust-score/trust-score.variants.js.map +1 -1
  106. package/dist/index.d.ts +1 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +3 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/lib/focus-ring.d.ts +2 -0
  111. package/dist/lib/focus-ring.d.ts.map +1 -0
  112. package/dist/lib/focus-ring.js +5 -0
  113. package/dist/lib/focus-ring.js.map +1 -0
  114. package/package.json +18 -19
  115. package/registry/accordion.json +3 -2
  116. package/registry/agent-badge.json +1 -1
  117. package/registry/alert.json +3 -2
  118. package/registry/breadcrumb.json +3 -2
  119. package/registry/button.json +3 -2
  120. package/registry/card.json +3 -2
  121. package/registry/checkbox.json +4 -3
  122. package/registry/command-palette.json +3 -2
  123. package/registry/consent-toggle.json +1 -1
  124. package/registry/credential-card.json +1 -1
  125. package/registry/data-grid.json +2 -1
  126. package/registry/dialog.json +3 -2
  127. package/registry/focus-ring.json +16 -0
  128. package/registry/identity-chip.json +2 -1
  129. package/registry/init.json +1 -1
  130. package/registry/input.json +3 -2
  131. package/registry/label.json +1 -1
  132. package/registry/menu.json +4 -3
  133. package/registry/pagination.json +3 -2
  134. package/registry/popover.json +3 -2
  135. package/registry/progress.json +1 -1
  136. package/registry/radio.json +3 -2
  137. package/registry/select.json +3 -2
  138. package/registry/sheet.json +3 -2
  139. package/registry/sidebar.json +3 -2
  140. package/registry/switch.json +3 -2
  141. package/registry/table.json +3 -2
  142. package/registry/tabs.json +3 -2
  143. package/registry/textarea.json +3 -2
  144. package/registry/toast.json +3 -2
  145. package/registry/trust-score.json +1 -1
  146. package/registry.json +4 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { cn } from \"../../lib/cn\";\nimport {\n menuTriggerClass,\n menuPopupClass,\n menuItemVariants,\n menuItemIconClass,\n menuItemShortcutClass,\n menuSubChevronClass,\n menuLabelClass,\n menuSeparatorClass,\n type MenuItemVariantProps,\n} from \"./menu.variants\";\n\nexport interface MenuProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> {}\n\n/**\n * Menu is a popup list of ACTIONS that a trigger opens — the row of commands behind a button, an\n * avatar, or a row's overflow control (spec §1). Reach for it when you want to fire an action (open,\n * rename, sign out, revoke a key), not pick a value: use Select to choose one option from a list, and\n * use the Sidebar for page-level navigation. Each item runs a command and then the menu closes.\n *\n * It is a NEUTRAL surface (spec §3): the popup, items, and separators are neutral, and the brand\n * violet never marks an item as \"the special one.\" The one colored item is the destructive item,\n * which takes the destructive ACTION treatment because the command it runs is irreversible — a risk\n * signal, not a status result; a verified result is never reported by a menu item (brand != state).\n *\n * Wraps the Radix DropdownMenu primitive (WAI-ARIA APG menu-button + menu pattern), which provides\n * the portal, roving tabindex, type-ahead, submenu, and Escape/arrow keyboard model — a stateful\n * primitive, so this file is `'use client'`.\n */\nexport function Menu(props: MenuProps) {\n return <DropdownMenuPrimitive.Root {...props} />;\n}\n\nexport interface MenuTriggerProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger> {}\n\n/**\n * The control that opens the menu (spec §2 trigger): the one stop in the page tab order for this\n * control, carrying the focus ring. Radix sets `aria-haspopup=\"menu\"`, `aria-expanded`, and\n * `aria-controls` (pointing at the popup) for you. Pass `asChild` to wrap your own Button so the\n * trigger inherits its role, keyboard, and focus ring rather than nesting a second button; the bare\n * (non-`asChild`) form renders the default neutral-ghost trigger.\n */\nexport const MenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n MenuTriggerProps\n>(function MenuTrigger({ className, asChild, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n asChild={asChild}\n className={asChild ? className : cn(menuTriggerClass, className)}\n {...props}\n />\n );\n});\n\nexport interface MenuContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {}\n\n/**\n * Renders the portal and the popup (spec §2 popup): the floating `role=\"menu\"` surface that opens on\n * activation, raised above the page and anchored to the trigger. On open, focus moves into the popup\n * (first item, or last on Up) and roving tabindex tracks the active item; on close — by Escape,\n * activation, or click-away — focus returns to the trigger (Radix, spec §6/§7). The menu is NOT a\n * modal dialog: focus is not trapped, and Tab leaves the menu rather than stepping through items.\n * A neutral raised surface; brand violet and Verified Green never appear here (spec §3/§5/§8).\n */\nexport const MenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n MenuContentProps\n>(function MenuContent({ className, sideOffset = 4, loop = true, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n // `loop` wraps arrow movement at the ends (spec §6: \"wrapping at the ends\") — Radix leaves\n // it OFF by default, so we default it ON to honor the frozen keyboard model. Disabled items\n // are skipped by the roving handler for free (Radix, spec §4/§6).\n loop={loop}\n className={cn(menuPopupClass, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n});\n\nexport interface MenuItemProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>,\n \"color\"\n >,\n MenuItemVariantProps {\n /** The leading icon (spec §2): decorative, sized by `--size-icon-md`; the item names itself by its label text. */\n icon?: React.ReactNode;\n /** A trailing shortcut hint (spec §2): text such as \"⌘K\", in the muted label role; never a focus stop. */\n shortcut?: React.ReactNode;\n}\n\n/**\n * One command row (spec §2 item, §4 states): a `role=\"menuitem\"` whose activation runs its command\n * and closes the menu, returning focus to the trigger (Radix). It holds an optional leading icon, a\n * label, and an optional trailing shortcut hint. Pointer hover and keyboard arrow movement share ONE\n * highlight (Radix `data-highlighted`), so the active item is the same for both (spec §4 Hover).\n *\n * `destructive` (spec §3 `item=destructive`) marks the ONE colored item — a command that is\n * irreversible (revoke a key, delete a profile). It takes the destructive ACTION treatment and must\n * name the consequence in its TEXT, never by color alone (spec §7/§8). A `disabled` item stays in the\n * menu and readable to assistive technology (`aria-disabled`), is skipped by arrow movement, and does\n * not fire on activation (spec §4 Disabled / §7).\n */\nexport const MenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n MenuItemProps\n>(function MenuItem({ className, destructive, icon, shortcut, children, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(menuItemVariants({ destructive }), className)}\n {...props}\n >\n {icon ? (\n <span aria-hidden=\"true\" className={menuItemIconClass}>\n {icon}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut ? <span className={menuItemShortcutClass}>{shortcut}</span> : null}\n </DropdownMenuPrimitive.Item>\n );\n});\n\nexport interface MenuGroupProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group> {\n /**\n * The non-interactive heading that partitions the popup (spec §2 group / group-label). It names the\n * group for assistive technology via `aria-labelledby` (Radix `Label`) and is NEVER a focus stop.\n */\n label?: React.ReactNode;\n}\n\n/**\n * A set of related items under a non-interactive `group-label` that partitions the popup (spec §2\n * group). The items read as a related set (`role=\"group\"` named by the label); the label is never a\n * menuitem and never a focus stop.\n */\nexport const MenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n MenuGroupProps\n>(function MenuGroup({ label, children, ...props }, ref) {\n // Radix Group + Label do NOT auto-wire aria-labelledby (the Label renders a bare div with no id),\n // so the group would read as an unnamed role=group. We hand-wire it the same way Separator\n // hand-rolls a named anatomy when the primitive can't carry it (skill: compose the role by hand\n // when Radix can't express the spec's anatomy): generate a stable id on the Label and point the\n // Group's aria-labelledby at it, so the items read as a related set named by the label (spec §2\n // group / §7 group named by its label).\n const labelId = React.useId();\n return (\n <DropdownMenuPrimitive.Group\n ref={ref}\n aria-labelledby={label ? labelId : undefined}\n {...props}\n >\n {label ? (\n <DropdownMenuPrimitive.Label id={labelId} className={menuLabelClass}>\n {label}\n </DropdownMenuPrimitive.Label>\n ) : null}\n {children}\n </DropdownMenuPrimitive.Group>\n );\n});\n\nexport interface MenuLabelProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> {}\n\n/**\n * A standalone non-interactive section label (spec §2 group-label) for a label that is not wrapped in\n * a `MenuGroup`. Like the group label it is the muted label-role heading and is never a focus stop.\n */\nexport const MenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n MenuLabelProps\n>(function MenuLabel({ className, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Label ref={ref} className={cn(menuLabelClass, className)} {...props} />\n );\n});\n\nexport interface MenuSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> {}\n\n/**\n * A thin neutral divider between groups (spec §2 separator): decorative (`role=\"separator\"`), never a\n * focus stop.\n */\nexport const MenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n MenuSeparatorProps\n>(function MenuSeparator({ className, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(menuSeparatorClass, className)}\n {...props}\n />\n );\n});\n\nexport interface MenuSubProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Sub> {}\n\n/**\n * A submenu: an item that opens a nested popup of its own items (spec §2 submenu). Keep nesting\n * shallow — deep trees are hard to operate by keyboard (spec §2). Wraps `MenuSubTrigger` +\n * `MenuSubContent`.\n */\nexport function MenuSub(props: MenuSubProps) {\n return <DropdownMenuPrimitive.Sub {...props} />;\n}\n\nexport interface MenuSubTriggerProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>,\n \"color\"\n >,\n MenuItemVariantProps {\n /** The leading icon (spec §2): decorative, sized by `--size-icon-md`. */\n icon?: React.ReactNode;\n}\n\n/**\n * The item that opens a submenu (spec §2/§6): a `role=\"menuitem\"` with `aria-haspopup=\"menu\"` and its\n * own `aria-expanded` (Radix). Right opens the submenu and focuses its first item; Left closes it and\n * returns focus here. It carries the same row treatment as a `MenuItem`, plus a trailing chevron\n * pointing to the inline-end.\n */\nexport const MenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n MenuSubTriggerProps\n>(function MenuSubTrigger({ className, destructive, icon, children, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(menuItemVariants({ destructive }), className)}\n {...props}\n >\n {icon ? (\n <span aria-hidden=\"true\" className={menuItemIconClass}>\n {icon}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n <ChevronGlyph />\n </DropdownMenuPrimitive.SubTrigger>\n );\n});\n\nexport interface MenuSubContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> {}\n\n/**\n * The nested popup of a submenu (spec §2 submenu): the same neutral raised `role=\"menu\"` surface as\n * `MenuContent`, anchored to its `MenuSubTrigger`.\n */\nexport const MenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n MenuSubContentProps\n>(function MenuSubContent({ className, sideOffset = 2, loop = true, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={sideOffset}\n // `loop` wraps arrow movement at the ends inside the submenu too (spec §6), matching the\n // parent popup; Radix leaves it OFF by default.\n loop={loop}\n className={cn(menuPopupClass, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n});\n\n// The submenu chevron — inline SVG (no icon dep), --size-icon-md, pointing to the inline-end to\n// signal the nested popup. Decorative (aria-hidden); aria-haspopup/aria-expanded carry the state, not\n// the glyph (spec §2/§7). Drawn with currentColor so it inherits the row's color.\nfunction ChevronGlyph() {\n return (\n <span data-testid=\"menu-sub-chevron\" aria-hidden=\"true\" className={menuSubChevronClass}>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" focusable=\"false\">\n <path d=\"M6 4l4 4-4 4\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </span>\n );\n}\n"],"mappings":"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","names":["MenuTrigger","MenuContent","MenuItem","MenuGroup","MenuLabel","MenuSeparator","MenuSubTrigger","MenuSubContent"]}
1
+ {"version":3,"sources":["../../../src/components/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { cn } from \"../../lib/cn\";\nimport {\n menuTriggerClass,\n menuPopupClass,\n menuItemVariants,\n menuItemIconClass,\n menuItemShortcutClass,\n menuSubChevronClass,\n menuLabelClass,\n menuSeparatorClass,\n type MenuItemVariantProps,\n} from \"./menu.variants\";\n\nexport interface MenuProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> {}\n\n/**\n * Menu is a popup list of ACTIONS that a trigger opens — the row of commands behind a button, an\n * avatar, or a row's overflow control (spec §1). Reach for it when you want to fire an action (open,\n * rename, sign out, revoke a key), not pick a value: use Select to choose one option from a list, and\n * use the Sidebar for page-level navigation. Each item runs a command and then the menu closes.\n *\n * It is a NEUTRAL surface (spec §3): the popup, items, and separators are neutral, and the brand\n * violet never marks an item as \"the special one.\" The one colored item is the destructive item,\n * which takes the destructive ACTION treatment because the command it runs is irreversible — a risk\n * signal, not a status result; a verified result is never reported by a menu item (brand != state).\n *\n * Wraps the Radix DropdownMenu primitive (WAI-ARIA APG menu-button + menu pattern), which provides\n * the portal, roving tabindex, type-ahead, submenu, and Escape/arrow keyboard model — a stateful\n * primitive, so this file is `'use client'`.\n */\nexport function Menu(props: MenuProps) {\n return <DropdownMenuPrimitive.Root {...props} />;\n}\n\nexport interface MenuTriggerProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger> {}\n\n/**\n * The control that opens the menu (spec §2 trigger): the one stop in the page tab order for this\n * control, carrying the focus ring. Radix sets `aria-haspopup=\"menu\"`, `aria-expanded`, and\n * `aria-controls` (pointing at the popup) for you. Pass `asChild` to wrap your own Button so the\n * trigger inherits its role, keyboard, and focus ring rather than nesting a second button; the bare\n * (non-`asChild`) form renders the default neutral-ghost trigger.\n */\nexport const MenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n MenuTriggerProps\n>(function MenuTrigger({ className, asChild, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n asChild={asChild}\n className={asChild ? className : cn(menuTriggerClass, className)}\n {...props}\n />\n );\n});\n\nexport interface MenuContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {}\n\n/**\n * Renders the portal and the popup (spec §2 popup): the floating `role=\"menu\"` surface that opens on\n * activation, raised above the page and anchored to the trigger. On open, focus moves into the popup\n * (first item, or last on Up) and roving tabindex tracks the active item; on close — by Escape,\n * activation, or click-away — focus returns to the trigger (Radix, spec §6/§7). The menu is NOT a\n * modal dialog: focus is not trapped, and Tab leaves the menu rather than stepping through items.\n * A neutral raised surface; brand violet and Verified Green never appear here (spec §3/§5/§8).\n */\nexport const MenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n MenuContentProps\n>(function MenuContent({ className, sideOffset = 4, loop = true, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n // `loop` wraps arrow movement at the ends (spec §6: \"wrapping at the ends\") — Radix leaves\n // it OFF by default, so we default it ON to honor the frozen keyboard model. Disabled items\n // are skipped by the roving handler for free (Radix, spec §4/§6).\n loop={loop}\n className={cn(menuPopupClass, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n});\n\nexport interface MenuItemProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>,\n \"color\"\n >,\n MenuItemVariantProps {\n /** The leading icon (spec §2): decorative, sized by `--size-icon-md`; the item names itself by its label text. */\n icon?: React.ReactNode;\n /** A trailing shortcut hint (spec §2): text such as \"⌘K\", in the muted label role; never a focus stop. */\n shortcut?: React.ReactNode;\n}\n\n/**\n * One command row (spec §2 item, §4 states): a `role=\"menuitem\"` whose activation runs its command\n * and closes the menu, returning focus to the trigger (Radix). It holds an optional leading icon, a\n * label, and an optional trailing shortcut hint. Pointer hover and keyboard arrow movement share ONE\n * highlight (Radix `data-highlighted`), so the active item is the same for both (spec §4 Hover).\n *\n * `destructive` (spec §3 `item=destructive`) marks the ONE colored item — a command that is\n * irreversible (revoke a key, delete a profile). It takes the destructive ACTION treatment and must\n * name the consequence in its TEXT, never by color alone (spec §7/§8). A `disabled` item stays in the\n * menu and readable to assistive technology (`aria-disabled`), is skipped by arrow movement, and does\n * not fire on activation (spec §4 Disabled / §7).\n */\nexport const MenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n MenuItemProps\n>(function MenuItem({ className, destructive, icon, shortcut, children, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(menuItemVariants({ destructive }), className)}\n {...props}\n >\n {icon ? (\n <span aria-hidden=\"true\" className={menuItemIconClass}>\n {icon}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut ? (\n <span aria-hidden=\"true\" className={menuItemShortcutClass}>\n {shortcut}\n </span>\n ) : null}\n </DropdownMenuPrimitive.Item>\n );\n});\n\nexport interface MenuGroupProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group> {\n /**\n * The non-interactive heading that partitions the popup (spec §2 group / group-label). It names the\n * group for assistive technology via `aria-labelledby` (Radix `Label`) and is NEVER a focus stop.\n */\n label?: React.ReactNode;\n}\n\n/**\n * A set of related items under a non-interactive `group-label` that partitions the popup (spec §2\n * group). The items read as a related set (`role=\"group\"` named by the label); the label is never a\n * menuitem and never a focus stop.\n */\nexport const MenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n MenuGroupProps\n>(function MenuGroup({ label, children, ...props }, ref) {\n // Radix Group + Label do NOT auto-wire aria-labelledby (the Label renders a bare div with no id),\n // so the group would read as an unnamed role=group. We hand-wire it the same way Separator\n // hand-rolls a named anatomy when the primitive can't carry it (skill: compose the role by hand\n // when Radix can't express the spec's anatomy): generate a stable id on the Label and point the\n // Group's aria-labelledby at it, so the items read as a related set named by the label (spec §2\n // group / §7 group named by its label).\n const labelId = React.useId();\n return (\n <DropdownMenuPrimitive.Group\n ref={ref}\n aria-labelledby={label ? labelId : undefined}\n {...props}\n >\n {label ? (\n <DropdownMenuPrimitive.Label id={labelId} className={menuLabelClass}>\n {label}\n </DropdownMenuPrimitive.Label>\n ) : null}\n {children}\n </DropdownMenuPrimitive.Group>\n );\n});\n\nexport interface MenuLabelProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> {}\n\n/**\n * A standalone non-interactive section label (spec §2 group-label) for a label that is not wrapped in\n * a `MenuGroup`. Like the group label it is the muted label-role heading and is never a focus stop.\n */\nexport const MenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n MenuLabelProps\n>(function MenuLabel({ className, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Label ref={ref} className={cn(menuLabelClass, className)} {...props} />\n );\n});\n\nexport interface MenuSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> {}\n\n/**\n * A thin neutral divider between groups (spec §2 separator): decorative (`role=\"separator\"`), never a\n * focus stop.\n */\nexport const MenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n MenuSeparatorProps\n>(function MenuSeparator({ className, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(menuSeparatorClass, className)}\n {...props}\n />\n );\n});\n\nexport interface MenuSubProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Sub> {}\n\n/**\n * A submenu: an item that opens a nested popup of its own items (spec §2 submenu). Keep nesting\n * shallow — deep trees are hard to operate by keyboard (spec §2). Wraps `MenuSubTrigger` +\n * `MenuSubContent`.\n */\nexport function MenuSub(props: MenuSubProps) {\n return <DropdownMenuPrimitive.Sub {...props} />;\n}\n\nexport interface MenuSubTriggerProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>,\n \"color\"\n >,\n MenuItemVariantProps {\n /** The leading icon (spec §2): decorative, sized by `--size-icon-md`. */\n icon?: React.ReactNode;\n}\n\n/**\n * The item that opens a submenu (spec §2/§6): a `role=\"menuitem\"` with `aria-haspopup=\"menu\"` and its\n * own `aria-expanded` (Radix). Right opens the submenu and focuses its first item; Left closes it and\n * returns focus here. It carries the same row treatment as a `MenuItem`, plus a trailing chevron\n * pointing to the inline-end.\n */\nexport const MenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n MenuSubTriggerProps\n>(function MenuSubTrigger({ className, destructive, icon, children, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(menuItemVariants({ destructive }), className)}\n {...props}\n >\n {icon ? (\n <span aria-hidden=\"true\" className={menuItemIconClass}>\n {icon}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n <ChevronGlyph />\n </DropdownMenuPrimitive.SubTrigger>\n );\n});\n\nexport interface MenuSubContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> {}\n\n/**\n * The nested popup of a submenu (spec §2 submenu): the same neutral raised `role=\"menu\"` surface as\n * `MenuContent`, anchored to its `MenuSubTrigger`.\n */\nexport const MenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n MenuSubContentProps\n>(function MenuSubContent({ className, sideOffset = 2, loop = true, ...props }, ref) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={sideOffset}\n // `loop` wraps arrow movement at the ends inside the submenu too (spec §6), matching the\n // parent popup; Radix leaves it OFF by default.\n loop={loop}\n className={cn(menuPopupClass, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n});\n\n// The submenu chevron — inline SVG (no icon dep), --size-icon-md, pointing to the inline-end to\n// signal the nested popup. Decorative (aria-hidden); aria-haspopup/aria-expanded carry the state, not\n// the glyph (spec §2/§7). Drawn with currentColor so it inherits the row's color.\nfunction ChevronGlyph() {\n return (\n <span data-testid=\"menu-sub-chevron\" aria-hidden=\"true\" className={menuSubChevronClass}>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" focusable=\"false\">\n <path d=\"M6 4l4 4-4 4\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </span>\n );\n}\n"],"mappings":"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","names":["MenuTrigger","MenuContent","MenuItem","MenuGroup","MenuLabel","MenuSeparator","MenuSubTrigger","MenuSubContent"]}
@@ -8,6 +8,6 @@ export type MenuItemVariantProps = VariantProps<typeof menuItemVariants>;
8
8
  export declare const menuItemIconClass = "inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center";
9
9
  export declare const menuItemShortcutClass = "ms-auto ps-(--space-4) text-label text-text-muted";
10
10
  export declare const menuSubChevronClass = "ms-auto inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center";
11
- export declare const menuLabelClass = "px-(--space-2) py-(--space-1) text-label text-text-muted select-none";
11
+ export declare const menuLabelClass = "px-(--space-2) py-(--space-1) text-label text-text-secondary select-none";
12
12
  export declare const menuSeparatorClass = "-mx-(--space-1) my-(--space-1) h-px bg-border-default";
13
13
  //# sourceMappingURL=menu.variants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"menu.variants.d.ts","sourceRoot":"","sources":["../../../src/components/menu/menu.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAmBlE,eAAO,MAAM,gBAAgB,QAQ+B,CAAC;AAU7D,eAAO,MAAM,cAAc,QAKoC,CAAC;AAyBhE,eAAO,MAAM,gBAAgB;;8EAoC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,YAAY,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAMzE,eAAO,MAAM,iBAAiB,2FAC4D,CAAC;AAK3F,eAAO,MAAM,qBAAqB,sDACmB,CAAC;AAItD,eAAO,MAAM,mBAAmB,mGACkE,CAAC;AAKnG,eAAO,MAAM,cAAc,yEAC6C,CAAC;AAMzE,eAAO,MAAM,kBAAkB,0DAC0B,CAAC"}
1
+ {"version":3,"file":"menu.variants.d.ts","sourceRoot":"","sources":["../../../src/components/menu/menu.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAoBlE,eAAO,MAAM,gBAAgB,QAQ+B,CAAC;AAU7D,eAAO,MAAM,cAAc,QAKoC,CAAC;AAyBhE,eAAO,MAAM,gBAAgB;;8EAoC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,YAAY,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAMzE,eAAO,MAAM,iBAAiB,2FAC4D,CAAC;AAK3F,eAAO,MAAM,qBAAqB,sDACmB,CAAC;AAItD,eAAO,MAAM,mBAAmB,mGACkE,CAAC;AAMnG,eAAO,MAAM,cAAc,6EACiD,CAAC;AAM7E,eAAO,MAAM,kBAAkB,0DAC0B,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
- const menuTriggerClass = "inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) text-label text-action-ghost-fg cursor-pointer select-none hover:bg-action-ghost-bg-hover transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 disabled:pointer-events-none disabled:text-text-disabled";
2
+ import { focusRing } from "../../lib/focus-ring";
3
+ const menuTriggerClass = "inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) text-label text-action-ghost-fg cursor-pointer select-none hover:bg-action-ghost-bg-hover transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) " + focusRing + " disabled:pointer-events-none disabled:text-text-disabled";
3
4
  const menuPopupClass = "z-(--z-index-popover) min-w-(--container-sm) overflow-hidden p-(--space-1) bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) data-[state=open]:opacity-100 data-[state=closed]:opacity-0";
4
5
  const menuItemVariants = cva(
5
6
  [
@@ -41,7 +42,7 @@ const menuItemVariants = cva(
41
42
  const menuItemIconClass = "inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center";
42
43
  const menuItemShortcutClass = "ms-auto ps-(--space-4) text-label text-text-muted";
43
44
  const menuSubChevronClass = "ms-auto inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center";
44
- const menuLabelClass = "px-(--space-2) py-(--space-1) text-label text-text-muted select-none";
45
+ const menuLabelClass = "px-(--space-2) py-(--space-1) text-label text-text-secondary select-none";
45
46
  const menuSeparatorClass = "-mx-(--space-1) my-(--space-1) h-px bg-border-default";
46
47
  export {
47
48
  menuItemIconClass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/menu/menu.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\n\n// Menu is a popup list of ACTIONS a trigger opens (spec §1). It is a NEUTRAL surface (spec §3): its\n// items, popup, and separators are neutral, and the brand violet NEVER marks an item as \"the\n// special one.\" The ONE colored item is the DESTRUCTIVE item, which takes the destructive ACTION\n// treatment because the command it runs is irreversible — a RISK signal, not a status result. A\n// verified meaning is never reported by a menu item (that is VerifiedBadge's job), so NOTHING here\n// binds a --color-status-* token and the brand action-primary tier never appears (brand != state,\n// G-U2). This is the ONLY token-binding site (skill §5 hard rule). All open/close motion is the\n// FAST token transition on the verdify easing, instant under reduced motion — never the 350ms\n// VerifiedBadge-only theatre duration (G-U3).\n\n// The trigger: the one stop in the page tab order for this control (spec §2 trigger). A NEUTRAL\n// ghost surface — the label/glyph in the ghost action fg at rest, the restrained ghost hover fill,\n// the md radius, the persistent 2px focus ring (never removed, spec §4 Focus), and the target-size\n// floor (44px touch / 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor.\n// A disabled trigger dims via the disabled TOKEN (DEC-C), never a blanket opacity. fast functional\n// hover motion + verdify easing, instant under reduced motion (G-U3). This styles the DEFAULT\n// (non-asChild) trigger; when a Button is passed via `asChild` it carries its own treatment.\nexport const menuTriggerClass =\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) \" +\n \"text-label text-action-ghost-fg cursor-pointer select-none \" +\n \"hover:bg-action-ghost-bg-hover \" +\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) \" +\n \"outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 \" +\n \"disabled:pointer-events-none disabled:text-text-disabled\";\n\n// The popup (spec §2 popup, §5): the floating surface that opens on activation, raised above the\n// page and anchored to the trigger. A NEUTRAL raised surface (--color-surface-raised) with the\n// outer surface border, the md corner radius, and the md elevation shadow above the page, on the\n// POPOVER z-layer (a menu is a non-modal popover layer, not the modal layer). It NEVER wears a brand\n// or status fill (spec §3/§8). The open/close fade is a PLAIN fast transition + verdify easing,\n// instant under reduced motion — never the 350ms VerifiedBadge-only theatre (G-U3). Enter/exit ride\n// Radix's data-state on the content (attribute-selector variants, not arbitrary values). Inset\n// padding from --space-*; a SubContent shares the same surface treatment.\nexport const menuPopupClass =\n \"z-(--z-index-popover) min-w-(--container-sm) overflow-hidden p-(--space-1) \" +\n \"bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) \" +\n \"transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"data-[state=open]:opacity-100 data-[state=closed]:opacity-0\";\n\n// One command row (spec §2 item, §4 states). A neutral row at rest; the active/hovered item shares\n// ONE highlight (pointer and keyboard both drive Radix's data-highlighted, spec §4 Hover) painted\n// with the ghost hover fill.\n//\n// RESTING (default): the LABEL in the PRIMARY text color (spec §5 --color-text-primary) at the BODY\n// type role (spec §5 --text-body), on the popup surface with NO fill (spec §4 Default).\n// HIGHLIGHTED (data-highlighted): the restrained ghost-action hover fill (spec §5\n// --color-action-ghost-bg-hover) — pointer hover AND keyboard arrow movement set the same\n// data-highlighted, so the two share one highlight (spec §4 Hover/Active). No motion beyond the\n// token transition.\n// DISABLED (data-disabled): dims via the disabled TOKEN (DEC-C), never a blanket opacity; Radix\n// keeps it readable to AT (aria-disabled) and skips it in arrow movement (spec §4 Disabled / §7).\n// FOCUS: the open popup tracks the active item by ROVING FOCUS and shows the active fill, not a\n// second ring (spec §4 Focus) — so an item does NOT paint its own focus-visible ring; the active\n// fill is the focus affordance inside the menu.\n// Motion: fast token transition + verdify easing, instant under reduced motion (NEVER the check\n// theatre, G-U3). Target-size floor on every row (44px touch / 40px pointer, spec §7 2.5.8), never a\n// fixed height below the floor.\n//\n// `destructive` is the spec §3 `item=destructive` axis — the ONE colored item: the destructive\n// ACTION treatment (label/icon in the destructive fg; the highlight fill is the destructive bg). It\n// is a RISK signal, not the brand and NEVER status-verified (G-U2); the risk is also named in the\n// item's text + icon, never carried by color alone (spec §7/§8, 1.4.1).\nexport const menuItemVariants = cva(\n [\n // shape + the icon-to-label gap + logical inline padding so it mirrors under RTL (G-U6)\n \"relative flex items-center gap-(--space-2) rounded-(--radius-md) px-(--space-2)\",\n // type ROLE + the resting (neutral) label color, no fill, pointer cursor\n \"text-body text-text-primary no-underline cursor-pointer select-none\",\n // the shared pointer+keyboard highlight: the restrained ghost-action fill (spec §4 Hover/Active)\n \"data-[highlighted]:bg-action-ghost-bg-hover\",\n // motion: fast + verdify easing, instant under reduced motion (NEVER the check theatre, G-U3)\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify)\",\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // target-size floor — 44px touch / 40px pointer, on every row (spec §7, 2.5.8), never a fixed\n // height below the floor\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // the open popup tracks the active item by ROVING focus + the active fill, not a second ring\n // (spec §4 Focus) — no per-item focus-visible ring inside the menu\n \"outline-none\",\n // disabled (non-operable) row — DEC-C: dim via the disabled TOKEN, never opacity. Radix drives\n // it via data-disabled and keeps the label readable to AT (aria-disabled), skipping arrow focus.\n \"data-[disabled]:pointer-events-none data-[disabled]:text-text-disabled\",\n ],\n {\n variants: {\n // the spec §3 `item=destructive` axis — the ONE colored item: the destructive ACTION\n // treatment, a RISK signal (not the brand, NEVER status-verified — G-U2). At rest the label +\n // icon take the destructive fg; the shared highlight fill is the destructive bg.\n destructive: {\n true: [\n \"text-action-destructive-fg\",\n \"data-[highlighted]:bg-action-destructive-bg\",\n ],\n false: \"\",\n },\n },\n defaultVariants: { destructive: false },\n },\n);\n\nexport type MenuItemVariantProps = VariantProps<typeof menuItemVariants>;\n\n// The leading item icon (spec §2/§5): the md icon role, decorative (the item names itself by its\n// label text, not the glyph). It inherits the row's color via currentColor, so a destructive row's\n// icon is the destructive fg and a disabled row's icon dims with the disabled token. shrink-0 so it\n// never collapses.\nexport const menuItemIconClass =\n \"inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center\";\n\n// The trailing shortcut hint on an item (spec §2/§5): the keyboard hint pushed to the inline-end, in\n// the MUTED text color at the LABEL type role (spec §5 --color-text-muted / --text-label).\n// Decorative wayfinding, never a focus stop; logical inline-end placement (G-U6).\nexport const menuItemShortcutClass =\n \"ms-auto ps-(--space-4) text-label text-text-muted\";\n\n// The submenu chevron on a SubTrigger (spec §2 submenu, §5): the md icon role, decorative; it\n// points to the inline-end to signal the nested popup. Inherits the row color via currentColor.\nexport const menuSubChevronClass =\n \"ms-auto inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center\";\n\n// The group label (spec §2 group-label, §5): the non-interactive heading that partitions the popup;\n// it is NEVER a focus stop. The MUTED text color at the LABEL type role (spec §5 --color-text-muted\n// / --text-label). Logical inline padding so it mirrors under RTL (G-U6).\nexport const menuLabelClass =\n \"px-(--space-2) py-(--space-1) text-label text-text-muted select-none\";\n\n// The separator (spec §2 separator, §5): a thin neutral divider between groups. It is decorative and\n// never takes focus. A neutral hairline in the default border color (spec §5 --color-border-default),\n// with a little vertical breathing room. Negated logical inline margins keep the rule flush to the\n// popup's inner padding edge (it spans the popup inset, mirrors under RTL — G-U6).\nexport const menuSeparatorClass =\n \"-mx-(--space-1) my-(--space-1) h-px bg-border-default\";\n"],"mappings":"AAAA,SAAS,WAA8B;AAmBhC,MAAM,mBACX;AAiBK,MAAM,iBACX;AA6BK,MAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA;AAAA;AAAA,MAIR,aAAa;AAAA,QACX,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,aAAa,MAAM;AAAA,EACxC;AACF;AAQO,MAAM,oBACX;AAKK,MAAM,wBACX;AAIK,MAAM,sBACX;AAKK,MAAM,iBACX;AAMK,MAAM,qBACX;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/menu/menu.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport { focusRing } from \"../../lib/focus-ring\";\n\n// Menu is a popup list of ACTIONS a trigger opens (spec §1). It is a NEUTRAL surface (spec §3): its\n// items, popup, and separators are neutral, and the brand violet NEVER marks an item as \"the\n// special one.\" The ONE colored item is the DESTRUCTIVE item, which takes the destructive ACTION\n// treatment because the command it runs is irreversible — a RISK signal, not a status result. A\n// verified meaning is never reported by a menu item (that is VerifiedBadge's job), so NOTHING here\n// binds a --color-status-* token and the brand action-primary tier never appears (brand != state,\n// G-U2). This is the ONLY token-binding site (skill §5 hard rule). All open/close motion is the\n// FAST token transition on the verdify easing, instant under reduced motion — never the 350ms\n// VerifiedBadge-only theatre duration (G-U3).\n\n// The trigger: the one stop in the page tab order for this control (spec §2 trigger). A NEUTRAL\n// ghost surface — the label/glyph in the ghost action fg at rest, the restrained ghost hover fill,\n// the md radius, the persistent 2px focus ring (never removed, spec §4 Focus), and the target-size\n// floor (44px touch / 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor.\n// A disabled trigger dims via the disabled TOKEN (DEC-C), never a blanket opacity. fast functional\n// hover motion + verdify easing, instant under reduced motion (G-U3). This styles the DEFAULT\n// (non-asChild) trigger; when a Button is passed via `asChild` it carries its own treatment.\nexport const menuTriggerClass =\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) \" +\n \"text-label text-action-ghost-fg cursor-pointer select-none \" +\n \"hover:bg-action-ghost-bg-hover \" +\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) \" +\n focusRing + \" \" +\n \"disabled:pointer-events-none disabled:text-text-disabled\";\n\n// The popup (spec §2 popup, §5): the floating surface that opens on activation, raised above the\n// page and anchored to the trigger. A NEUTRAL raised surface (--color-surface-raised) with the\n// outer surface border, the md corner radius, and the md elevation shadow above the page, on the\n// POPOVER z-layer (a menu is a non-modal popover layer, not the modal layer). It NEVER wears a brand\n// or status fill (spec §3/§8). The open/close fade is a PLAIN fast transition + verdify easing,\n// instant under reduced motion — never the 350ms VerifiedBadge-only theatre (G-U3). Enter/exit ride\n// Radix's data-state on the content (attribute-selector variants, not arbitrary values). Inset\n// padding from --space-*; a SubContent shares the same surface treatment.\nexport const menuPopupClass =\n \"z-(--z-index-popover) min-w-(--container-sm) overflow-hidden p-(--space-1) \" +\n \"bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) \" +\n \"transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"data-[state=open]:opacity-100 data-[state=closed]:opacity-0\";\n\n// One command row (spec §2 item, §4 states). A neutral row at rest; the active/hovered item shares\n// ONE highlight (pointer and keyboard both drive Radix's data-highlighted, spec §4 Hover) painted\n// with the ghost hover fill.\n//\n// RESTING (default): the LABEL in the PRIMARY text color (spec §5 --color-text-primary) at the BODY\n// type role (spec §5 --text-body), on the popup surface with NO fill (spec §4 Default).\n// HIGHLIGHTED (data-highlighted): the restrained ghost-action hover fill (spec §5\n// --color-action-ghost-bg-hover) — pointer hover AND keyboard arrow movement set the same\n// data-highlighted, so the two share one highlight (spec §4 Hover/Active). No motion beyond the\n// token transition.\n// DISABLED (data-disabled): dims via the disabled TOKEN (DEC-C), never a blanket opacity; Radix\n// keeps it readable to AT (aria-disabled) and skips it in arrow movement (spec §4 Disabled / §7).\n// FOCUS: the open popup tracks the active item by ROVING FOCUS and shows the active fill, not a\n// second ring (spec §4 Focus) — so an item does NOT paint its own focus-visible ring; the active\n// fill is the focus affordance inside the menu.\n// Motion: fast token transition + verdify easing, instant under reduced motion (NEVER the check\n// theatre, G-U3). Target-size floor on every row (44px touch / 40px pointer, spec §7 2.5.8), never a\n// fixed height below the floor.\n//\n// `destructive` is the spec §3 `item=destructive` axis — the ONE colored item: the destructive\n// ACTION treatment (label/icon in the destructive fg; the highlight fill is the destructive bg). It\n// is a RISK signal, not the brand and NEVER status-verified (G-U2); the risk is also named in the\n// item's text + icon, never carried by color alone (spec §7/§8, 1.4.1).\nexport const menuItemVariants = cva(\n [\n // shape + the icon-to-label gap + logical inline padding so it mirrors under RTL (G-U6)\n \"relative flex items-center gap-(--space-2) rounded-(--radius-md) px-(--space-2)\",\n // type ROLE + the resting (neutral) label color, no fill, pointer cursor\n \"text-body text-text-primary no-underline cursor-pointer select-none\",\n // the shared pointer+keyboard highlight: the restrained ghost-action fill (spec §4 Hover/Active)\n \"data-[highlighted]:bg-action-ghost-bg-hover\",\n // motion: fast + verdify easing, instant under reduced motion (NEVER the check theatre, G-U3)\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify)\",\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // target-size floor — 44px touch / 40px pointer, on every row (spec §7, 2.5.8), never a fixed\n // height below the floor\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // the open popup tracks the active item by ROVING focus + the active fill, not a second ring\n // (spec §4 Focus) — no per-item focus-visible ring inside the menu\n \"outline-none\",\n // disabled (non-operable) row — DEC-C: dim via the disabled TOKEN, never opacity. Radix drives\n // it via data-disabled and keeps the label readable to AT (aria-disabled), skipping arrow focus.\n \"data-[disabled]:pointer-events-none data-[disabled]:text-text-disabled\",\n ],\n {\n variants: {\n // the spec §3 `item=destructive` axis — the ONE colored item: the destructive ACTION\n // treatment, a RISK signal (not the brand, NEVER status-verified — G-U2). At rest the label +\n // icon take the destructive fg; the shared highlight fill is the destructive bg.\n destructive: {\n true: [\n \"text-action-destructive-fg\",\n \"data-[highlighted]:bg-action-destructive-bg\",\n ],\n false: \"\",\n },\n },\n defaultVariants: { destructive: false },\n },\n);\n\nexport type MenuItemVariantProps = VariantProps<typeof menuItemVariants>;\n\n// The leading item icon (spec §2/§5): the md icon role, decorative (the item names itself by its\n// label text, not the glyph). It inherits the row's color via currentColor, so a destructive row's\n// icon is the destructive fg and a disabled row's icon dims with the disabled token. shrink-0 so it\n// never collapses.\nexport const menuItemIconClass =\n \"inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center\";\n\n// The trailing shortcut hint on an item (spec §2/§5): the keyboard hint pushed to the inline-end, in\n// the MUTED text color at the LABEL type role (spec §5 --color-text-muted / --text-label).\n// Decorative wayfinding, never a focus stop; logical inline-end placement (G-U6).\nexport const menuItemShortcutClass =\n \"ms-auto ps-(--space-4) text-label text-text-muted\";\n\n// The submenu chevron on a SubTrigger (spec §2 submenu, §5): the md icon role, decorative; it\n// points to the inline-end to signal the nested popup. Inherits the row color via currentColor.\nexport const menuSubChevronClass =\n \"ms-auto inline-flex h-(--size-icon-md) w-(--size-icon-md) shrink-0 items-center justify-center\";\n\n// The group label (spec §2 group-label, §5): the non-interactive heading that partitions the popup;\n// it is NEVER a focus stop. It is essential de-emphasized text (it names the group), so it uses the\n// SECONDARY text color (AA) at the LABEL type role (spec §5 --color-text-secondary / --text-label) —\n// not the decorative-only muted role (accessibility.md). Logical inline padding mirrors under RTL.\nexport const menuLabelClass =\n \"px-(--space-2) py-(--space-1) text-label text-text-secondary select-none\";\n\n// The separator (spec §2 separator, §5): a thin neutral divider between groups. It is decorative and\n// never takes focus. A neutral hairline in the default border color (spec §5 --color-border-default),\n// with a little vertical breathing room. Negated logical inline margins keep the rule flush to the\n// popup's inner padding edge (it spans the popup inset, mirrors under RTL — G-U6).\nexport const menuSeparatorClass =\n \"-mx-(--space-1) my-(--space-1) h-px bg-border-default\";\n"],"mappings":"AAAA,SAAS,WAA8B;AACvC,SAAS,iBAAiB;AAmBnB,MAAM,mBACX,kZAMA,YAAY;AAWP,MAAM,iBACX;AA6BK,MAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA;AAAA;AAAA,MAIR,aAAa;AAAA,QACX,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,aAAa,MAAM;AAAA,EACxC;AACF;AAQO,MAAM,oBACX;AAKK,MAAM,wBACX;AAIK,MAAM,sBACX;AAMK,MAAM,iBACX;AAMK,MAAM,qBACX;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.variants.d.ts","sourceRoot":"","sources":["../../../src/components/pagination/pagination.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAalE,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAIrE,eAAO,MAAM,mBAAmB,gDACe,CAAC;AAGhD,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAY9D,eAAO,MAAM,yBAAyB;;;8EA6CrC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,YAAY,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI3F,eAAO,MAAM,mBAAmB,2FAC0D,CAAC;AAI3F,eAAO,MAAM,qBAAqB,uFACoD,CAAC;AAKvF,eAAO,MAAM,uBAAuB,2GACsE,CAAC"}
1
+ {"version":3,"file":"pagination.variants.d.ts","sourceRoot":"","sources":["../../../src/components/pagination/pagination.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAclE,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAIrE,eAAO,MAAM,mBAAmB,gDACe,CAAC;AAGhD,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAY9D,eAAO,MAAM,yBAAyB;;;8EA6CrC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,YAAY,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI3F,eAAO,MAAM,mBAAmB,2FAC0D,CAAC;AAI3F,eAAO,MAAM,qBAAqB,uFACoD,CAAC;AAKvF,eAAO,MAAM,uBAAuB,2GACsE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { cva } from "class-variance-authority";
2
+ import { focusRing } from "../../lib/focus-ring";
2
3
  const paginationNavClass = "bg-surface-canvas py-(--space-2)";
3
4
  const paginationListClass = "flex flex-wrap items-center gap-(--space-4)";
4
5
  const paginationItemClass = "inline-flex items-center";
@@ -18,7 +19,7 @@ const paginationControlVariants = cva(
18
19
  "min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)",
19
20
  // focus ring — identical on every state, never removed (spec §4 / 2.4.7)
20
21
  "outline-none",
21
- "focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2",
22
+ focusRing,
22
23
  // disabled prev/next — DEC-C: dim via the disabled TOKEN for BOTH the native-button form
23
24
  // (`disabled`) and the link form (`aria-disabled`), never a blanket opacity. The component
24
25
  // also strips href + tabindex on a disabled link so it cannot navigate or be tabbed to.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/pagination/pagination.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\n\n// Pagination is a row of standard links/buttons inside a labeled navigation landmark (spec §7):\n// there is no APG \"pagination\" widget. Brand and status colors are accents — NEUTRALS carry the\n// surface (spec §3). The one accent the pager paints is on the CURRENT page, and that accent is\n// the primary ACTION (brand) alias, never a status color: a current page reports where you are in\n// the set, not a verification result, so it binds nothing from the status tier (brand != state,\n// G-U2). The set paints from the text, action(primary + ghost), border, and surface aliases only\n// (spec §5).\n\n// The nav landmark wrapping the control set. A neutral canvas surface with logical-property block\n// padding so the row mirrors under dir=\"rtl\" (G-U6). The optional divider above the set (spec §5,\n// border-default) is a caller decision applied via className, not a default binding.\nexport const paginationNavClass = \"bg-surface-canvas py-(--space-2)\";\n\n// The control list. An inline row of items with the inter-control gap; wraps when narrow. The\n// list carries no text color — each control sets its own.\nexport const paginationListClass =\n \"flex flex-wrap items-center gap-(--space-4)\";\n\n// A single item (the <li>). Inline so the control sits in the row.\nexport const paginationItemClass = \"inline-flex items-center\";\n\n// A page / prev / next control (spec §4). At rest, an enabled non-current control is the label\n// type role in the SECONDARY text color with NO fill; on hover it takes the restrained ghost fill\n// (the only fill a non-current control paints) and the cursor is a pointer. The CURRENT page lifts\n// its label to the primary action FG on the primary action BG and does not navigate — this accent\n// is the BRAND action alias (where you are), never a status. The focus ring is part of the base on\n// every state, never removed. Motion is the fast token transition on the verdify easing, collapsing\n// to the instant endpoint under reduced motion — never the 350ms VerifiedBadge-only theatre\n// duration (a page turning is a plain wait, G-U3). A disabled prev/next dims via the disabled TOKEN\n// (DEC-C): native `disabled:` for a button-form control and `aria-disabled:` for a link-form one\n// (an <a> has no native disabled), never a blanket opacity.\nexport const paginationControlVariants = cva(\n [\n // type ROLE + shape + the icon-to-label gap; logical inline padding so it mirrors under RTL\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-2)\",\n \"text-label font-medium select-none\",\n // resting state — enabled, non-current: secondary label, no fill, pointer cursor\n \"cursor-pointer text-text-secondary\",\n // hover: the restrained ghost fill (the only fill a non-current control paints)\n \"hover:bg-action-ghost-bg-hover\",\n // motion: fast + verdify easing, instant under reduced motion (NEVER the check theatre)\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify)\",\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // target-size floor — 44px touch / 40px pointer, on every control (spec §7, 2.5.8)\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // focus ring — identical on every state, never removed (spec §4 / 2.4.7)\n \"outline-none\",\n \"focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2\",\n // disabled prev/next — DEC-C: dim via the disabled TOKEN for BOTH the native-button form\n // (`disabled`) and the link form (`aria-disabled`), never a blanket opacity. The component\n // also strips href + tabindex on a disabled link so it cannot navigate or be tabbed to.\n \"disabled:pointer-events-none disabled:text-text-disabled\",\n \"aria-disabled:pointer-events-none aria-disabled:text-text-disabled\",\n ],\n {\n variants: {\n // STATE axis (spec §4): the current page is the only control carrying the brand action fill.\n current: {\n true: [\n // the current page: brand action accent (where you are in the set), non-navigating.\n // This is the action(primary) alias, NEVER status-verified (brand != state, G-U2).\n \"bg-action-primary-bg text-action-primary-fg\",\n // the current page is not a control: no hover fill, no pointer\n \"cursor-default hover:bg-action-primary-bg\",\n ],\n false: \"\",\n },\n // SIZE axis (spec §3, DEC-B): both sizes hold the shared target-size floor above; sm differs\n // only by density (tighter vertical padding) below the type role, never a height below the floor.\n size: {\n md: \"py-(--space-2)\",\n sm: \"py-(--space-1)\",\n },\n },\n defaultVariants: { current: false, size: \"md\" },\n },\n);\n\nexport type PaginationControlVariantProps = VariantProps<typeof paginationControlVariants>;\n\n// The prev/next direction icon (spec §5): the sm icon role, decorative (the control names its\n// direction via aria-label, not the glyph). Inherits the control's current text color.\nexport const paginationIconClass =\n \"inline-flex h-(--size-icon-sm) w-(--size-icon-sm) shrink-0 items-center justify-center\";\n\n// The \"Page m of n\" readout for the prev-next variant (spec §3): plain text in the secondary\n// color at the label type role. Not a control — a non-interactive status of position in the set.\nexport const paginationStatusClass =\n \"inline-flex items-center px-(--space-2) text-label text-text-secondary select-none\";\n\n// The ellipsis gap (spec §2/§7): a DECORATIVE, non-interactive stand-in for a run of hidden page\n// controls, in the muted text color at the sm icon role. Removed from the a11y tree + tab order by\n// the component (aria-hidden); it is never a stop.\nexport const paginationEllipsisClass =\n \"inline-flex h-(--size-icon-sm) w-(--size-icon-sm) shrink-0 items-center justify-center text-text-muted\";\n"],"mappings":"AAAA,SAAS,WAA8B;AAahC,MAAM,qBAAqB;AAI3B,MAAM,sBACX;AAGK,MAAM,sBAAsB;AAY5B,MAAM,4BAA4B;AAAA,EACvC;AAAA;AAAA,IAEE;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,MAER,SAAS;AAAA,QACP,MAAM;AAAA;AAAA;AAAA,UAGJ;AAAA;AAAA,UAEA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,MACT;AAAA;AAAA;AAAA,MAGA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,SAAS,OAAO,MAAM,KAAK;AAAA,EAChD;AACF;AAMO,MAAM,sBACX;AAIK,MAAM,wBACX;AAKK,MAAM,0BACX;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/pagination/pagination.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport { focusRing } from \"../../lib/focus-ring\";\n\n// Pagination is a row of standard links/buttons inside a labeled navigation landmark (spec §7):\n// there is no APG \"pagination\" widget. Brand and status colors are accents — NEUTRALS carry the\n// surface (spec §3). The one accent the pager paints is on the CURRENT page, and that accent is\n// the primary ACTION (brand) alias, never a status color: a current page reports where you are in\n// the set, not a verification result, so it binds nothing from the status tier (brand != state,\n// G-U2). The set paints from the text, action(primary + ghost), border, and surface aliases only\n// (spec §5).\n\n// The nav landmark wrapping the control set. A neutral canvas surface with logical-property block\n// padding so the row mirrors under dir=\"rtl\" (G-U6). The optional divider above the set (spec §5,\n// border-default) is a caller decision applied via className, not a default binding.\nexport const paginationNavClass = \"bg-surface-canvas py-(--space-2)\";\n\n// The control list. An inline row of items with the inter-control gap; wraps when narrow. The\n// list carries no text color — each control sets its own.\nexport const paginationListClass =\n \"flex flex-wrap items-center gap-(--space-4)\";\n\n// A single item (the <li>). Inline so the control sits in the row.\nexport const paginationItemClass = \"inline-flex items-center\";\n\n// A page / prev / next control (spec §4). At rest, an enabled non-current control is the label\n// type role in the SECONDARY text color with NO fill; on hover it takes the restrained ghost fill\n// (the only fill a non-current control paints) and the cursor is a pointer. The CURRENT page lifts\n// its label to the primary action FG on the primary action BG and does not navigate — this accent\n// is the BRAND action alias (where you are), never a status. The focus ring is part of the base on\n// every state, never removed. Motion is the fast token transition on the verdify easing, collapsing\n// to the instant endpoint under reduced motion — never the 350ms VerifiedBadge-only theatre\n// duration (a page turning is a plain wait, G-U3). A disabled prev/next dims via the disabled TOKEN\n// (DEC-C): native `disabled:` for a button-form control and `aria-disabled:` for a link-form one\n// (an <a> has no native disabled), never a blanket opacity.\nexport const paginationControlVariants = cva(\n [\n // type ROLE + shape + the icon-to-label gap; logical inline padding so it mirrors under RTL\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-2)\",\n \"text-label font-medium select-none\",\n // resting state — enabled, non-current: secondary label, no fill, pointer cursor\n \"cursor-pointer text-text-secondary\",\n // hover: the restrained ghost fill (the only fill a non-current control paints)\n \"hover:bg-action-ghost-bg-hover\",\n // motion: fast + verdify easing, instant under reduced motion (NEVER the check theatre)\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify)\",\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // target-size floor — 44px touch / 40px pointer, on every control (spec §7, 2.5.8)\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // focus ring — identical on every state, never removed (spec §4 / 2.4.7)\n \"outline-none\",\n focusRing,\n // disabled prev/next — DEC-C: dim via the disabled TOKEN for BOTH the native-button form\n // (`disabled`) and the link form (`aria-disabled`), never a blanket opacity. The component\n // also strips href + tabindex on a disabled link so it cannot navigate or be tabbed to.\n \"disabled:pointer-events-none disabled:text-text-disabled\",\n \"aria-disabled:pointer-events-none aria-disabled:text-text-disabled\",\n ],\n {\n variants: {\n // STATE axis (spec §4): the current page is the only control carrying the brand action fill.\n current: {\n true: [\n // the current page: brand action accent (where you are in the set), non-navigating.\n // This is the action(primary) alias, NEVER status-verified (brand != state, G-U2).\n \"bg-action-primary-bg text-action-primary-fg\",\n // the current page is not a control: no hover fill, no pointer\n \"cursor-default hover:bg-action-primary-bg\",\n ],\n false: \"\",\n },\n // SIZE axis (spec §3, DEC-B): both sizes hold the shared target-size floor above; sm differs\n // only by density (tighter vertical padding) below the type role, never a height below the floor.\n size: {\n md: \"py-(--space-2)\",\n sm: \"py-(--space-1)\",\n },\n },\n defaultVariants: { current: false, size: \"md\" },\n },\n);\n\nexport type PaginationControlVariantProps = VariantProps<typeof paginationControlVariants>;\n\n// The prev/next direction icon (spec §5): the sm icon role, decorative (the control names its\n// direction via aria-label, not the glyph). Inherits the control's current text color.\nexport const paginationIconClass =\n \"inline-flex h-(--size-icon-sm) w-(--size-icon-sm) shrink-0 items-center justify-center\";\n\n// The \"Page m of n\" readout for the prev-next variant (spec §3): plain text in the secondary\n// color at the label type role. Not a control — a non-interactive status of position in the set.\nexport const paginationStatusClass =\n \"inline-flex items-center px-(--space-2) text-label text-text-secondary select-none\";\n\n// The ellipsis gap (spec §2/§7): a DECORATIVE, non-interactive stand-in for a run of hidden page\n// controls, in the muted text color at the sm icon role. Removed from the a11y tree + tab order by\n// the component (aria-hidden); it is never a stop.\nexport const paginationEllipsisClass =\n \"inline-flex h-(--size-icon-sm) w-(--size-icon-sm) shrink-0 items-center justify-center text-text-muted\";\n"],"mappings":"AAAA,SAAS,WAA8B;AACvC,SAAS,iBAAiB;AAanB,MAAM,qBAAqB;AAI3B,MAAM,sBACX;AAGK,MAAM,sBAAsB;AAY5B,MAAM,4BAA4B;AAAA,EACvC;AAAA;AAAA,IAEE;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA,MAER,SAAS;AAAA,QACP,MAAM;AAAA;AAAA;AAAA,UAGJ;AAAA;AAAA,UAEA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,MACT;AAAA;AAAA;AAAA,MAGA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,SAAS,OAAO,MAAM,KAAK;AAAA,EAChD;AACF;AAMO,MAAM,sBACX;AAIK,MAAM,wBACX;AAKK,MAAM,0BACX;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"popover.variants.d.ts","sourceRoot":"","sources":["../../../src/components/popover/popover.variants.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,mBAAmB,QAQ4B,CAAC;AAc7D,eAAO,MAAM,iBAAiB,QAOmE,CAAC;AAIlG,eAAO,MAAM,kBAAkB,qDACqB,CAAC;AAKrD,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAK7D,eAAO,MAAM,gBAAgB,kCAAkC,CAAC;AAShE,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAOvD,eAAO,MAAM,iBAAiB,QAOmE,CAAC;AAIlG,eAAO,MAAM,sBAAsB,0CAA0C,CAAC"}
1
+ {"version":3,"file":"popover.variants.d.ts","sourceRoot":"","sources":["../../../src/components/popover/popover.variants.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,mBAAmB,QAQ4B,CAAC;AAc7D,eAAO,MAAM,iBAAiB,QAOnB,CAAC;AAIZ,eAAO,MAAM,kBAAkB,qDACqB,CAAC;AAKrD,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAK7D,eAAO,MAAM,gBAAgB,kCAAkC,CAAC;AAShE,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAOvD,eAAO,MAAM,iBAAiB,QAOnB,CAAC;AAIZ,eAAO,MAAM,sBAAsB,0CAA0C,CAAC"}
@@ -1,10 +1,11 @@
1
- const popoverTriggerClass = "inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) text-label text-action-ghost-fg cursor-pointer select-none hover:bg-action-ghost-bg-hover transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 disabled:pointer-events-none disabled:text-text-disabled";
2
- const popoverPanelClass = "z-(--z-index-popover) flex flex-col gap-(--space-3) max-w-(--container-sm) p-(--space-4) bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) data-[state=open]:opacity-100 data-[state=closed]:opacity-0 outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2";
1
+ import { focusRing } from "../../lib/focus-ring";
2
+ const popoverTriggerClass = "inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) text-label text-action-ghost-fg cursor-pointer select-none hover:bg-action-ghost-bg-hover transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) " + focusRing + " disabled:pointer-events-none disabled:text-text-disabled";
3
+ const popoverPanelClass = "z-(--z-index-popover) flex flex-col gap-(--space-3) max-w-(--container-sm) p-(--space-4) bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) data-[state=open]:opacity-100 data-[state=closed]:opacity-0 " + focusRing;
3
4
  const popoverHeaderClass = "flex items-start justify-between gap-(--space-3)";
4
5
  const popoverTitleClass = "text-h3 text-text-primary";
5
6
  const popoverBodyClass = "text-body text-text-secondary";
6
7
  const popoverArrowClass = "fill-surface-raised";
7
- const popoverCloseClass = "inline-flex items-center justify-center rounded-(--radius-md) text-action-ghost-fg hover:bg-action-ghost-bg-hover transition-colors duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) min-w-(--size-target-mobile) sm:min-h-(--size-target-desktop) sm:min-w-(--size-target-desktop) outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2";
8
+ const popoverCloseClass = "inline-flex items-center justify-center rounded-(--radius-md) text-action-ghost-fg hover:bg-action-ghost-bg-hover transition-colors duration-(--motion-duration-fast) ease-(--motion-easing-verdify) motion-reduce:duration-(--motion-duration-instant) min-h-(--size-target-mobile) min-w-(--size-target-mobile) sm:min-h-(--size-target-desktop) sm:min-w-(--size-target-desktop) " + focusRing;
8
9
  const popoverCloseGlyphClass = "h-(--size-icon-md) w-(--size-icon-md)";
9
10
  export {
10
11
  popoverArrowClass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/popover/popover.variants.ts"],"sourcesContent":["// Popover is a small NON-MODAL surface a trigger opens next to itself to hold secondary content —\n// a short form, a definition, a few related controls (spec §1). It is a NEUTRAL surface (spec §3):\n// the panel, the arrow, and the border are neutral, and the brand violet NEVER tints the panel to\n// look \"premium.\" A status color NEVER paints the panel — a verified result is reported by a\n// VerifiedBadge placed INSIDE the body, not by coloring the popover. So NOTHING in this file binds a\n// --color-status-* token or the brand action-primary tier; color enters only through the components\n// the caller places inside it (brand != state, G-U2). This is the ONLY token-binding site (skill §5\n// hard rule). All open/close motion is the FAST token transition on the verdify easing, instant\n// under reduced motion — never the 350ms VerifiedBadge-only theatre duration (G-U3).\n\n// The trigger: the one stop in the page tab order for this control (spec §2 trigger, §4 Focus). A\n// NEUTRAL ghost surface — the label/glyph in the ghost action fg at rest (spec §5\n// --color-action-ghost-fg), the restrained ghost hover fill (spec §5 --color-action-ghost-bg-hover),\n// the md radius, the persistent 2px focus ring (never removed, spec §4 Focus), and the target-size\n// floor (44px touch / 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor.\n// A disabled trigger dims via the disabled TOKEN (DEC-C), never a blanket opacity. fast functional\n// hover motion + verdify easing, instant under reduced motion (G-U3). This styles the DEFAULT\n// (non-asChild) trigger; when a Button is passed via `asChild` it carries its own treatment.\nexport const popoverTriggerClass =\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) \" +\n \"text-label text-action-ghost-fg cursor-pointer select-none \" +\n \"hover:bg-action-ghost-bg-hover \" +\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) \" +\n \"outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 \" +\n \"disabled:pointer-events-none disabled:text-text-disabled\";\n\n// The panel (spec §2 panel, §5): the floating surface that opens on activation, raised above the\n// page and anchored to the trigger; it repositions to stay in the viewport (Radix). A NEUTRAL raised\n// surface (spec §5 --color-surface-raised) with the outer surface border (spec §5\n// --color-surface-border), the md corner radius (spec §5 --radius-md), and the md elevation shadow\n// above the page (spec §5 --shadow-md), on the POPOVER z-layer (a popover is a non-modal popover\n// layer, NOT the modal layer — there is no scrim, the page behind stays live, spec §1/§7). It NEVER\n// wears a brand or status fill (spec §3/§8). Inset padding + content gaps from --space-*; a column so\n// the header, body, and footer stack. The open/close fade is a PLAIN fast transition + verdify\n// easing, instant under reduced motion — never the 350ms VerifiedBadge-only theatre (spec §4, G-U3).\n// Enter/exit ride Radix's data-state on the content (attribute-selector variants, not arbitrary\n// values). The panel is focusable (Radix sets tabIndex=-1) so focus can land on it when the body has\n// no focusable control; its ring is never removed (spec §7 focus management).\nexport const popoverPanelClass =\n \"z-(--z-index-popover) flex flex-col gap-(--space-3) \" +\n \"max-w-(--container-sm) p-(--space-4) \" +\n \"bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) \" +\n \"transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 \" +\n \"outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2\";\n\n// The header (spec §2 header): the top region holding a short title and an optional close control on\n// the inline-end. Logical-property layout (G-U6) so it mirrors under RTL.\nexport const popoverHeaderClass =\n \"flex items-start justify-between gap-(--space-3)\";\n\n// The title (spec §2 header title, §5): names the panel as a statement, sentence case. The h3 type\n// role in the PRIMARY text color (spec §5 --text-h3 / --color-text-primary). When present, it is the\n// panel's accessible name, wired via aria-labelledby (the panel takes role=\"dialog\", spec §7).\nexport const popoverTitleClass = \"text-h3 text-text-primary\";\n\n// The body (spec §2 body, §5): the content region — text, a short form, or a few controls. The body\n// type role; supporting text in the SECONDARY text color (spec §5 --text-body / --color-text-secondary).\n// Content that needs to scroll usually belongs in a Sheet (spec §2), so the body does not own a scroll.\nexport const popoverBodyClass = \"text-body text-text-secondary\";\n\n// The arrow (spec §2 arrow): a small DECORATIVE pointer joining the panel to its trigger, carrying no\n// meaning of its own (Radix renders it inside an aria-hidden wrapper). It is filled with the SAME\n// neutral raised surface as the panel so it reads as part of the surface, never a brand or status\n// fill (spec §3/§5). `fill-*` is the SVG fill utility for the Radix arrow polygon. (Radix's bare\n// arrow does not carry the panel's outer BORDER edge — a conformant, non-load-bearing deviation from\n// the §5 \"arrow edge\" border, flagged for amendment rather than hand-rolling a bordered polygon, the\n// same deviation the Tooltip arrow pins.)\nexport const popoverArrowClass = \"fill-surface-raised\";\n\n// The close control (spec §2 close, §5): the in-panel dismiss button. A NEUTRAL ghost surface — the\n// glyph in --color-action-ghost-fg at rest, the restrained ghost hover fill (spec §5 ghost-fg /\n// ghost-bg-hover), the md radius, the persistent focus ring, the target-size floor (44px touch /\n// 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor, never fixed below it.\n// fast functional hover motion + verdify easing, instant under reduced motion (G-U3).\nexport const popoverCloseClass =\n \"inline-flex items-center justify-center rounded-(--radius-md) \" +\n \"text-action-ghost-fg hover:bg-action-ghost-bg-hover \" +\n \"transition-colors duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) min-w-(--size-target-mobile) \" +\n \"sm:min-h-(--size-target-desktop) sm:min-w-(--size-target-desktop) \" +\n \"outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2\";\n\n// The close glyph: a neutral X, --size-icon-md, drawn with currentColor so it inherits the close\n// button's ghost-fg. Decorative (aria-hidden) — the button carries the accessible name (spec §7).\nexport const popoverCloseGlyphClass = \"h-(--size-icon-md) w-(--size-icon-md)\";\n"],"mappings":"AAkBO,MAAM,sBACX;AAqBK,MAAM,oBACX;AAUK,MAAM,qBACX;AAKK,MAAM,oBAAoB;AAK1B,MAAM,mBAAmB;AASzB,MAAM,oBAAoB;AAO1B,MAAM,oBACX;AAUK,MAAM,yBAAyB;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/popover/popover.variants.ts"],"sourcesContent":["import { focusRing } from \"../../lib/focus-ring\";\n// Popover is a small NON-MODAL surface a trigger opens next to itself to hold secondary content —\n// a short form, a definition, a few related controls (spec §1). It is a NEUTRAL surface (spec §3):\n// the panel, the arrow, and the border are neutral, and the brand violet NEVER tints the panel to\n// look \"premium.\" A status color NEVER paints the panel — a verified result is reported by a\n// VerifiedBadge placed INSIDE the body, not by coloring the popover. So NOTHING in this file binds a\n// --color-status-* token or the brand action-primary tier; color enters only through the components\n// the caller places inside it (brand != state, G-U2). This is the ONLY token-binding site (skill §5\n// hard rule). All open/close motion is the FAST token transition on the verdify easing, instant\n// under reduced motion — never the 350ms VerifiedBadge-only theatre duration (G-U3).\n\n// The trigger: the one stop in the page tab order for this control (spec §2 trigger, §4 Focus). A\n// NEUTRAL ghost surface — the label/glyph in the ghost action fg at rest (spec §5\n// --color-action-ghost-fg), the restrained ghost hover fill (spec §5 --color-action-ghost-bg-hover),\n// the md radius, the persistent 2px focus ring (never removed, spec §4 Focus), and the target-size\n// floor (44px touch / 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor.\n// A disabled trigger dims via the disabled TOKEN (DEC-C), never a blanket opacity. fast functional\n// hover motion + verdify easing, instant under reduced motion (G-U3). This styles the DEFAULT\n// (non-asChild) trigger; when a Button is passed via `asChild` it carries its own treatment.\nexport const popoverTriggerClass =\n \"inline-flex items-center justify-center gap-(--space-2) rounded-(--radius-md) px-(--space-3) \" +\n \"text-label text-action-ghost-fg cursor-pointer select-none \" +\n \"hover:bg-action-ghost-bg-hover \" +\n \"transition-[color,background-color] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop) \" +\n focusRing + \" \" +\n \"disabled:pointer-events-none disabled:text-text-disabled\";\n\n// The panel (spec §2 panel, §5): the floating surface that opens on activation, raised above the\n// page and anchored to the trigger; it repositions to stay in the viewport (Radix). A NEUTRAL raised\n// surface (spec §5 --color-surface-raised) with the outer surface border (spec §5\n// --color-surface-border), the md corner radius (spec §5 --radius-md), and the md elevation shadow\n// above the page (spec §5 --shadow-md), on the POPOVER z-layer (a popover is a non-modal popover\n// layer, NOT the modal layer — there is no scrim, the page behind stays live, spec §1/§7). It NEVER\n// wears a brand or status fill (spec §3/§8). Inset padding + content gaps from --space-*; a column so\n// the header, body, and footer stack. The open/close fade is a PLAIN fast transition + verdify\n// easing, instant under reduced motion — never the 350ms VerifiedBadge-only theatre (spec §4, G-U3).\n// Enter/exit ride Radix's data-state on the content (attribute-selector variants, not arbitrary\n// values). The panel is focusable (Radix sets tabIndex=-1) so focus can land on it when the body has\n// no focusable control; its ring is never removed (spec §7 focus management).\nexport const popoverPanelClass =\n \"z-(--z-index-popover) flex flex-col gap-(--space-3) \" +\n \"max-w-(--container-sm) p-(--space-4) \" +\n \"bg-surface-raised border border-surface-border rounded-(--radius-md) shadow-(--shadow-md) \" +\n \"transition-opacity duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 \" +\n focusRing;\n\n// The header (spec §2 header): the top region holding a short title and an optional close control on\n// the inline-end. Logical-property layout (G-U6) so it mirrors under RTL.\nexport const popoverHeaderClass =\n \"flex items-start justify-between gap-(--space-3)\";\n\n// The title (spec §2 header title, §5): names the panel as a statement, sentence case. The h3 type\n// role in the PRIMARY text color (spec §5 --text-h3 / --color-text-primary). When present, it is the\n// panel's accessible name, wired via aria-labelledby (the panel takes role=\"dialog\", spec §7).\nexport const popoverTitleClass = \"text-h3 text-text-primary\";\n\n// The body (spec §2 body, §5): the content region — text, a short form, or a few controls. The body\n// type role; supporting text in the SECONDARY text color (spec §5 --text-body / --color-text-secondary).\n// Content that needs to scroll usually belongs in a Sheet (spec §2), so the body does not own a scroll.\nexport const popoverBodyClass = \"text-body text-text-secondary\";\n\n// The arrow (spec §2 arrow): a small DECORATIVE pointer joining the panel to its trigger, carrying no\n// meaning of its own (Radix renders it inside an aria-hidden wrapper). It is filled with the SAME\n// neutral raised surface as the panel so it reads as part of the surface, never a brand or status\n// fill (spec §3/§5). `fill-*` is the SVG fill utility for the Radix arrow polygon. (Radix's bare\n// arrow does not carry the panel's outer BORDER edge — a conformant, non-load-bearing deviation from\n// the §5 \"arrow edge\" border, flagged for amendment rather than hand-rolling a bordered polygon, the\n// same deviation the Tooltip arrow pins.)\nexport const popoverArrowClass = \"fill-surface-raised\";\n\n// The close control (spec §2 close, §5): the in-panel dismiss button. A NEUTRAL ghost surface — the\n// glyph in --color-action-ghost-fg at rest, the restrained ghost hover fill (spec §5 ghost-fg /\n// ghost-bg-hover), the md radius, the persistent focus ring, the target-size floor (44px touch /\n// 40px pointer, spec §7 2.5.8 / DEC-B) with the height EMERGING from the floor, never fixed below it.\n// fast functional hover motion + verdify easing, instant under reduced motion (G-U3).\nexport const popoverCloseClass =\n \"inline-flex items-center justify-center rounded-(--radius-md) \" +\n \"text-action-ghost-fg hover:bg-action-ghost-bg-hover \" +\n \"transition-colors duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant) \" +\n \"min-h-(--size-target-mobile) min-w-(--size-target-mobile) \" +\n \"sm:min-h-(--size-target-desktop) sm:min-w-(--size-target-desktop) \" +\n focusRing;\n\n// The close glyph: a neutral X, --size-icon-md, drawn with currentColor so it inherits the close\n// button's ghost-fg. Decorative (aria-hidden) — the button carries the accessible name (spec §7).\nexport const popoverCloseGlyphClass = \"h-(--size-icon-md) w-(--size-icon-md)\";\n"],"mappings":"AAAA,SAAS,iBAAiB;AAmBnB,MAAM,sBACX,kZAMA,YAAY;AAeP,MAAM,oBACX,2XAMA;AAIK,MAAM,qBACX;AAKK,MAAM,oBAAoB;AAK1B,MAAM,mBAAmB;AASzB,MAAM,oBAAoB;AAO1B,MAAM,oBACX,yXAMA;AAIK,MAAM,yBAAyB;","names":[]}
@@ -9,6 +9,6 @@ export declare const progressHeaderClass = "flex items-baseline justify-between
9
9
  export declare const progressLabelClass = "text-caption text-text-primary";
10
10
  export declare const progressValueTextClass = "text-caption text-text-secondary";
11
11
  export declare const progressDescriptionClass = "text-caption text-text-secondary";
12
- export declare const progressErrorClass = "text-caption text-status-critical-fg";
12
+ export declare const progressErrorClass = "text-caption text-status-critical-on-surface";
13
13
  export type ProgressFillVariantProps = VariantProps<typeof progressFillVariants>;
14
14
  //# sourceMappingURL=progress.variants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"progress.variants.d.ts","sourceRoot":"","sources":["../../../src/components/progress/progress.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAmBlE,eAAO,MAAM,kBAAkB,QAGe,CAAC;AAU/C,eAAO,MAAM,+BAA+B,kCAAkC,CAAC;AAmB/E,eAAO,MAAM,oBAAoB;;8EAqB/B,CAAC;AAKH,eAAO,MAAM,iBAAiB,yCAAyC,CAAC;AAIxE,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAIzF,eAAO,MAAM,kBAAkB,mCAAmC,CAAC;AAInE,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AAIzE,eAAO,MAAM,wBAAwB,qCAAqC,CAAC;AAM3E,eAAO,MAAM,kBAAkB,yCAAyC,CAAC;AAEzE,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"progress.variants.d.ts","sourceRoot":"","sources":["../../../src/components/progress/progress.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAmBlE,eAAO,MAAM,kBAAkB,QAGe,CAAC;AAU/C,eAAO,MAAM,+BAA+B,kCAAkC,CAAC;AAmB/E,eAAO,MAAM,oBAAoB;;8EAqB/B,CAAC;AAKH,eAAO,MAAM,iBAAiB,yCAAyC,CAAC;AAIxE,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAIzF,eAAO,MAAM,kBAAkB,mCAAmC,CAAC;AAInE,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AAIzE,eAAO,MAAM,wBAAwB,qCAAqC,CAAC;AAM3E,eAAO,MAAM,kBAAkB,iDAAiD,CAAC;AAEjF,MAAM,MAAM,wBAAwB,GAAG,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -22,7 +22,7 @@ const progressHeaderClass = "flex items-baseline justify-between gap-(--space-2)
22
22
  const progressLabelClass = "text-caption text-text-primary";
23
23
  const progressValueTextClass = "text-caption text-text-secondary";
24
24
  const progressDescriptionClass = "text-caption text-text-secondary";
25
- const progressErrorClass = "text-caption text-status-critical-fg";
25
+ const progressErrorClass = "text-caption text-status-critical-on-surface";
26
26
  export {
27
27
  PROGRESS_INDETERMINATE_KEYFRAME,
28
28
  progressDescriptionClass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/progress/progress.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\n\n// Progress reports how far a task has advanced — a status OUTPUT, not a control\n// (spec §1/§4/§6). It takes no focus, binds no keys, and renders no focus ring or\n// target-size floor: the value reaches assistive technology through role=\"progressbar\"\n// + aria-value* and a polite live region, never motion (spec §7).\n//\n// Brand != state (spec §3/§5/§8): the fill is a plain control affordance, NOT a\n// verification result. Neutrals carry the track and the fill takes the primary ACTION\n// accent (the visible action color on the surface) — NEVER --color-status-verified-*,\n// because verified green is the in-product verified status and is never a generic\n// progress affordance. There is no \"verified\" Progress; surfacing a verified outcome is\n// the job of VerifiedBadge, whose deliberate check animation is never borrowed here.\n\n// The track (spec §2/§5): the full-length rail that holds the fill, in the neutral\n// control surface with the control border, clipped so the fill's rounded end and the\n// traveling indeterminate indicator never bleed past the rail. Rounded on the full radius.\n// In the error state the rail edge takes the critical status border, driven by\n// aria-invalid on the bar so the failure is identified BY THE FIELD, in text (spec §4/§7).\nexport const progressTrackClass =\n \"relative block h-(--space-2) w-full overflow-hidden \" +\n \"rounded-(--radius-full) bg-control-bg border border-control-border \" +\n \"aria-invalid:border-status-critical-border\";\n\n// The fill / indeterminate indicator keyframe (spec §3/§4): a TRAVELLING indicator. A\n// narrow indicator (`w-2/5`, set in the variant below) slides along the track's INLINE\n// axis from before the inline-start edge to past the inline-end edge, then loops. Travel\n// is driven on the LOGICAL `inset-inline-start` property (not a physical `translateX`), so\n// the indicator mirrors automatically under `dir=\"rtl\"` (G-U6 global-first). This keyframe\n// is pure geometry — it binds NO design token (the duration and easing tokens are bound on\n// the variant className, the single §5 binding site), so it is emitted from `progress.tsx`\n// as a component-scoped <style>, not from this binding file.\nexport const PROGRESS_INDETERMINATE_KEYFRAME = \"progress-indeterminate-travel\";\n\n// The fill (spec §2/§4/§5): the portion of the track that is done, in the primary action\n// accent, rounded on the full radius.\n//\n// DETERMINATE: its inline length is the value (set as an inline width in tsx — a data\n// percentage, not a design token). The length CHANGES on the FAST duration with verdify\n// easing, collapsing to the instant endpoint under reduced motion (spec §4/§5).\n//\n// INDETERMINATE: no measured length exists, so a moving indicator TRAVELS the track on a\n// continuous AMBIENT loop with verdify easing — restrained activity, never the 350ms\n// verified-check theatre duration (spec §3/§4/§8). The indicator is positioned `absolute`\n// on the track and its `inset-inline-start` is animated by the `progress-indeterminate-travel`\n// keyframe (defined in progress.tsx). The loop length is driven onto the ambient token via\n// the arbitrary `animation-duration` PROPERTY, the easing onto the verdify token, and the\n// loop is set to repeat — all keyword arbitrary properties or paren-shorthand tokens, not\n// raw values, so the token-binding gate does not flag them. Under prefers-reduced-motion the\n// travel is suppressed (`motion-reduce:animate-none`) and the busy state is carried by the\n// live region + name, not motion alone (spec §4/§7).\nexport const progressFillVariants = cva(\"block h-full rounded-(--radius-full) bg-action-primary-bg\", {\n variants: {\n // STRUCTURAL axis = spec §3: the determinate fill is a measured length; the\n // indeterminate indicator is a looping travel. Neither recolors the fill (brand !=\n // state) — they differ only by motion and how the length is set.\n indeterminate: {\n // a measured length set inline; the length transition runs on the fast duration\n false:\n \"transition-[inline-size] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // a narrow indicator that TRAVELS the inline axis on a continuous ambient loop;\n // static under reduced motion. Absolutely positioned so it slides past both edges\n // of the clipped track (overflow-hidden on the track hides the off-track portion).\n true:\n \"absolute w-2/5 [animation-name:progress-indeterminate-travel] \" +\n \"[animation-iteration-count:infinite] \" +\n \"[animation-duration:var(--motion-duration-ambient)] \" +\n \"ease-(--motion-easing-verdify) motion-reduce:animate-none\",\n },\n },\n defaultVariants: { indeterminate: false },\n});\n\n// The bar root (spec §2): a layout column stacking the label/value-text row, the track,\n// and the optional description/error message at the --space-2 gap. Non-interactive: no\n// focus ring, no tab stop, no target-size floor (spec §4/§6/§7).\nexport const progressRootClass = \"flex w-full flex-col gap-(--space-2)\";\n\n// The label + value-text header row (spec §2): the label sits inline-start, the optional\n// value-text inline-end, spread across the bar's width.\nexport const progressHeaderClass = \"flex items-baseline justify-between gap-(--space-2)\";\n\n// The label (spec §2/§5): the text naming the task, in the primary text color at the\n// caption type role.\nexport const progressLabelClass = \"text-caption text-text-primary\";\n\n// The optional value-text readout (spec §2/§5): a plain progress readout (\"40%\",\n// \"Step 2 of 4\") in the secondary text color at the caption role. Determinate only.\nexport const progressValueTextClass = \"text-caption text-text-secondary\";\n\n// The optional one-line description (spec §2/§5): a statement clarifying what is running,\n// in the secondary text color at the caption role.\nexport const progressDescriptionClass = \"text-caption text-text-secondary\";\n\n// The error message (spec §4/§5/§8): states what failed and the next step, naming the\n// failure without blaming the reader. The critical status FOREGROUND marks the text at the\n// caption role; the field edge (track) takes the critical border via aria-invalid. Error\n// is shown in TEXT, never by color alone.\nexport const progressErrorClass = \"text-caption text-status-critical-fg\";\n\nexport type ProgressFillVariantProps = VariantProps<typeof progressFillVariants>;\n"],"mappings":"AAAA,SAAS,WAA8B;AAmBhC,MAAM,qBACX;AAYK,MAAM,kCAAkC;AAmBxC,MAAM,uBAAuB,IAAI,6DAA6D;AAAA,EACnG,UAAU;AAAA;AAAA;AAAA;AAAA,IAIR,eAAe;AAAA;AAAA,MAEb,OACE;AAAA;AAAA;AAAA;AAAA,MAKF,MACE;AAAA,IAIJ;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,eAAe,MAAM;AAC1C,CAAC;AAKM,MAAM,oBAAoB;AAI1B,MAAM,sBAAsB;AAI5B,MAAM,qBAAqB;AAI3B,MAAM,yBAAyB;AAI/B,MAAM,2BAA2B;AAMjC,MAAM,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/progress/progress.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\n\n// Progress reports how far a task has advanced — a status OUTPUT, not a control\n// (spec §1/§4/§6). It takes no focus, binds no keys, and renders no focus ring or\n// target-size floor: the value reaches assistive technology through role=\"progressbar\"\n// + aria-value* and a polite live region, never motion (spec §7).\n//\n// Brand != state (spec §3/§5/§8): the fill is a plain control affordance, NOT a\n// verification result. Neutrals carry the track and the fill takes the primary ACTION\n// accent (the visible action color on the surface) — NEVER --color-status-verified-*,\n// because verified green is the in-product verified status and is never a generic\n// progress affordance. There is no \"verified\" Progress; surfacing a verified outcome is\n// the job of VerifiedBadge, whose deliberate check animation is never borrowed here.\n\n// The track (spec §2/§5): the full-length rail that holds the fill, in the neutral\n// control surface with the control border, clipped so the fill's rounded end and the\n// traveling indeterminate indicator never bleed past the rail. Rounded on the full radius.\n// In the error state the rail edge takes the critical status border, driven by\n// aria-invalid on the bar so the failure is identified BY THE FIELD, in text (spec §4/§7).\nexport const progressTrackClass =\n \"relative block h-(--space-2) w-full overflow-hidden \" +\n \"rounded-(--radius-full) bg-control-bg border border-control-border \" +\n \"aria-invalid:border-status-critical-border\";\n\n// The fill / indeterminate indicator keyframe (spec §3/§4): a TRAVELLING indicator. A\n// narrow indicator (`w-2/5`, set in the variant below) slides along the track's INLINE\n// axis from before the inline-start edge to past the inline-end edge, then loops. Travel\n// is driven on the LOGICAL `inset-inline-start` property (not a physical `translateX`), so\n// the indicator mirrors automatically under `dir=\"rtl\"` (G-U6 global-first). This keyframe\n// is pure geometry — it binds NO design token (the duration and easing tokens are bound on\n// the variant className, the single §5 binding site), so it is emitted from `progress.tsx`\n// as a component-scoped <style>, not from this binding file.\nexport const PROGRESS_INDETERMINATE_KEYFRAME = \"progress-indeterminate-travel\";\n\n// The fill (spec §2/§4/§5): the portion of the track that is done, in the primary action\n// accent, rounded on the full radius.\n//\n// DETERMINATE: its inline length is the value (set as an inline width in tsx — a data\n// percentage, not a design token). The length CHANGES on the FAST duration with verdify\n// easing, collapsing to the instant endpoint under reduced motion (spec §4/§5).\n//\n// INDETERMINATE: no measured length exists, so a moving indicator TRAVELS the track on a\n// continuous AMBIENT loop with verdify easing — restrained activity, never the 350ms\n// verified-check theatre duration (spec §3/§4/§8). The indicator is positioned `absolute`\n// on the track and its `inset-inline-start` is animated by the `progress-indeterminate-travel`\n// keyframe (defined in progress.tsx). The loop length is driven onto the ambient token via\n// the arbitrary `animation-duration` PROPERTY, the easing onto the verdify token, and the\n// loop is set to repeat — all keyword arbitrary properties or paren-shorthand tokens, not\n// raw values, so the token-binding gate does not flag them. Under prefers-reduced-motion the\n// travel is suppressed (`motion-reduce:animate-none`) and the busy state is carried by the\n// live region + name, not motion alone (spec §4/§7).\nexport const progressFillVariants = cva(\"block h-full rounded-(--radius-full) bg-action-primary-bg\", {\n variants: {\n // STRUCTURAL axis = spec §3: the determinate fill is a measured length; the\n // indeterminate indicator is a looping travel. Neither recolors the fill (brand !=\n // state) — they differ only by motion and how the length is set.\n indeterminate: {\n // a measured length set inline; the length transition runs on the fast duration\n false:\n \"transition-[inline-size] duration-(--motion-duration-fast) ease-(--motion-easing-verdify) \" +\n \"motion-reduce:duration-(--motion-duration-instant)\",\n // a narrow indicator that TRAVELS the inline axis on a continuous ambient loop;\n // static under reduced motion. Absolutely positioned so it slides past both edges\n // of the clipped track (overflow-hidden on the track hides the off-track portion).\n true:\n \"absolute w-2/5 [animation-name:progress-indeterminate-travel] \" +\n \"[animation-iteration-count:infinite] \" +\n \"[animation-duration:var(--motion-duration-ambient)] \" +\n \"ease-(--motion-easing-verdify) motion-reduce:animate-none\",\n },\n },\n defaultVariants: { indeterminate: false },\n});\n\n// The bar root (spec §2): a layout column stacking the label/value-text row, the track,\n// and the optional description/error message at the --space-2 gap. Non-interactive: no\n// focus ring, no tab stop, no target-size floor (spec §4/§6/§7).\nexport const progressRootClass = \"flex w-full flex-col gap-(--space-2)\";\n\n// The label + value-text header row (spec §2): the label sits inline-start, the optional\n// value-text inline-end, spread across the bar's width.\nexport const progressHeaderClass = \"flex items-baseline justify-between gap-(--space-2)\";\n\n// The label (spec §2/§5): the text naming the task, in the primary text color at the\n// caption type role.\nexport const progressLabelClass = \"text-caption text-text-primary\";\n\n// The optional value-text readout (spec §2/§5): a plain progress readout (\"40%\",\n// \"Step 2 of 4\") in the secondary text color at the caption role. Determinate only.\nexport const progressValueTextClass = \"text-caption text-text-secondary\";\n\n// The optional one-line description (spec §2/§5): a statement clarifying what is running,\n// in the secondary text color at the caption role.\nexport const progressDescriptionClass = \"text-caption text-text-secondary\";\n\n// The error message (spec §4/§5/§8): states what failed and the next step, naming the\n// failure without blaming the reader. The critical status FOREGROUND marks the text at the\n// caption role; the field edge (track) takes the critical border via aria-invalid. Error\n// is shown in TEXT, never by color alone.\nexport const progressErrorClass = \"text-caption text-status-critical-on-surface\";\n\nexport type ProgressFillVariantProps = VariantProps<typeof progressFillVariants>;\n"],"mappings":"AAAA,SAAS,WAA8B;AAmBhC,MAAM,qBACX;AAYK,MAAM,kCAAkC;AAmBxC,MAAM,uBAAuB,IAAI,6DAA6D;AAAA,EACnG,UAAU;AAAA;AAAA;AAAA;AAAA,IAIR,eAAe;AAAA;AAAA,MAEb,OACE;AAAA;AAAA;AAAA;AAAA,MAKF,MACE;AAAA,IAIJ;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,eAAe,MAAM;AAC1C,CAAC;AAKM,MAAM,oBAAoB;AAI1B,MAAM,sBAAsB;AAI5B,MAAM,qBAAqB;AAI3B,MAAM,yBAAyB;AAI/B,MAAM,2BAA2B;AAMjC,MAAM,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../../../src/components/radio/radio.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAQL,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AAoB1B,MAAM,WAAW,eACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,EAC5D,sBAAsB;IACxB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,eAAO,MAAM,UAAU,wFAuFtB,CAAC;AAEF,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpF,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC/B;AAED,eAAO,MAAM,KAAK,qFAuGjB,CAAC"}
1
+ {"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../../../src/components/radio/radio.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAQL,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AAoB1B,MAAM,WAAW,eACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,EAC5D,sBAAsB;IACxB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,eAAO,MAAM,UAAU,wFAuFtB,CAAC;AAEF,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpF,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC/B;AAED,eAAO,MAAM,KAAK,qFAuGjB,CAAC"}
@@ -2,6 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../lib/cn";
5
+ import { focusRing } from "../../lib/focus-ring";
5
6
  import {
6
7
  radioControlVariants,
7
8
  radioDotVariants,
@@ -140,7 +141,7 @@ const Radio = React.forwardRef(
140
141
  onKeyDown,
141
142
  className: cn(
142
143
  "peer sr-only",
143
- "focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2"
144
+ focusRing
144
145
  ),
145
146
  ...props
146
147
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport {\n radioControlVariants,\n radioDotVariants,\n radioTargetVariants,\n radioLabelVariants,\n radioDescriptionVariants,\n radioCardVariants,\n radioGroupVariants,\n type RadioGroupVariantProps,\n} from \"./radio.variants\";\n\ninterface RadioGroupContextValue {\n name: string;\n value: string | undefined;\n variant: NonNullable<RadioGroupVariantProps[\"variant\"]>;\n select: (value: string) => void;\n register: (value: string, el: HTMLInputElement | null, disabled: boolean) => void;\n onArrow: (current: string, dir: 1 | -1) => void;\n rovingValue: string | undefined; // which option owns tabindex=0\n}\n\nconst RadioGroupContext = React.createContext<RadioGroupContextValue | null>(null);\n\nfunction useRadioGroup(): RadioGroupContextValue {\n const ctx = React.useContext(RadioGroupContext);\n if (!ctx) throw new Error(\"<Radio> must be used inside <RadioGroup>\");\n return ctx;\n}\n\nexport interface RadioGroupProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\">,\n RadioGroupVariantProps {\n /** Shared `name` for the native radios in this group. */\n name: string;\n /** The group label — the question the options answer. */\n label: React.ReactNode;\n /** Uncontrolled initial selection. */\n defaultValue?: string;\n /** Controlled selection. */\n value?: string;\n /** Fires with the newly selected value. */\n onValueChange?: (value: string) => void;\n}\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(\n function RadioGroup(\n { className, name, label, defaultValue, value: controlled, onValueChange,\n variant = \"default\", children, ...props },\n ref,\n ) {\n const labelId = React.useId();\n const [uncontrolled, setUncontrolled] = React.useState(defaultValue);\n const value = controlled ?? uncontrolled;\n // insertion-ordered registry of options: value → { el, disabled }\n const items = React.useRef<Map<string, { el: HTMLInputElement | null; disabled: boolean }>>(\n new Map(),\n );\n\n const select = React.useCallback(\n (next: string) => {\n if (controlled === undefined) setUncontrolled(next);\n onValueChange?.(next);\n },\n [controlled, onValueChange],\n );\n\n const register = React.useCallback(\n (v: string, el: HTMLInputElement | null, disabled: boolean) => {\n if (el) items.current.set(v, { el, disabled });\n else items.current.delete(v);\n },\n [],\n );\n\n // Arrow navigation: move to next/prev ENABLED option, wrapping, then select + focus.\n // Focus is a post-commit concern, so it legitimately reads the ref Map for the\n // live DOM node; only the render-time roving fallback (below) avoids that Map.\n const onArrow = React.useCallback(\n (current: string, dir: 1 | -1) => {\n const entries = [...items.current.entries()];\n const enabled = entries.filter(([, m]) => !m.disabled);\n if (enabled.length === 0) return;\n const idx = enabled.findIndex(([v]) => v === current);\n const nextIdx = (idx + dir + enabled.length) % enabled.length;\n const [nextValue, nextMeta] = enabled[nextIdx];\n select(nextValue);\n nextMeta.el?.focus();\n },\n [select],\n );\n\n // Roving tabindex owner: the selected option, else the first ENABLED option.\n // Computed from React.Children DURING RENDER, not from the post-commit `items`\n // ref Map — children register via useEffect AFTER the group commits, which would\n // not trigger a recompute, leaving no tab stop when nothing is selected. Reading\n // children at render time recomputes on every render and keeps the defaultValue\n // path working (a real `value` always wins).\n const firstEnabledValue = React.useMemo(() => {\n let first: string | undefined;\n React.Children.forEach(children, (child) => {\n if (first !== undefined) return;\n if (!React.isValidElement<RadioProps>(child)) return;\n if (child.props.disabled) return;\n first = child.props.value;\n });\n return first;\n }, [children]);\n const rovingValue = value ?? firstEnabledValue;\n\n const ctx = React.useMemo<RadioGroupContextValue>(\n () => ({ name, value, variant: variant ?? \"default\", select, register, onArrow, rovingValue }),\n [name, value, variant, select, register, onArrow, rovingValue],\n );\n\n return (\n <RadioGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n aria-labelledby={labelId}\n className={cn(radioGroupVariants({ variant }), className)}\n {...props}\n >\n <span id={labelId} className=\"text-label text-text-primary\">\n {label}\n </span>\n {children}\n </div>\n </RadioGroupContext.Provider>\n );\n },\n);\n\nexport interface RadioProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"type\" | \"name\" | \"value\"> {\n /** This option's value within the group. */\n value: string;\n /** Secondary text under the option label (With-description variant). */\n description?: React.ReactNode;\n}\n\nexport const Radio = React.forwardRef<HTMLInputElement, RadioProps>(\n function Radio({ className, value, description, disabled = false, children, ...props }, ref) {\n const ctx = useRadioGroup();\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const descId = React.useId();\n const checked = ctx.value === value;\n const tabbable = ctx.rovingValue === value;\n\n // Register this option with the group so roving + arrow nav can see it.\n React.useEffect(() => {\n ctx.register(value, inputRef.current, disabled);\n return () => ctx.register(value, null, disabled);\n }, [ctx, value, disabled]);\n\n const setRefs = (el: HTMLInputElement | null) => {\n inputRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = el;\n };\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\") {\n e.preventDefault();\n ctx.onArrow(value, 1);\n } else if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\") {\n e.preventDefault();\n ctx.onArrow(value, -1);\n } else if (e.key === \" \") {\n e.preventDefault();\n if (!checked) ctx.select(value);\n }\n };\n\n // The naming <label> wraps ONLY the input, the control, and the option-label\n // text — never the description. A native <label> contributes its text content to\n // the input's accessible name, so a nested description would pollute the name\n // (spec §7: the name comes from the associated label). The description sits\n // OUTSIDE the label and is linked via aria-describedby (mirrors Checkbox/Select).\n const row = (\n <label className=\"flex items-start gap-2\">\n {/* native radio is the peer; visually hidden but in the a11y tree */}\n <input\n ref={setRefs}\n type=\"radio\"\n name={ctx.name}\n value={value}\n checked={checked}\n disabled={disabled}\n aria-checked={checked}\n aria-disabled={disabled || undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={tabbable ? 0 : -1}\n onChange={() => ctx.select(value)}\n onKeyDown={onKeyDown}\n className={cn(\n \"peer sr-only\",\n \"focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2\",\n )}\n {...props}\n />\n <span\n aria-hidden=\"true\"\n data-testid={`radio-control-${value}`}\n className={cn(radioControlVariants())}\n >\n {/* the dot is a child of the control, not a sibling of the peer input,\n so its visibility is driven by the explicit `selected` variant. */}\n <span\n data-testid={`radio-dot-${value}`}\n className={cn(radioDotVariants({ selected: checked, disabled }))}\n />\n </span>\n {/* disabled colour comes from the explicit cva variant (the label-text span\n is not a sibling of the peer input, so peer-disabled cannot reach it). */}\n <span className={cn(radioLabelVariants({ disabled }))}>{children}</span>\n </label>\n );\n\n // The option container the test queries: it carries the target-size floor once\n // and stacks the naming row above an optional description.\n const option = (\n <div\n data-testid={`radio-target-${value}`}\n className={cn(radioTargetVariants(), \"flex-col\", className)}\n >\n {row}\n {description ? (\n <span id={descId} className={cn(radioDescriptionVariants())}>\n {description}\n </span>\n ) : null}\n </div>\n );\n\n return ctx.variant === \"card\" ? (\n <span data-testid={`radio-card-${value}`} className={cn(radioCardVariants())}>\n {option}\n </span>\n ) : (\n option\n );\n },\n);\n"],"mappings":"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","names":["RadioGroup","Radio"]}
1
+ {"version":3,"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { focusRing } from \"../../lib/focus-ring\";\nimport {\n radioControlVariants,\n radioDotVariants,\n radioTargetVariants,\n radioLabelVariants,\n radioDescriptionVariants,\n radioCardVariants,\n radioGroupVariants,\n type RadioGroupVariantProps,\n} from \"./radio.variants\";\n\ninterface RadioGroupContextValue {\n name: string;\n value: string | undefined;\n variant: NonNullable<RadioGroupVariantProps[\"variant\"]>;\n select: (value: string) => void;\n register: (value: string, el: HTMLInputElement | null, disabled: boolean) => void;\n onArrow: (current: string, dir: 1 | -1) => void;\n rovingValue: string | undefined; // which option owns tabindex=0\n}\n\nconst RadioGroupContext = React.createContext<RadioGroupContextValue | null>(null);\n\nfunction useRadioGroup(): RadioGroupContextValue {\n const ctx = React.useContext(RadioGroupContext);\n if (!ctx) throw new Error(\"<Radio> must be used inside <RadioGroup>\");\n return ctx;\n}\n\nexport interface RadioGroupProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\">,\n RadioGroupVariantProps {\n /** Shared `name` for the native radios in this group. */\n name: string;\n /** The group label — the question the options answer. */\n label: React.ReactNode;\n /** Uncontrolled initial selection. */\n defaultValue?: string;\n /** Controlled selection. */\n value?: string;\n /** Fires with the newly selected value. */\n onValueChange?: (value: string) => void;\n}\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(\n function RadioGroup(\n { className, name, label, defaultValue, value: controlled, onValueChange,\n variant = \"default\", children, ...props },\n ref,\n ) {\n const labelId = React.useId();\n const [uncontrolled, setUncontrolled] = React.useState(defaultValue);\n const value = controlled ?? uncontrolled;\n // insertion-ordered registry of options: value → { el, disabled }\n const items = React.useRef<Map<string, { el: HTMLInputElement | null; disabled: boolean }>>(\n new Map(),\n );\n\n const select = React.useCallback(\n (next: string) => {\n if (controlled === undefined) setUncontrolled(next);\n onValueChange?.(next);\n },\n [controlled, onValueChange],\n );\n\n const register = React.useCallback(\n (v: string, el: HTMLInputElement | null, disabled: boolean) => {\n if (el) items.current.set(v, { el, disabled });\n else items.current.delete(v);\n },\n [],\n );\n\n // Arrow navigation: move to next/prev ENABLED option, wrapping, then select + focus.\n // Focus is a post-commit concern, so it legitimately reads the ref Map for the\n // live DOM node; only the render-time roving fallback (below) avoids that Map.\n const onArrow = React.useCallback(\n (current: string, dir: 1 | -1) => {\n const entries = [...items.current.entries()];\n const enabled = entries.filter(([, m]) => !m.disabled);\n if (enabled.length === 0) return;\n const idx = enabled.findIndex(([v]) => v === current);\n const nextIdx = (idx + dir + enabled.length) % enabled.length;\n const [nextValue, nextMeta] = enabled[nextIdx];\n select(nextValue);\n nextMeta.el?.focus();\n },\n [select],\n );\n\n // Roving tabindex owner: the selected option, else the first ENABLED option.\n // Computed from React.Children DURING RENDER, not from the post-commit `items`\n // ref Map — children register via useEffect AFTER the group commits, which would\n // not trigger a recompute, leaving no tab stop when nothing is selected. Reading\n // children at render time recomputes on every render and keeps the defaultValue\n // path working (a real `value` always wins).\n const firstEnabledValue = React.useMemo(() => {\n let first: string | undefined;\n React.Children.forEach(children, (child) => {\n if (first !== undefined) return;\n if (!React.isValidElement<RadioProps>(child)) return;\n if (child.props.disabled) return;\n first = child.props.value;\n });\n return first;\n }, [children]);\n const rovingValue = value ?? firstEnabledValue;\n\n const ctx = React.useMemo<RadioGroupContextValue>(\n () => ({ name, value, variant: variant ?? \"default\", select, register, onArrow, rovingValue }),\n [name, value, variant, select, register, onArrow, rovingValue],\n );\n\n return (\n <RadioGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n aria-labelledby={labelId}\n className={cn(radioGroupVariants({ variant }), className)}\n {...props}\n >\n <span id={labelId} className=\"text-label text-text-primary\">\n {label}\n </span>\n {children}\n </div>\n </RadioGroupContext.Provider>\n );\n },\n);\n\nexport interface RadioProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"type\" | \"name\" | \"value\"> {\n /** This option's value within the group. */\n value: string;\n /** Secondary text under the option label (With-description variant). */\n description?: React.ReactNode;\n}\n\nexport const Radio = React.forwardRef<HTMLInputElement, RadioProps>(\n function Radio({ className, value, description, disabled = false, children, ...props }, ref) {\n const ctx = useRadioGroup();\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const descId = React.useId();\n const checked = ctx.value === value;\n const tabbable = ctx.rovingValue === value;\n\n // Register this option with the group so roving + arrow nav can see it.\n React.useEffect(() => {\n ctx.register(value, inputRef.current, disabled);\n return () => ctx.register(value, null, disabled);\n }, [ctx, value, disabled]);\n\n const setRefs = (el: HTMLInputElement | null) => {\n inputRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = el;\n };\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\") {\n e.preventDefault();\n ctx.onArrow(value, 1);\n } else if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\") {\n e.preventDefault();\n ctx.onArrow(value, -1);\n } else if (e.key === \" \") {\n e.preventDefault();\n if (!checked) ctx.select(value);\n }\n };\n\n // The naming <label> wraps ONLY the input, the control, and the option-label\n // text — never the description. A native <label> contributes its text content to\n // the input's accessible name, so a nested description would pollute the name\n // (spec §7: the name comes from the associated label). The description sits\n // OUTSIDE the label and is linked via aria-describedby (mirrors Checkbox/Select).\n const row = (\n <label className=\"flex items-start gap-2\">\n {/* native radio is the peer; visually hidden but in the a11y tree */}\n <input\n ref={setRefs}\n type=\"radio\"\n name={ctx.name}\n value={value}\n checked={checked}\n disabled={disabled}\n aria-checked={checked}\n aria-disabled={disabled || undefined}\n aria-describedby={description ? descId : undefined}\n tabIndex={tabbable ? 0 : -1}\n onChange={() => ctx.select(value)}\n onKeyDown={onKeyDown}\n className={cn(\n \"peer sr-only\",\n focusRing,\n )}\n {...props}\n />\n <span\n aria-hidden=\"true\"\n data-testid={`radio-control-${value}`}\n className={cn(radioControlVariants())}\n >\n {/* the dot is a child of the control, not a sibling of the peer input,\n so its visibility is driven by the explicit `selected` variant. */}\n <span\n data-testid={`radio-dot-${value}`}\n className={cn(radioDotVariants({ selected: checked, disabled }))}\n />\n </span>\n {/* disabled colour comes from the explicit cva variant (the label-text span\n is not a sibling of the peer input, so peer-disabled cannot reach it). */}\n <span className={cn(radioLabelVariants({ disabled }))}>{children}</span>\n </label>\n );\n\n // The option container the test queries: it carries the target-size floor once\n // and stacks the naming row above an optional description.\n const option = (\n <div\n data-testid={`radio-target-${value}`}\n className={cn(radioTargetVariants(), \"flex-col\", className)}\n >\n {row}\n {description ? (\n <span id={descId} className={cn(radioDescriptionVariants())}>\n {description}\n </span>\n ) : null}\n </div>\n );\n\n return ctx.variant === \"card\" ? (\n <span data-testid={`radio-card-${value}`} className={cn(radioCardVariants())}>\n {option}\n </span>\n ) : (\n option\n );\n },\n);\n"],"mappings":";AAyHQ,SAOE,KAPF;AAvHR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAYP,MAAM,oBAAoB,MAAM,cAA6C,IAAI;AAEjF,SAAS,gBAAwC;AAC/C,QAAM,MAAM,MAAM,WAAW,iBAAiB;AAC9C,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,0CAA0C;AACpE,SAAO;AACT;AAiBO,MAAM,aAAa,MAAM;AAAA,EAC9B,SAASA,YACP;AAAA,IAAE;AAAA,IAAW;AAAA,IAAM;AAAA,IAAO;AAAA,IAAc,OAAO;AAAA,IAAY;AAAA,IACzD,UAAU;AAAA,IAAW;AAAA,IAAU,GAAG;AAAA,EAAM,GAC1C,KACA;AACA,UAAM,UAAU,MAAM,MAAM;AAC5B,UAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,YAAY;AACnE,UAAM,QAAQ,cAAc;AAE5B,UAAM,QAAQ,MAAM;AAAA,MAClB,oBAAI,IAAI;AAAA,IACV;AAEA,UAAM,SAAS,MAAM;AAAA,MACnB,CAAC,SAAiB;AAChB,YAAI,eAAe,OAAW,iBAAgB,IAAI;AAClD,wBAAgB,IAAI;AAAA,MACtB;AAAA,MACA,CAAC,YAAY,aAAa;AAAA,IAC5B;AAEA,UAAM,WAAW,MAAM;AAAA,MACrB,CAAC,GAAW,IAA6B,aAAsB;AAC7D,YAAI,GAAI,OAAM,QAAQ,IAAI,GAAG,EAAE,IAAI,SAAS,CAAC;AAAA,YACxC,OAAM,QAAQ,OAAO,CAAC;AAAA,MAC7B;AAAA,MACA,CAAC;AAAA,IACH;AAKA,UAAM,UAAU,MAAM;AAAA,MACpB,CAAC,SAAiB,QAAgB;AAChC,cAAM,UAAU,CAAC,GAAG,MAAM,QAAQ,QAAQ,CAAC;AAC3C,cAAM,UAAU,QAAQ,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ;AACrD,YAAI,QAAQ,WAAW,EAAG;AAC1B,cAAM,MAAM,QAAQ,UAAU,CAAC,CAAC,CAAC,MAAM,MAAM,OAAO;AACpD,cAAM,WAAW,MAAM,MAAM,QAAQ,UAAU,QAAQ;AACvD,cAAM,CAAC,WAAW,QAAQ,IAAI,QAAQ,OAAO;AAC7C,eAAO,SAAS;AAChB,iBAAS,IAAI,MAAM;AAAA,MACrB;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AAQA,UAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,UAAI;AACJ,YAAM,SAAS,QAAQ,UAAU,CAAC,UAAU;AAC1C,YAAI,UAAU,OAAW;AACzB,YAAI,CAAC,MAAM,eAA2B,KAAK,EAAG;AAC9C,YAAI,MAAM,MAAM,SAAU;AAC1B,gBAAQ,MAAM,MAAM;AAAA,MACtB,CAAC;AACD,aAAO;AAAA,IACT,GAAG,CAAC,QAAQ,CAAC;AACb,UAAM,cAAc,SAAS;AAE7B,UAAM,MAAM,MAAM;AAAA,MAChB,OAAO,EAAE,MAAM,OAAO,SAAS,WAAW,WAAW,QAAQ,UAAU,SAAS,YAAY;AAAA,MAC5F,CAAC,MAAM,OAAO,SAAS,QAAQ,UAAU,SAAS,WAAW;AAAA,IAC/D;AAEA,WACE,oBAAC,kBAAkB,UAAlB,EAA2B,OAAO,KACjC;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,mBAAiB;AAAA,QACjB,WAAW,GAAG,mBAAmB,EAAE,QAAQ,CAAC,GAAG,SAAS;AAAA,QACvD,GAAG;AAAA,QAEJ;AAAA,8BAAC,UAAK,IAAI,SAAS,WAAU,gCAC1B,iBACH;AAAA,UACC;AAAA;AAAA;AAAA,IACH,GACF;AAAA,EAEJ;AACF;AAUO,MAAM,QAAQ,MAAM;AAAA,EACzB,SAASC,OAAM,EAAE,WAAW,OAAO,aAAa,WAAW,OAAO,UAAU,GAAG,MAAM,GAAG,KAAK;AAC3F,UAAM,MAAM,cAAc;AAC1B,UAAM,WAAW,MAAM,OAAgC,IAAI;AAC3D,UAAM,SAAS,MAAM,MAAM;AAC3B,UAAM,UAAU,IAAI,UAAU;AAC9B,UAAM,WAAW,IAAI,gBAAgB;AAGrC,UAAM,UAAU,MAAM;AACpB,UAAI,SAAS,OAAO,SAAS,SAAS,QAAQ;AAC9C,aAAO,MAAM,IAAI,SAAS,OAAO,MAAM,QAAQ;AAAA,IACjD,GAAG,CAAC,KAAK,OAAO,QAAQ,CAAC;AAEzB,UAAM,UAAU,CAAC,OAAgC;AAC/C,eAAS,UAAU;AACnB,UAAI,OAAO,QAAQ,WAAY,KAAI,EAAE;AAAA,eAC5B,IAAK,CAAC,IAAwD,UAAU;AAAA,IACnF;AAEA,UAAM,YAAY,CAAC,MAA6C;AAC9D,UAAI,SAAU;AACd,UAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,cAAc;AACnD,UAAE,eAAe;AACjB,YAAI,QAAQ,OAAO,CAAC;AAAA,MACtB,WAAW,EAAE,QAAQ,aAAa,EAAE,QAAQ,aAAa;AACvD,UAAE,eAAe;AACjB,YAAI,QAAQ,OAAO,EAAE;AAAA,MACvB,WAAW,EAAE,QAAQ,KAAK;AACxB,UAAE,eAAe;AACjB,YAAI,CAAC,QAAS,KAAI,OAAO,KAAK;AAAA,MAChC;AAAA,IACF;AAOA,UAAM,MACJ,qBAAC,WAAM,WAAU,0BAEf;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,IAAI;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA,gBAAc;AAAA,UACd,iBAAe,YAAY;AAAA,UAC3B,oBAAkB,cAAc,SAAS;AAAA,UACzC,UAAU,WAAW,IAAI;AAAA,UACzB,UAAU,MAAM,IAAI,OAAO,KAAK;AAAA,UAChC;AAAA,UACA,WAAW;AAAA,YACT;AAAA,YACA;AAAA,UACF;AAAA,UACC,GAAG;AAAA;AAAA,MACN;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,eAAY;AAAA,UACZ,eAAa,iBAAiB,KAAK;AAAA,UACnC,WAAW,GAAG,qBAAqB,CAAC;AAAA,UAIpC;AAAA,YAAC;AAAA;AAAA,cACC,eAAa,aAAa,KAAK;AAAA,cAC/B,WAAW,GAAG,iBAAiB,EAAE,UAAU,SAAS,SAAS,CAAC,CAAC;AAAA;AAAA,UACjE;AAAA;AAAA,MACF;AAAA,MAGA,oBAAC,UAAK,WAAW,GAAG,mBAAmB,EAAE,SAAS,CAAC,CAAC,GAAI,UAAS;AAAA,OACnE;AAKF,UAAM,SACJ;AAAA,MAAC;AAAA;AAAA,QACC,eAAa,gBAAgB,KAAK;AAAA,QAClC,WAAW,GAAG,oBAAoB,GAAG,YAAY,SAAS;AAAA,QAEzD;AAAA;AAAA,UACA,cACC,oBAAC,UAAK,IAAI,QAAQ,WAAW,GAAG,yBAAyB,CAAC,GACvD,uBACH,IACE;AAAA;AAAA;AAAA,IACN;AAGF,WAAO,IAAI,YAAY,SACrB,oBAAC,UAAK,eAAa,cAAc,KAAK,IAAI,WAAW,GAAG,kBAAkB,CAAC,GACxE,kBACH,IAEA;AAAA,EAEJ;AACF;","names":["RadioGroup","Radio"]}
@@ -9,10 +9,10 @@ export declare const optionVariants: (props?: ({
9
9
  size?: "md" | "sm" | "lg" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  export declare const checkClass = "absolute end-2 inline-flex text-text-primary";
12
- export declare const groupLabelClass = "px-3 py-1 text-label text-text-muted select-none";
12
+ export declare const groupLabelClass = "px-3 py-1 text-label text-text-secondary select-none";
13
13
  export declare const separatorClass = "my-1 h-px bg-border-default";
14
- export declare const errorTextClass = "mt-1 text-label text-status-critical-fg";
15
- export declare const descriptionClass = "mt-1 text-label text-text-muted";
14
+ export declare const errorTextClass = "mt-1 text-label text-status-critical-on-surface";
15
+ export declare const descriptionClass = "mt-1 text-caption text-text-secondary";
16
16
  export declare const labelClass = "text-label text-text-primary";
17
17
  export type TriggerVariantProps = VariantProps<typeof triggerVariants>;
18
18
  //# sourceMappingURL=select.variants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"select.variants.d.ts","sourceRoot":"","sources":["../../../src/components/select/select.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE,eAAO,MAAM,eAAe;;;8EAqD3B,CAAC;AAGF,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAG3D,eAAO,MAAM,YAAY,QAKd,CAAC;AAYZ,eAAO,MAAM,cAAc;;8EAoB1B,CAAC;AAGF,eAAO,MAAM,UAAU,iDAAiD,CAAC;AAGzE,eAAO,MAAM,eAAe,qDAAqD,CAAC;AAGlF,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAG5D,eAAO,MAAM,cAAc,4CAA4C,CAAC;AAExE,eAAO,MAAM,gBAAgB,oCAAoC,CAAC;AAElE,eAAO,MAAM,UAAU,iCAAiC,CAAC;AAEzD,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"select.variants.d.ts","sourceRoot":"","sources":["../../../src/components/select/select.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE,eAAO,MAAM,eAAe;;;8EAqD3B,CAAC;AAGF,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAG3D,eAAO,MAAM,YAAY,QAKd,CAAC;AAYZ,eAAO,MAAM,cAAc;;8EAoB1B,CAAC;AAGF,eAAO,MAAM,UAAU,iDAAiD,CAAC;AAIzE,eAAO,MAAM,eAAe,yDAAyD,CAAC;AAGtF,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAG5D,eAAO,MAAM,cAAc,oDAAoD,CAAC;AAIhF,eAAO,MAAM,gBAAgB,0CAA0C,CAAC;AAExE,eAAO,MAAM,UAAU,iCAAiC,CAAC;AAEzD,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { cva } from "class-variance-authority";
2
+ import { focusRing } from "../../lib/focus-ring";
2
3
  const triggerVariants = cva(
3
4
  [
4
5
  "inline-flex items-center justify-between gap-2 rounded-(--radius-md) px-3",
@@ -18,7 +19,7 @@ const triggerVariants = cva(
18
19
  "min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)",
19
20
  // visible 2px signal-blue ring at 2px offset; persists while the listbox is open
20
21
  "outline-none",
21
- "focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2",
22
+ focusRing,
22
23
  "data-[state=open]:ring-2 data-[state=open]:ring-border-focus data-[state=open]:ring-offset-2",
23
24
  // error: strong border treatment, driven by aria-invalid
24
25
  "aria-invalid:border-border-strong",
@@ -82,10 +83,10 @@ const optionVariants = cva(
82
83
  }
83
84
  );
84
85
  const checkClass = "absolute end-2 inline-flex text-text-primary";
85
- const groupLabelClass = "px-3 py-1 text-label text-text-muted select-none";
86
+ const groupLabelClass = "px-3 py-1 text-label text-text-secondary select-none";
86
87
  const separatorClass = "my-1 h-px bg-border-default";
87
- const errorTextClass = "mt-1 text-label text-status-critical-fg";
88
- const descriptionClass = "mt-1 text-label text-text-muted";
88
+ const errorTextClass = "mt-1 text-label text-status-critical-on-surface";
89
+ const descriptionClass = "mt-1 text-caption text-text-secondary";
89
90
  const labelClass = "text-label text-text-primary";
90
91
  export {
91
92
  checkClass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/select/select.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\n\n// Trigger: control-* tier surface, secondary hover fill, focus ring that persists\n// while open, strong border in error, target-size floor, base+verdify motion.\nexport const triggerVariants = cva(\n [\n \"inline-flex items-center justify-between gap-2 rounded-(--radius-md) px-3\",\n \"bg-control-bg text-control-fg border border-control-border\",\n \"hover:bg-action-secondary-bg-hover cursor-pointer select-none\",\n // DEC-A — the trigger is a form field, so its value SIZE is text-base (16px): the\n // iOS no-zoom reset is a hard floor that holds on EVERY size. The brand type ROLE\n // (line-height + letter-spacing) rides along via the role-suffix vars set per size\n // below. text-body itself (a 15px font-size) is NEVER bound on the trigger — under\n // the role-aware cn it collapses against text-base, and 15px reintroduces the iOS\n // focus zoom the reset exists to prevent. So the font-size stays text-base across\n // sizes; only the leading/tracking role and the padding density shift.\n \"text-base\",\n // open/close transition — base duration + verdify easing, never deliberate theatre\n \"transition-colors duration-(--motion-duration-base) ease-(--motion-easing-verdify)\",\n // target-size floor: 44px touch / 40px pointer\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // visible 2px signal-blue ring at 2px offset; persists while the listbox is open\n \"outline-none\",\n \"focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2\",\n \"data-[state=open]:ring-2 data-[state=open]:ring-border-focus data-[state=open]:ring-offset-2\",\n // error: strong border treatment, driven by aria-invalid\n \"aria-invalid:border-border-strong\",\n // disabled: out of tab order (Radix sets data-disabled), reduced emphasis\n \"data-[disabled]:pointer-events-none data-[disabled]:cursor-default\",\n \"disabled:text-text-disabled data-[disabled]:text-text-disabled\",\n ],\n {\n variants: {\n // DEC-B: @verdify/tokens exposes only target-size FLOORS (44px / 40px), no\n // height scale. Every size anchors the shared floor (min-h-, in the base) and\n // NEVER sets a fixed height below it (a11y). Each size is TYPE-ROLE + vertical\n // padding (density) ABOVE the floor; the resting height EMERGES from the padding\n // and grows monotonically sm <= md <= lg. Because the trigger is a form field\n // (DEC-A), the value font-SIZE is pinned to text-base in the base — so here the\n // type role shifts only through the brand line-height + letter-spacing role\n // suffix (NOT the font-size), tightening at sm (caption metrics) and loosening at\n // lg (body-lg metrics). The padding ladder is --space-1 (.25rem) <= --space-2\n // (.5rem) <= --space-3 (.75rem), all >= the floor. (An earlier build set fixed\n // h-(--size-target-*), which made lg SHORTER than sm/md on desktop — the inverse\n // of the requirement — and is removed.)\n size: {\n sm: \"leading-(--text-caption--line-height) tracking-(--text-caption--letter-spacing) py-(--space-1)\",\n md: \"leading-(--text-body--line-height) tracking-(--text-body--letter-spacing) py-(--space-2)\",\n lg: \"leading-(--text-body-lg--line-height) tracking-(--text-body-lg--letter-spacing) py-(--space-3)\",\n },\n width: {\n auto: \"w-auto\",\n full: \"w-full\",\n },\n },\n defaultVariants: { size: \"md\", width: \"auto\" },\n },\n);\n\n// Placeholder text: de-emphasised, never the accessible name.\nexport const placeholderClass = \"text-control-placeholder\";\n\n// Listbox: raised surface, outer border, md elevation; opens with base motion only.\nexport const listboxClass = [\n \"z-50 overflow-hidden rounded-(--radius-md) p-1\",\n \"bg-surface-raised border border-surface-border shadow-(--shadow-md)\",\n \"transition-opacity duration-(--motion-duration-base) ease-(--motion-easing-verdify)\",\n \"motion-reduce:transition-none\",\n].join(\" \");\n\n// Option row: primary label, secondary hover/active fill, target-row floor.\n// DEC-B: the option row is parameterized by the SAME size the trigger is, so the\n// listbox density tracks the trigger. It anchors the shared target-row floor\n// (min-h-, in the base) and NEVER sets a fixed height below it; each size is\n// TYPE-ROLE + vertical padding (density) ABOVE the floor, the row height emerging\n// from the padding. Unlike the trigger, an option row is NOT a focused text field —\n// the iOS no-zoom reset does not apply — so here the type role shifts through the\n// actual font-SIZE role: caption (sm) < body (md) < body-lg (lg), paired with the\n// identical --space-1 <= --space-2 <= --space-3 padding ladder as the trigger. Both\n// type role and density therefore climb monotonically sm <= md <= lg, all >= floor.\nexport const optionVariants = cva(\n [\n \"relative flex items-center gap-2 rounded-(--radius-md) pe-8 ps-3\",\n \"text-text-primary outline-none cursor-pointer select-none\",\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // active (highlighted) option uses the secondary hover fill\n \"data-[highlighted]:bg-action-secondary-bg-hover data-[highlighted]:outline-none\",\n // disabled option: reduced emphasis, not operable\n \"data-[disabled]:text-text-disabled data-[disabled]:pointer-events-none\",\n ],\n {\n variants: {\n size: {\n sm: \"text-caption py-(--space-1)\",\n md: \"text-body py-(--space-2)\",\n lg: \"text-body-lg py-(--space-3)\",\n },\n },\n defaultVariants: { size: \"md\" },\n },\n);\n\n// The selected-option check — a NEUTRAL mark (text-text-primary), never a status color.\nexport const checkClass = \"absolute end-2 inline-flex text-text-primary\";\n\n// Group heading: non-selectable, muted.\nexport const groupLabelClass = \"px-3 py-1 text-label text-text-muted select-none\";\n\n// Listbox/option dividers.\nexport const separatorClass = \"my-1 h-px bg-border-default\";\n\n// Error-slot text — status critical foreground.\nexport const errorTextClass = \"mt-1 text-label text-status-critical-fg\";\n// Non-error helper text — muted.\nexport const descriptionClass = \"mt-1 text-label text-text-muted\";\n// The visible, associated label.\nexport const labelClass = \"text-label text-text-primary\";\n\nexport type TriggerVariantProps = VariantProps<typeof triggerVariants>;\n"],"mappings":"AAAA,SAAS,WAA8B;AAIhC,MAAM,kBAAkB;AAAA,EAC7B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAaR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,MAAM,MAAM,OAAO,OAAO;AAAA,EAC/C;AACF;AAGO,MAAM,mBAAmB;AAGzB,MAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAYH,MAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,MAAM,KAAK;AAAA,EAChC;AACF;AAGO,MAAM,aAAa;AAGnB,MAAM,kBAAkB;AAGxB,MAAM,iBAAiB;AAGvB,MAAM,iBAAiB;AAEvB,MAAM,mBAAmB;AAEzB,MAAM,aAAa;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/select/select.variants.ts"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport { focusRing } from \"../../lib/focus-ring\";\n\n// Trigger: control-* tier surface, secondary hover fill, focus ring that persists\n// while open, strong border in error, target-size floor, base+verdify motion.\nexport const triggerVariants = cva(\n [\n \"inline-flex items-center justify-between gap-2 rounded-(--radius-md) px-3\",\n \"bg-control-bg text-control-fg border border-control-border\",\n \"hover:bg-action-secondary-bg-hover cursor-pointer select-none\",\n // DEC-A — the trigger is a form field, so its value SIZE is text-base (16px): the\n // iOS no-zoom reset is a hard floor that holds on EVERY size. The brand type ROLE\n // (line-height + letter-spacing) rides along via the role-suffix vars set per size\n // below. text-body itself (a 15px font-size) is NEVER bound on the trigger — under\n // the role-aware cn it collapses against text-base, and 15px reintroduces the iOS\n // focus zoom the reset exists to prevent. So the font-size stays text-base across\n // sizes; only the leading/tracking role and the padding density shift.\n \"text-base\",\n // open/close transition — base duration + verdify easing, never deliberate theatre\n \"transition-colors duration-(--motion-duration-base) ease-(--motion-easing-verdify)\",\n // target-size floor: 44px touch / 40px pointer\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // visible 2px signal-blue ring at 2px offset; persists while the listbox is open\n \"outline-none\",\n focusRing,\n \"data-[state=open]:ring-2 data-[state=open]:ring-border-focus data-[state=open]:ring-offset-2\",\n // error: strong border treatment, driven by aria-invalid\n \"aria-invalid:border-border-strong\",\n // disabled: out of tab order (Radix sets data-disabled), reduced emphasis\n \"data-[disabled]:pointer-events-none data-[disabled]:cursor-default\",\n \"disabled:text-text-disabled data-[disabled]:text-text-disabled\",\n ],\n {\n variants: {\n // DEC-B: @verdify/tokens exposes only target-size FLOORS (44px / 40px), no\n // height scale. Every size anchors the shared floor (min-h-, in the base) and\n // NEVER sets a fixed height below it (a11y). Each size is TYPE-ROLE + vertical\n // padding (density) ABOVE the floor; the resting height EMERGES from the padding\n // and grows monotonically sm <= md <= lg. Because the trigger is a form field\n // (DEC-A), the value font-SIZE is pinned to text-base in the base — so here the\n // type role shifts only through the brand line-height + letter-spacing role\n // suffix (NOT the font-size), tightening at sm (caption metrics) and loosening at\n // lg (body-lg metrics). The padding ladder is --space-1 (.25rem) <= --space-2\n // (.5rem) <= --space-3 (.75rem), all >= the floor. (An earlier build set fixed\n // h-(--size-target-*), which made lg SHORTER than sm/md on desktop — the inverse\n // of the requirement — and is removed.)\n size: {\n sm: \"leading-(--text-caption--line-height) tracking-(--text-caption--letter-spacing) py-(--space-1)\",\n md: \"leading-(--text-body--line-height) tracking-(--text-body--letter-spacing) py-(--space-2)\",\n lg: \"leading-(--text-body-lg--line-height) tracking-(--text-body-lg--letter-spacing) py-(--space-3)\",\n },\n width: {\n auto: \"w-auto\",\n full: \"w-full\",\n },\n },\n defaultVariants: { size: \"md\", width: \"auto\" },\n },\n);\n\n// Placeholder text: de-emphasised, never the accessible name.\nexport const placeholderClass = \"text-control-placeholder\";\n\n// Listbox: raised surface, outer border, md elevation; opens with base motion only.\nexport const listboxClass = [\n \"z-50 overflow-hidden rounded-(--radius-md) p-1\",\n \"bg-surface-raised border border-surface-border shadow-(--shadow-md)\",\n \"transition-opacity duration-(--motion-duration-base) ease-(--motion-easing-verdify)\",\n \"motion-reduce:transition-none\",\n].join(\" \");\n\n// Option row: primary label, secondary hover/active fill, target-row floor.\n// DEC-B: the option row is parameterized by the SAME size the trigger is, so the\n// listbox density tracks the trigger. It anchors the shared target-row floor\n// (min-h-, in the base) and NEVER sets a fixed height below it; each size is\n// TYPE-ROLE + vertical padding (density) ABOVE the floor, the row height emerging\n// from the padding. Unlike the trigger, an option row is NOT a focused text field —\n// the iOS no-zoom reset does not apply — so here the type role shifts through the\n// actual font-SIZE role: caption (sm) < body (md) < body-lg (lg), paired with the\n// identical --space-1 <= --space-2 <= --space-3 padding ladder as the trigger. Both\n// type role and density therefore climb monotonically sm <= md <= lg, all >= floor.\nexport const optionVariants = cva(\n [\n \"relative flex items-center gap-2 rounded-(--radius-md) pe-8 ps-3\",\n \"text-text-primary outline-none cursor-pointer select-none\",\n \"min-h-(--size-target-mobile) sm:min-h-(--size-target-desktop)\",\n // active (highlighted) option uses the secondary hover fill\n \"data-[highlighted]:bg-action-secondary-bg-hover data-[highlighted]:outline-none\",\n // disabled option: reduced emphasis, not operable\n \"data-[disabled]:text-text-disabled data-[disabled]:pointer-events-none\",\n ],\n {\n variants: {\n size: {\n sm: \"text-caption py-(--space-1)\",\n md: \"text-body py-(--space-2)\",\n lg: \"text-body-lg py-(--space-3)\",\n },\n },\n defaultVariants: { size: \"md\" },\n },\n);\n\n// The selected-option check — a NEUTRAL mark (text-text-primary), never a status color.\nexport const checkClass = \"absolute end-2 inline-flex text-text-primary\";\n\n// Group heading: non-selectable, essential de-emphasized text — uses secondary (AA), not the\n// decorative-only muted role (accessibility.md).\nexport const groupLabelClass = \"px-3 py-1 text-label text-text-secondary select-none\";\n\n// Listbox/option dividers.\nexport const separatorClass = \"my-1 h-px bg-border-default\";\n\n// Error-slot text — status critical foreground.\nexport const errorTextClass = \"mt-1 text-label text-status-critical-on-surface\";\n// Non-error helper text — secondary (informational; mirrors Input's help tone, text-caption per\n// the message scale, text-text-secondary rather than text-muted so it reads at the same legibility\n// floor as Input help text — both carry information the user needs to complete the field).\nexport const descriptionClass = \"mt-1 text-caption text-text-secondary\";\n// The visible, associated label.\nexport const labelClass = \"text-label text-text-primary\";\n\nexport type TriggerVariantProps = VariantProps<typeof triggerVariants>;\n"],"mappings":"AAAA,SAAS,WAA8B;AACvC,SAAS,iBAAiB;AAInB,MAAM,kBAAkB;AAAA,EAC7B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAaR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,MAAM,MAAM,OAAO,OAAO;AAAA,EAC/C;AACF;AAGO,MAAM,mBAAmB;AAGzB,MAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAYH,MAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,MAAM,KAAK;AAAA,EAChC;AACF;AAGO,MAAM,aAAa;AAInB,MAAM,kBAAkB;AAGxB,MAAM,iBAAiB;AAGvB,MAAM,iBAAiB;AAIvB,MAAM,mBAAmB;AAEzB,MAAM,aAAa;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.variants.d.ts","sourceRoot":"","sources":["../../../src/components/sheet/sheet.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAclE,eAAO,MAAM,kBAAkB,oFAK7B,CAAC;AA0BH,eAAO,MAAM,kBAAkB;;;8EAyD9B,CAAC;AAMF,eAAO,MAAM,gBAAgB,yGAC2E,CAAC;AAKzG,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAMrE,eAAO,MAAM,cAAc,iEACqC,CAAC;AAMjE,eAAO,MAAM,gBAAgB,sGACwE,CAAC;AAQtG,eAAO,MAAM,kBAAkB,oFAa7B,CAAC;AAIH,eAAO,MAAM,oBAAoB,0CAA0C,CAAC;AAE5E,MAAM,MAAM,sBAAsB,GAAG,YAAY,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"sheet.variants.d.ts","sourceRoot":"","sources":["../../../src/components/sheet/sheet.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAelE,eAAO,MAAM,kBAAkB,oFAK7B,CAAC;AA0BH,eAAO,MAAM,kBAAkB;;;8EAyD9B,CAAC;AAMF,eAAO,MAAM,gBAAgB,yGAC2E,CAAC;AAKzG,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAMrE,eAAO,MAAM,cAAc,iEACqC,CAAC;AAMjE,eAAO,MAAM,gBAAgB,sGACwE,CAAC;AAQtG,eAAO,MAAM,kBAAkB,oFAa7B,CAAC;AAIH,eAAO,MAAM,oBAAoB,0CAA0C,CAAC;AAE5E,MAAM,MAAM,sBAAsB,GAAG,YAAY,CAAC,OAAO,kBAAkB,CAAC,CAAC"}