@krizaka/ui 3.36.0 → 3.38.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 (115) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/activity-feed.js.map +1 -1
  3. package/dist/app-shell.js +3 -3
  4. package/dist/app-shell.js.map +1 -1
  5. package/dist/badge.d.ts +1 -1
  6. package/dist/badge.js +1 -1
  7. package/dist/badge.js.map +1 -1
  8. package/dist/blocks.css +30 -30
  9. package/dist/card.d.ts +6 -6
  10. package/dist/card.js +2 -2
  11. package/dist/card.js.map +1 -1
  12. package/dist/chip-row.js +1 -1
  13. package/dist/chip-row.js.map +1 -1
  14. package/dist/chip.js +1 -1
  15. package/dist/chip.js.map +1 -1
  16. package/dist/{chunk-QDXV544C.js → chunk-4QH6CP2Y.js} +2 -2
  17. package/dist/chunk-4QH6CP2Y.js.map +1 -0
  18. package/dist/{chunk-CKSHLSDH.js → chunk-AAP3U66V.js} +2 -2
  19. package/dist/{chunk-7UCPGXKN.js → chunk-C3NF7V5Y.js} +2 -2
  20. package/dist/{chunk-7UCPGXKN.js.map → chunk-C3NF7V5Y.js.map} +1 -1
  21. package/dist/{chunk-K2ZYB4HP.js → chunk-S5LP7KKW.js} +2 -2
  22. package/dist/{chunk-63KCDMX2.js → chunk-VPO27AFS.js} +4 -4
  23. package/dist/chunk-VPO27AFS.js.map +1 -0
  24. package/dist/{chunk-YOTQD7JD.js → chunk-WYQ277F2.js} +2 -2
  25. package/dist/{chunk-2DGLWTPS.js → chunk-YKOLME7R.js} +3 -3
  26. package/dist/{chunk-2DGLWTPS.js.map → chunk-YKOLME7R.js.map} +1 -1
  27. package/dist/code-block.js +4 -4
  28. package/dist/code-block.js.map +1 -1
  29. package/dist/combobox.js +2 -2
  30. package/dist/data-table.js +3 -3
  31. package/dist/file-upload.js +3 -3
  32. package/dist/file-upload.js.map +1 -1
  33. package/dist/filter-bar.js +2 -2
  34. package/dist/genui.js +4 -4
  35. package/dist/landing-page.js +1 -6
  36. package/dist/landing-page.js.map +1 -1
  37. package/dist/language-switch.js +1 -1
  38. package/dist/marketing-layout.js +1 -1
  39. package/dist/marketing-layout.js.map +1 -1
  40. package/dist/media-grid.js +1 -1
  41. package/dist/meter.js +2 -2
  42. package/dist/meter.js.map +1 -1
  43. package/dist/motion.css +2 -2
  44. package/dist/native.js +4 -4
  45. package/dist/native.js.map +1 -1
  46. package/dist/nav-bar.js +7 -7
  47. package/dist/nav-bar.js.map +1 -1
  48. package/dist/product-page.js +1 -6
  49. package/dist/product-page.js.map +1 -1
  50. package/dist/progress.js +3 -3
  51. package/dist/progress.js.map +1 -1
  52. package/dist/rail-nav.js +1 -1
  53. package/dist/rail-nav.js.map +1 -1
  54. package/dist/service-card.js +2 -2
  55. package/dist/service-card.js.map +1 -1
  56. package/dist/settings-layout.js +1 -1
  57. package/dist/settings-layout.js.map +1 -1
  58. package/dist/site-footer.js +2 -2
  59. package/dist/site-footer.js.map +1 -1
  60. package/dist/skip-link.js +1 -1
  61. package/dist/slot-picker.js +1 -1
  62. package/dist/slot-picker.js.map +1 -1
  63. package/dist/step-progress.js +1 -1
  64. package/dist/step-progress.js.map +1 -1
  65. package/dist/tabs.js +4 -4
  66. package/dist/tabs.js.map +1 -1
  67. package/dist/tag-input.js +2 -2
  68. package/dist/tag-selector.js +1 -1
  69. package/dist/toast.js +1 -1
  70. package/dist/toast.js.map +1 -1
  71. package/package.json +5 -5
  72. package/registry/activity-feed.json +1 -1
  73. package/registry/app-shell.json +1 -1
  74. package/registry/badge.json +1 -1
  75. package/registry/card.json +1 -1
  76. package/registry/chip-row.json +1 -1
  77. package/registry/chip.json +1 -1
  78. package/registry/examples/badge/native/dot.dark.png +0 -0
  79. package/registry/examples/badge/native/dot.light.png +0 -0
  80. package/registry/examples/badge/native/scrim.dark.png +0 -0
  81. package/registry/examples/badge/native/scrim.light.png +0 -0
  82. package/registry/examples/badge/native/tones.dark.png +0 -0
  83. package/registry/examples/badge/native/tones.light.png +0 -0
  84. package/registry/examples/card/native/default.dark.png +0 -0
  85. package/registry/examples/card/native/default.light.png +0 -0
  86. package/registry/examples/card/native/elevated.dark.png +0 -0
  87. package/registry/examples/card/native/elevated.light.png +0 -0
  88. package/registry/examples/description-list/native/job-details.dark.png +0 -0
  89. package/registry/examples/description-list/native/job-details.light.png +0 -0
  90. package/registry/examples/meta-row/native/video.dark.png +0 -0
  91. package/registry/examples/meta-row/native/video.light.png +0 -0
  92. package/registry/examples/swipe-deck/native/discover.dark.png +0 -0
  93. package/registry/examples/swipe-deck/native/discover.light.png +0 -0
  94. package/registry/index.json +5 -5
  95. package/registry/landing-page.json +1 -1
  96. package/registry/language-switch.json +1 -1
  97. package/registry/map.json +1 -1
  98. package/registry/meter.json +1 -1
  99. package/registry/nav-bar.json +2 -2
  100. package/registry/product-page.json +1 -1
  101. package/registry/progress.json +1 -1
  102. package/registry/rail-nav.json +2 -2
  103. package/registry/service-card.json +1 -1
  104. package/registry/settings-layout.json +1 -1
  105. package/registry/site-footer.json +2 -2
  106. package/registry/skip-link.json +1 -1
  107. package/registry/slot-picker.json +1 -1
  108. package/registry/step-progress.json +1 -1
  109. package/registry/tabs.json +1 -1
  110. package/registry/toast.json +1 -1
  111. package/dist/chunk-63KCDMX2.js.map +0 -1
  112. package/dist/chunk-QDXV544C.js.map +0 -1
  113. /package/dist/{chunk-CKSHLSDH.js.map → chunk-AAP3U66V.js.map} +0 -0
  114. /package/dist/{chunk-K2ZYB4HP.js.map → chunk-S5LP7KKW.js.map} +0 -0
  115. /package/dist/{chunk-YOTQD7JD.js.map → chunk-WYQ277F2.js.map} +0 -0
@@ -205,7 +205,7 @@
205
205
  },
206
206
  {
207
207
  "path": "step-progress/step-progress.tsx",
208
- "content": "// Server-safe. The head of a multi-step flow: back, segments that fill, the position in words — a `progressbar`.\nimport type * as React from \"react\";\n\nexport type StepProgressProps = {\n /** The current step, from 1. */\n step: number;\n /** How many steps. */\n total: number;\n /** The position in words: `(n, total) => \"Step \" + n + \" of \" + total` — passed translated. */\n label: (step: number, total: number) => string;\n /** The back link or button (a `Button`), at the left. */\n back?: React.ReactNode;\n /** At the right: a Skip link. */\n trailing?: React.ReactNode;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** The head of a sign-up or a profile to fill: back, segments that fill, the position in words. */\nexport function StepProgress({ step, total, label, back, trailing, className }: StepProgressProps) {\n const words = label(step, total);\n return (\n <div className={[\"flex flex-col gap-1.5\", className].filter(Boolean).join(\" \")}>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex min-w-10 justify-start\">{back}</div>\n <div role=\"progressbar\" aria-label={words} aria-valuemin={1} aria-valuemax={total} aria-valuenow={step} aria-valuetext={words} className=\"flex flex-1 gap-1\">\n {Array.from({ length: total }, (_, i) => (\n <span key={i} className={[\"h-1 flex-1 rounded-full transition-colors motion-reduce:transition-none\", i < step ? \"bg-accent\" : \"bg-surface-3\"].join(\" \")} />\n ))}\n </div>\n <div className=\"flex min-w-10 justify-end\">{trailing}</div>\n </div>\n <p className=\"m-0 text-center text-xs text-fg-secondary\" aria-hidden>\n {words}\n </p>\n </div>\n );\n}\n"
208
+ "content": "// Server-safe. The head of a multi-step flow: back, segments that fill, the position in words — a `progressbar`.\nimport type * as React from \"react\";\n\nexport type StepProgressProps = {\n /** The current step, from 1. */\n step: number;\n /** How many steps. */\n total: number;\n /** The position in words: `(n, total) => \"Step \" + n + \" of \" + total` — passed translated. */\n label: (step: number, total: number) => string;\n /** The back link or button (a `Button`), at the left. */\n back?: React.ReactNode;\n /** At the right: a Skip link. */\n trailing?: React.ReactNode;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** The head of a sign-up or a profile to fill: back, segments that fill, the position in words. */\nexport function StepProgress({ step, total, label, back, trailing, className }: StepProgressProps) {\n const words = label(step, total);\n return (\n <div className={[\"flex flex-col gap-1.5\", className].filter(Boolean).join(\" \")}>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex min-w-10 justify-start\">{back}</div>\n <div role=\"progressbar\" aria-label={words} aria-valuemin={1} aria-valuemax={total} aria-valuenow={step} aria-valuetext={words} className=\"flex flex-1 gap-1\">\n {Array.from({ length: total }, (_, i) => (\n <span key={i} className={[\"h-1 flex-1 rounded-sm transition-colors motion-reduce:transition-none\", i < step ? \"bg-accent\" : \"bg-surface-3\"].join(\" \")} />\n ))}\n </div>\n <div className=\"flex min-w-10 justify-end\">{trailing}</div>\n </div>\n <p className=\"m-0 text-center text-xs text-fg-secondary\" aria-hidden>\n {words}\n </p>\n </div>\n );\n}\n"
209
209
  }
210
210
  ],
211
211
  "dependencies": [],
@@ -229,7 +229,7 @@
229
229
  },
230
230
  {
231
231
  "path": "tabs/tabs.tsx",
232
- "content": "// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { useSlidingIndicator } from \"../sliding-indicator\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n // One light glides from the active tab to the next (`before:`, placed by useSlidingIndicator); until it is placed\n // (server render), each trigger carries its own active style.\n list:\n \"group/tabs relative isolate flex shrink-0 data-[orientation=vertical]:flex-col \" +\n \"before:pointer-events-none before:absolute before:-z-10 before:opacity-0 before:transition-[translate,width,height,opacity] before:duration-300 \" +\n \"motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-fade min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=inactive]:hidden\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list:\n \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r before:rounded-full before:bg-accent \" +\n \"data-[orientation=horizontal]:before:-bottom-px data-[orientation=horizontal]:before:left-0 data-[orientation=horizontal]:before:h-0.5 \" +\n \"data-[orientation=horizontal]:before:w-(--kz-ind-w) data-[orientation=horizontal]:before:translate-x-(--kz-ind-x) \" +\n \"data-[orientation=vertical]:before:-right-px data-[orientation=vertical]:before:top-0 data-[orientation=vertical]:before:w-0.5 \" +\n \"data-[orientation=vertical]:before:h-(--kz-ind-h) data-[orientation=vertical]:before:translate-y-(--kz-ind-y)\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-full after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5 \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:after:bg-transparent\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list:\n \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:bg-accent before:shadow-sm\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent \" +\n \"transition-colors group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent group-data-[indicator=on]/tabs:data-[state=active]:shadow-none\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list:\n \"gap-1.5 overflow-x-auto overflow-y-hidden overscroll-x-contain pt-1 -mt-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-full before:border before:border-accent before:bg-accent-soft\",\n trigger:\n \"h-9 rounded-full border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:border-transparent group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */\n variant?: TabsVariants[\"variant\"];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. One light glides to the active tab. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=tab]\", active: \"[role=tab][data-state=active]\" });\n return <TabsPrimitive.List ref={ref} data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n"
232
+ "content": "// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { useSlidingIndicator } from \"../sliding-indicator\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n // One light glides from the active tab to the next (`before:`, placed by useSlidingIndicator); until it is placed\n // (server render), each trigger carries its own active style.\n list:\n \"group/tabs relative isolate flex shrink-0 data-[orientation=vertical]:flex-col \" +\n \"before:pointer-events-none before:absolute before:-z-10 before:opacity-0 before:transition-[translate,width,height,opacity] before:duration-300 \" +\n \"motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-fade min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=inactive]:hidden\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list:\n \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r before:rounded-sm before:bg-accent \" +\n \"data-[orientation=horizontal]:before:-bottom-px data-[orientation=horizontal]:before:left-0 data-[orientation=horizontal]:before:h-0.5 \" +\n \"data-[orientation=horizontal]:before:w-(--kz-ind-w) data-[orientation=horizontal]:before:translate-x-(--kz-ind-x) \" +\n \"data-[orientation=vertical]:before:-right-px data-[orientation=vertical]:before:top-0 data-[orientation=vertical]:before:w-0.5 \" +\n \"data-[orientation=vertical]:before:h-(--kz-ind-h) data-[orientation=vertical]:before:translate-y-(--kz-ind-y)\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-sm after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5 \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:after:bg-transparent\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list:\n \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:bg-accent before:shadow-sm\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent \" +\n \"transition-colors group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent group-data-[indicator=on]/tabs:data-[state=active]:shadow-none\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list:\n \"gap-1.5 overflow-x-auto overflow-y-hidden overscroll-x-contain pt-1 -mt-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-sm before:border before:border-accent before:bg-accent-soft\",\n trigger:\n \"h-9 rounded-sm border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:border-transparent group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */\n variant?: TabsVariants[\"variant\"];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. One light glides to the active tab. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=tab]\", active: \"[role=tab][data-state=active]\" });\n return <TabsPrimitive.List ref={ref} data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n"
233
233
  }
234
234
  ],
235
235
  "dependencies": [
@@ -249,7 +249,7 @@
249
249
  },
250
250
  {
251
251
  "path": "toast/toast.tsx",
252
- "content": "// Client. sonner does the queue, the stacking, the swipe, the timers, the live region and the hotkey; the look is\n// ours: sonner's own styling is off (`unstyled`) and every part reads a role. The status tones are soft (a tinted\n// border and a coloured icon, the text stays `text-fg`): the invariant status tokens are too light for small text.\nimport type * as React from \"react\";\nimport { type ExternalToast, toast, Toaster as Sonner, type ToasterProps as SonnerProps } from \"sonner\";\n\nimport { cn } from \"../cn\";\nimport { LoaderGlyph } from \"../spinner/glyph\";\n\nexport type { ExternalToast } from \"sonner\";\nexport { toast } from \"sonner\";\n\nconst tone = (color: string) => cn(\"border-l-4\", color);\n\nexport const toastClassNames = {\n // `kz-toast`: a hairline at the bottom counts the time left, and pauses with the toast (hover, focus) — motion.css.\n toast:\n \"kz-toast group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-xl border border-border-default bg-surface-2 p-4 pr-10 \" +\n \"text-sm text-fg shadow-lg\",\n default: \"\",\n title: \"font-semibold text-fg\",\n description: \"mt-0.5 text-xs text-fg-secondary\",\n content: \"flex min-w-0 flex-1 flex-col\",\n icon: \"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center [&>svg]:h-4 [&>svg]:w-4\",\n success: tone(\"border-l-success [&_[data-icon]]:text-success\"),\n warning: tone(\"border-l-warning [&_[data-icon]]:text-warning\"),\n error: tone(\"border-l-danger [&_[data-icon]]:text-danger\"),\n info: tone(\"border-l-info [&_[data-icon]]:text-info\"),\n loading: \"\",\n actionButton:\n \"ml-auto shrink-0 self-center rounded-lg bg-accent px-3 py-1.5 text-xs font-semibold text-on-accent hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n cancelButton:\n \"shrink-0 self-center rounded-lg px-3 py-1.5 text-xs font-semibold text-fg-secondary hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n closeButton:\n \"absolute right-2 top-2 flex h-7 w-7 items-center justify-center rounded-full text-fg-secondary hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&>svg]:h-3.5 [&>svg]:w-3.5\",\n} satisfies NonNullable<NonNullable<SonnerProps[\"toastOptions\"]>[\"classNames\"]>;\n\nexport type ToasterProps = Omit<SonnerProps, \"richColors\" | \"theme\" | \"containerAriaLabel\" | \"toastOptions\"> & {\n /** The accessible name of the notifications region (sonner's `containerAriaLabel`) — passed translated. */\n label: string;\n /** The accessible name of each toast's close button — passed translated. */\n closeLabel: string;\n /** Extra options for every toast (duration, a className); the class names are merged with the platform's. */\n toastOptions?: Omit<NonNullable<SonnerProps[\"toastOptions\"]>, \"unstyled\" | \"closeButtonAriaLabel\">;\n};\n\n/** Mount once, in the root layout. Then call `toast()`, `toast.success/warning/error/info`, `toast.custom(…)`, and the helpers below. */\nexport function Toaster({ label, closeLabel, position = \"bottom-right\", closeButton = true, toastOptions, duration = 4000, ...props }: ToasterProps) {\n const extra = toastOptions?.classNames ?? {};\n const classNames = Object.fromEntries(\n Object.entries(toastClassNames).map(([key, value]) => [key, cn(value, extra[key as keyof typeof extra])]),\n ) as typeof toastClassNames;\n return (\n <Sonner\n position={position}\n closeButton={closeButton}\n containerAriaLabel={label}\n richColors={false}\n duration={duration}\n style={{ \"--kz-toast-duration\": Number.isFinite(duration) ? `${duration}ms` : \"none\" } as React.CSSProperties}\n toastOptions={{ ...toastOptions, unstyled: true, closeButtonAriaLabel: closeLabel, classNames }}\n {...props}\n />\n );\n}\n\n/** The time-left hairline of one toast: its own duration (none when it stays). */\nconst timer = (duration: number | undefined, style?: React.CSSProperties) =>\n ({ ...style, \"--kz-toast-duration\": duration === undefined ? undefined : Number.isFinite(duration) ? `${duration}ms` : \"none\" }) as React.CSSProperties;\n\nexport interface UndoToastOptions extends Omit<ExternalToast, \"action\" | \"cancel\"> {\n /** The label of the undo button (“Undo”) — passed translated. */\n undoLabel: string;\n /** Called when the user undoes: restore what was done. */\n onUndo: () => void;\n /** Called when the toast goes without an undo: make it final (delete for good, send…). */\n onCommit?: () => void;\n}\n\n/**\n * “Archived — Undo”: an action that waits before it becomes final. The hairline counts the time left to undo (6 s by\n * default, paused while hovered or focused); `onCommit` runs when it goes without an undo. Returns the toast's id.\n */\nexport function undoToast(message: React.ReactNode, { undoLabel, onUndo, onCommit, duration = 6000, style, onAutoClose, onDismiss, ...options }: UndoToastOptions) {\n let undone = false;\n const commit = () => {\n if (!undone) onCommit?.();\n };\n return toast(message, {\n ...options,\n duration,\n style: timer(duration, style),\n action: {\n label: undoLabel,\n onClick: () => {\n undone = true;\n onUndo();\n },\n },\n onAutoClose: (t) => {\n commit();\n onAutoClose?.(t);\n },\n onDismiss: (t) => {\n commit();\n onDismiss?.(t);\n },\n });\n}\n\nexport interface ProgressToastOptions extends Omit<ExternalToast, \"id\" | \"icon\"> {\n /** The toast to update: pass the id it returned the first time. */\n id?: string | number;\n /** Between 0 and 1. At 1 the toast turns to `doneMessage` (a success) and goes after the usual duration. */\n value: number;\n /** The message once done (“Uploaded”) — passed translated. */\n doneMessage?: React.ReactNode;\n}\n\n/**\n * A long task in a toast (an upload in the background): call it again with the same `id` as it advances — the glyph's\n * trace and a bar grow, without a new toast each time; at 1 it becomes a success. Returns the toast's id.\n */\nexport function progressToast(message: React.ReactNode, { id, value, doneMessage, description, style, ...options }: ProgressToastOptions) {\n const ratio = Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));\n if (ratio >= 1) return toast.success(doneMessage ?? message, { ...options, id, description, style: timer(options.duration, style) });\n return toast(message, {\n ...options,\n id,\n duration: Number.POSITIVE_INFINITY,\n style: timer(Number.POSITIVE_INFINITY, style),\n icon: <LoaderGlyph value={ratio} className=\"h-4 w-4 text-accent\" />,\n description: (\n <span className=\"mt-1 flex flex-col gap-1.5\">\n {typeof description === \"function\" ? description() : description}\n <span aria-hidden className=\"block h-1 w-full overflow-hidden rounded-full bg-surface-3\">\n <span className=\"block h-full rounded-full bg-linear-to-r from-accent to-accent-2 transition-[width] duration-500\" style={{ width: `${ratio * 100}%` }} />\n </span>\n <span className=\"tabular-nums\">{Math.round(ratio * 100)} %</span>\n </span>\n ),\n });\n}\n\nconst counts = new Map<string, number>();\n\nexport interface GroupedToastOptions extends Omit<ExternalToast, \"id\"> {\n /** The repeat count in words (`(n) => \\`${n} new messages\\``) — passed translated; default “×n”. */\n countLabel?: (count: number) => string;\n}\n\n/**\n * The same event again and again (new messages, failed retries) folds into one toast that counts — instead of a pile.\n * Toasts with the same `key` share one place; the count resets once it goes. Returns the toast's id.\n */\nexport function groupedToast(key: string, message: React.ReactNode, { countLabel = (n) => `×${n}`, onAutoClose, onDismiss, ...options }: GroupedToastOptions = {}) {\n const count = (counts.get(key) ?? 0) + 1;\n counts.set(key, count);\n const reset = () => counts.delete(key);\n return toast(message, {\n ...options,\n id: `group:${key}`,\n description: count > 1 ? countLabel(count) : options.description,\n onAutoClose: (t) => {\n reset();\n onAutoClose?.(t);\n },\n onDismiss: (t) => {\n reset();\n onDismiss?.(t);\n },\n });\n}\n"
252
+ "content": "// Client. sonner does the queue, the stacking, the swipe, the timers, the live region and the hotkey; the look is\n// ours: sonner's own styling is off (`unstyled`) and every part reads a role. The status tones are soft (a tinted\n// border and a coloured icon, the text stays `text-fg`): the invariant status tokens are too light for small text.\nimport type * as React from \"react\";\nimport { type ExternalToast, toast, Toaster as Sonner, type ToasterProps as SonnerProps } from \"sonner\";\n\nimport { cn } from \"../cn\";\nimport { LoaderGlyph } from \"../spinner/glyph\";\n\nexport type { ExternalToast } from \"sonner\";\nexport { toast } from \"sonner\";\n\nconst tone = (color: string) => cn(\"border-l-4\", color);\n\nexport const toastClassNames = {\n // `kz-toast`: a hairline at the bottom counts the time left, and pauses with the toast (hover, focus) — motion.css.\n toast:\n \"kz-toast group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-xl border border-border-default bg-surface-2 p-4 pr-10 \" +\n \"text-sm text-fg shadow-lg\",\n default: \"\",\n title: \"font-semibold text-fg\",\n description: \"mt-0.5 text-xs text-fg-secondary\",\n content: \"flex min-w-0 flex-1 flex-col\",\n icon: \"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center [&>svg]:h-4 [&>svg]:w-4\",\n success: tone(\"border-l-success [&_[data-icon]]:text-success\"),\n warning: tone(\"border-l-warning [&_[data-icon]]:text-warning\"),\n error: tone(\"border-l-danger [&_[data-icon]]:text-danger\"),\n info: tone(\"border-l-info [&_[data-icon]]:text-info\"),\n loading: \"\",\n actionButton:\n \"ml-auto shrink-0 self-center rounded-lg bg-accent px-3 py-1.5 text-xs font-semibold text-on-accent hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n cancelButton:\n \"shrink-0 self-center rounded-lg px-3 py-1.5 text-xs font-semibold text-fg-secondary hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n closeButton:\n \"absolute right-2 top-2 flex h-7 w-7 items-center justify-center rounded-full text-fg-secondary hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&>svg]:h-3.5 [&>svg]:w-3.5\",\n} satisfies NonNullable<NonNullable<SonnerProps[\"toastOptions\"]>[\"classNames\"]>;\n\nexport type ToasterProps = Omit<SonnerProps, \"richColors\" | \"theme\" | \"containerAriaLabel\" | \"toastOptions\"> & {\n /** The accessible name of the notifications region (sonner's `containerAriaLabel`) — passed translated. */\n label: string;\n /** The accessible name of each toast's close button — passed translated. */\n closeLabel: string;\n /** Extra options for every toast (duration, a className); the class names are merged with the platform's. */\n toastOptions?: Omit<NonNullable<SonnerProps[\"toastOptions\"]>, \"unstyled\" | \"closeButtonAriaLabel\">;\n};\n\n/** Mount once, in the root layout. Then call `toast()`, `toast.success/warning/error/info`, `toast.custom(…)`, and the helpers below. */\nexport function Toaster({ label, closeLabel, position = \"bottom-right\", closeButton = true, toastOptions, duration = 4000, ...props }: ToasterProps) {\n const extra = toastOptions?.classNames ?? {};\n const classNames = Object.fromEntries(\n Object.entries(toastClassNames).map(([key, value]) => [key, cn(value, extra[key as keyof typeof extra])]),\n ) as typeof toastClassNames;\n return (\n <Sonner\n position={position}\n closeButton={closeButton}\n containerAriaLabel={label}\n richColors={false}\n duration={duration}\n style={{ \"--kz-toast-duration\": Number.isFinite(duration) ? `${duration}ms` : \"none\" } as React.CSSProperties}\n toastOptions={{ ...toastOptions, unstyled: true, closeButtonAriaLabel: closeLabel, classNames }}\n {...props}\n />\n );\n}\n\n/** The time-left hairline of one toast: its own duration (none when it stays). */\nconst timer = (duration: number | undefined, style?: React.CSSProperties) =>\n ({ ...style, \"--kz-toast-duration\": duration === undefined ? undefined : Number.isFinite(duration) ? `${duration}ms` : \"none\" }) as React.CSSProperties;\n\nexport interface UndoToastOptions extends Omit<ExternalToast, \"action\" | \"cancel\"> {\n /** The label of the undo button (“Undo”) — passed translated. */\n undoLabel: string;\n /** Called when the user undoes: restore what was done. */\n onUndo: () => void;\n /** Called when the toast goes without an undo: make it final (delete for good, send…). */\n onCommit?: () => void;\n}\n\n/**\n * “Archived — Undo”: an action that waits before it becomes final. The hairline counts the time left to undo (6 s by\n * default, paused while hovered or focused); `onCommit` runs when it goes without an undo. Returns the toast's id.\n */\nexport function undoToast(message: React.ReactNode, { undoLabel, onUndo, onCommit, duration = 6000, style, onAutoClose, onDismiss, ...options }: UndoToastOptions) {\n let undone = false;\n const commit = () => {\n if (!undone) onCommit?.();\n };\n return toast(message, {\n ...options,\n duration,\n style: timer(duration, style),\n action: {\n label: undoLabel,\n onClick: () => {\n undone = true;\n onUndo();\n },\n },\n onAutoClose: (t) => {\n commit();\n onAutoClose?.(t);\n },\n onDismiss: (t) => {\n commit();\n onDismiss?.(t);\n },\n });\n}\n\nexport interface ProgressToastOptions extends Omit<ExternalToast, \"id\" | \"icon\"> {\n /** The toast to update: pass the id it returned the first time. */\n id?: string | number;\n /** Between 0 and 1. At 1 the toast turns to `doneMessage` (a success) and goes after the usual duration. */\n value: number;\n /** The message once done (“Uploaded”) — passed translated. */\n doneMessage?: React.ReactNode;\n}\n\n/**\n * A long task in a toast (an upload in the background): call it again with the same `id` as it advances — the glyph's\n * trace and a bar grow, without a new toast each time; at 1 it becomes a success. Returns the toast's id.\n */\nexport function progressToast(message: React.ReactNode, { id, value, doneMessage, description, style, ...options }: ProgressToastOptions) {\n const ratio = Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));\n if (ratio >= 1) return toast.success(doneMessage ?? message, { ...options, id, description, style: timer(options.duration, style) });\n return toast(message, {\n ...options,\n id,\n duration: Number.POSITIVE_INFINITY,\n style: timer(Number.POSITIVE_INFINITY, style),\n icon: <LoaderGlyph value={ratio} className=\"h-4 w-4 text-accent\" />,\n description: (\n <span className=\"mt-1 flex flex-col gap-1.5\">\n {typeof description === \"function\" ? description() : description}\n <span aria-hidden className=\"block h-1 w-full overflow-hidden rounded-sm bg-surface-3\">\n <span className=\"block h-full rounded-sm bg-linear-to-r from-accent to-accent-2 transition-[width] duration-500\" style={{ width: `${ratio * 100}%` }} />\n </span>\n <span className=\"tabular-nums\">{Math.round(ratio * 100)} %</span>\n </span>\n ),\n });\n}\n\nconst counts = new Map<string, number>();\n\nexport interface GroupedToastOptions extends Omit<ExternalToast, \"id\"> {\n /** The repeat count in words (`(n) => \\`${n} new messages\\``) — passed translated; default “×n”. */\n countLabel?: (count: number) => string;\n}\n\n/**\n * The same event again and again (new messages, failed retries) folds into one toast that counts — instead of a pile.\n * Toasts with the same `key` share one place; the count resets once it goes. Returns the toast's id.\n */\nexport function groupedToast(key: string, message: React.ReactNode, { countLabel = (n) => `×${n}`, onAutoClose, onDismiss, ...options }: GroupedToastOptions = {}) {\n const count = (counts.get(key) ?? 0) + 1;\n counts.set(key, count);\n const reset = () => counts.delete(key);\n return toast(message, {\n ...options,\n id: `group:${key}`,\n description: count > 1 ? countLabel(count) : options.description,\n onAutoClose: (t) => {\n reset();\n onAutoClose?.(t);\n },\n onDismiss: (t) => {\n reset();\n onDismiss?.(t);\n },\n });\n}\n"
253
253
  }
254
254
  ],
255
255
  "dependencies": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/language-switch/language-switch.tsx"],"sourcesContent":["// Client (a disclosure). The one language switch of the family: a globe and the current code (FR), opening a short list of\n// the languages by their own names — links with `hreflang` that keep the page. A disclosure, not a Radix menu: the items\n// are navigation, and the bar must not pay for a menu engine (Escape, outside press, focus-out and the arrows are here).\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport type { Language } from \"./href\";\n\nexport const languageSwitch = tv({\n slots: {\n root: \"relative inline-flex\",\n trigger:\n \"group/language inline-flex h-9 shrink-0 select-none items-center gap-1.5 rounded-full px-2.5 font-mono text-xs font-semibold text-fg-secondary transition-colors \" +\n \"hover:bg-surface-3 hover:text-fg aria-expanded:bg-surface-3 aria-expanded:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n glyph: \"h-4 w-4 shrink-0\",\n panel:\n \"kz-pop absolute right-0 top-full z-50 mt-2 grid min-w-44 max-w-[calc(100vw-2rem)] gap-0.5 rounded-xl border border-border-default bg-surface-1 p-1.5 shadow-lg\",\n heading: \"px-2.5 pb-1 pt-1.5 text-[11px] font-semibold text-fg-secondary\",\n item:\n \"flex min-h-9 items-center justify-between gap-6 rounded-lg px-2.5 py-2 text-sm font-medium text-fg no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring aria-[current=true]:bg-accent-soft\",\n check: \"h-4 w-4 shrink-0 text-fg-accent\",\n inline: \"inline-flex items-center gap-1 rounded-full border border-border-subtle bg-surface-2/60 p-0.5\",\n inlineLink:\n \"inline-flex min-h-9 items-center rounded-full px-3 text-sm font-medium text-fg-secondary no-underline transition-colors hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=true]:bg-surface-3 aria-[current=true]:text-fg\",\n },\n});\nconst s = languageSwitch();\n\nexport type LanguageSwitchProps = {\n /** The control's accessible name (“Language”, “Langue”) — passed translated. */\n label: string;\n /** The code of the page's language (`fr`). */\n current: string;\n /** The languages offered, each with its name in its own language and the address of the same page (`languagesFor`). */\n languages: readonly Language[];\n /**\n * `menu`: a globe and the code, opening a menu (the bar). `inline`: every language in a row (a mobile panel, a settings page).\n * @default \"menu\"\n */\n variant?: \"menu\" | \"inline\";\n /** Your router's `<Link>` (it receives `href`, `hrefLang`, `lang`). A plain `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** Opens the menu at first render (a story, a screenshot). */\n defaultOpen?: boolean;\n /** Classes of the control. */\n className?: string;\n};\n\nconst Globe = ({ className }: { className?: string }) => (\n <svg viewBox=\"0 0 24 24\" className={className} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M3 12h18M12 3c2.5 2.6 3.8 5.6 3.8 9S14.5 18.4 12 21c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3Z\" />\n </svg>\n);\n\n/**\n * The language switch, the same on every Krizaka site and app: a compact control — a globe and the current code\n * (`FR`) — that opens a menu of the languages named in their own language (Français · English), each a link with\n * `hreflang` to the same page. It shows the language you are in, never the “other” one. `variant=\"inline\"` lays the\n * languages in a row for a phone's panel or a settings page.\n */\nexport function LanguageSwitch({ label, current, languages, variant = \"menu\", linkAs, defaultOpen = false, className }: LanguageSwitchProps) {\n const Link = linkAs ?? \"a\";\n const id = React.useId();\n const [open, setOpen] = React.useState(defaultOpen);\n const root = React.useRef<HTMLDivElement>(null);\n const trigger = React.useRef<HTMLButtonElement>(null);\n const active = languages.find((l) => l.code === current);\n\n React.useEffect(() => {\n if (!open) return;\n // A press outside closes it (the page under it stays usable).\n const onPress = (e: PointerEvent) => {\n if (e.target instanceof Node && !root.current?.contains(e.target)) setOpen(false);\n };\n document.addEventListener(\"pointerdown\", onPress);\n return () => document.removeEventListener(\"pointerdown\", onPress);\n }, [open]);\n\n if (variant === \"inline\") {\n return (\n <div role=\"group\" aria-label={label} className={s.inline({ className })}>\n {languages.map((l) => (\n <Link key={l.code} href={l.href} hrefLang={l.code} lang={l.code} aria-current={l.code === current ? \"true\" : undefined} className={s.inlineLink()}>\n {l.name}\n </Link>\n ))}\n </div>\n );\n }\n\n const links = () => [...(root.current?.querySelectorAll<HTMLElement>(\"[data-language]\") ?? [])];\n const close = (focus: boolean) => {\n setOpen(false);\n if (focus) trigger.current?.focus();\n };\n return (\n <div\n ref={root}\n className={s.root({ className })}\n onKeyDown={(e) => {\n if (e.key === \"Escape\" && open) {\n e.stopPropagation();\n close(true);\n }\n if (open && (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Home\" || e.key === \"End\")) {\n e.preventDefault();\n const all = links();\n const at = all.indexOf(document.activeElement as HTMLElement);\n const next = e.key === \"Home\" ? 0 : e.key === \"End\" ? all.length - 1 : (at + (e.key === \"ArrowDown\" ? 1 : -1) + all.length) % all.length;\n all[next]?.focus();\n }\n }}\n // Tab out of the switch closes it.\n onBlur={(e) => {\n if (open && !root.current?.contains(e.relatedTarget as Node | null)) setOpen(false);\n }}\n >\n <button\n ref={trigger}\n type=\"button\"\n aria-label={active ? `${label}: ${active.name}` : label}\n aria-expanded={open}\n aria-controls={id}\n onClick={() => setOpen(!open)}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setOpen(true);\n requestAnimationFrame(() => (links().find((l) => l.getAttribute(\"aria-current\") === \"true\") ?? links()[0])?.focus());\n }\n }}\n className={s.trigger()}\n >\n <Globe className={s.glyph()} />\n {/* Drawn by CSS: the code is no text of the button, so its name (`Language: English`) never mismatches what is visible. */}\n <span aria-hidden data-code={current} className=\"after:content-[attr(data-code)]\" />\n </button>\n {open && (\n <div id={id} role=\"group\" aria-label={label} className={s.panel()}>\n <p aria-hidden className={s.heading()}>\n {label}\n </p>\n {languages.map((l) => (\n <Link key={l.code} data-language=\"\" href={l.href} hrefLang={l.code} lang={l.code} aria-current={l.code === current ? \"true\" : undefined} className={s.item()}>\n {l.name}\n {l.code === current && (\n <svg viewBox=\"0 0 24 24\" className={s.check()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )}\n </Link>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/skip-link/skip-link.tsx"],"sourcesContent":["// Client (it moves focus). WCAG 2.4.1 Bypass Blocks: the first tab stop of a page, a link that skips the navigation and\n// lands on the content. Visually hidden until focused, then a pill above everything in the brand ring. The label is\n// passed translated; the target needs an `id` (and is made focusable, so focus really moves, router or not).\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const skipLink = tv({\n base:\n \"sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-full focus:bg-surface-1 focus:px-4 focus:py-2 \" +\n \"focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n});\n\nexport type SkipLinkProps = Omit<React.ComponentProps<\"a\">, \"href\"> & {\n /**\n * The content to land on: `#` and its `id`. The element becomes focusable (`tabindex=\"-1\"`) when it is not.\n * @default \"#content\"\n */\n href?: `#${string}`;\n};\n\n/** A \"skip to content\" link: the first tab stop, hidden until focused; Enter moves focus to the target. */\nexport function SkipLink({ href = \"#content\", className, onClick, children, ...props }: SkipLinkProps) {\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n const target = document.getElementById(decodeURIComponent(href.slice(1)));\n if (!target) return;\n if (!target.hasAttribute(\"tabindex\") && !target.matches(\"a[href], button, input, select, textarea, [contenteditable]\")) target.setAttribute(\"tabindex\", \"-1\");\n target.focus();\n };\n return (\n <a href={href} className={skipLink({ className })} onClick={handleClick} {...props}>\n {children}\n </a>\n );\n}\n"],"mappings":";;;AAIA,SAAS,UAAU;AA2Bf;AAzBG,IAAM,WAAW,GAAG;AAAA,EACzB,MACE;AAEJ,CAAC;AAWM,SAAS,SAAS,EAAE,OAAO,YAAY,WAAW,SAAS,UAAU,GAAG,MAAM,GAAkB;AACrG,QAAM,cAAc,CAAC,UAA+C;AAClE,cAAU,KAAK;AACf,QAAI,MAAM,iBAAkB;AAC5B,UAAM,SAAS,SAAS,eAAe,mBAAmB,KAAK,MAAM,CAAC,CAAC,CAAC;AACxE,QAAI,CAAC,OAAQ;AACb,QAAI,CAAC,OAAO,aAAa,UAAU,KAAK,CAAC,OAAO,QAAQ,6DAA6D,EAAG,QAAO,aAAa,YAAY,IAAI;AAC5J,WAAO,MAAM;AAAA,EACf;AACA,SACE,oBAAC,OAAE,MAAY,WAAW,SAAS,EAAE,UAAU,CAAC,GAAG,SAAS,aAAc,GAAG,OAC1E,UACH;AAEJ;","names":[]}