@kodffe/ui 0.0.0-stage → 0.10.1

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 (304) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +11 -0
  2. package/dist/accordion/accordion.d.ts +44 -0
  3. package/dist/accordion/accordion.js +84 -0
  4. package/dist/accordion/accordion.js.map +1 -0
  5. package/dist/alert/alert.d.ts +17 -0
  6. package/dist/alert/alert.js +59 -0
  7. package/dist/alert/alert.js.map +1 -0
  8. package/dist/app-shell/app-footer.d.ts +29 -0
  9. package/dist/app-shell/app-footer.js +39 -0
  10. package/dist/app-shell/app-footer.js.map +1 -0
  11. package/dist/app-shell/app-shell-context.d.ts +10 -0
  12. package/dist/app-shell/app-shell-context.js +12 -0
  13. package/dist/app-shell/app-shell-context.js.map +1 -0
  14. package/dist/app-shell/app-shell.d.ts +42 -0
  15. package/dist/app-shell/app-shell.js +191 -0
  16. package/dist/app-shell/app-shell.js.map +1 -0
  17. package/dist/app-shell/floating-actions.d.ts +19 -0
  18. package/dist/app-shell/floating-actions.js +59 -0
  19. package/dist/app-shell/floating-actions.js.map +1 -0
  20. package/dist/app-shell/workspace-switcher.d.ts +25 -0
  21. package/dist/app-shell/workspace-switcher.js +82 -0
  22. package/dist/app-shell/workspace-switcher.js.map +1 -0
  23. package/dist/badge/badge.d.ts +19 -0
  24. package/dist/badge/badge.js +59 -0
  25. package/dist/badge/badge.js.map +1 -0
  26. package/dist/banner/banner.d.ts +22 -0
  27. package/dist/banner/banner.js +80 -0
  28. package/dist/banner/banner.js.map +1 -0
  29. package/dist/breadcrumbs/breadcrumbs.d.ts +18 -0
  30. package/dist/breadcrumbs/breadcrumbs.js +45 -0
  31. package/dist/breadcrumbs/breadcrumbs.js.map +1 -0
  32. package/dist/button/button.d.ts +38 -0
  33. package/dist/button/button.js +83 -0
  34. package/dist/button/button.js.map +1 -0
  35. package/dist/card/card.d.ts +159 -0
  36. package/dist/card/card.js +390 -0
  37. package/dist/card/card.js.map +1 -0
  38. package/dist/card/insight-card.d.ts +19 -0
  39. package/dist/card/insight-card.js +51 -0
  40. package/dist/card/insight-card.js.map +1 -0
  41. package/dist/card/integration-card.d.ts +19 -0
  42. package/dist/card/integration-card.js +70 -0
  43. package/dist/card/integration-card.js.map +1 -0
  44. package/dist/charts/charts.d.ts +75 -0
  45. package/dist/charts/charts.js +453 -0
  46. package/dist/charts/charts.js.map +1 -0
  47. package/dist/choice/choice.d.ts +39 -0
  48. package/dist/choice/choice.js +143 -0
  49. package/dist/choice/choice.js.map +1 -0
  50. package/dist/combobox/combobox.d.ts +46 -0
  51. package/dist/combobox/combobox.js +196 -0
  52. package/dist/combobox/combobox.js.map +1 -0
  53. package/dist/command-palette/command-palette.d.ts +35 -0
  54. package/dist/command-palette/command-palette.js +164 -0
  55. package/dist/command-palette/command-palette.js.map +1 -0
  56. package/dist/confirm-dialog/confirm-dialog.d.ts +37 -0
  57. package/dist/confirm-dialog/confirm-dialog.js +106 -0
  58. package/dist/confirm-dialog/confirm-dialog.js.map +1 -0
  59. package/dist/content/assistant.d.ts +86 -0
  60. package/dist/content/assistant.js +213 -0
  61. package/dist/content/assistant.js.map +1 -0
  62. package/dist/content/content.d.ts +45 -0
  63. package/dist/content/content.js +136 -0
  64. package/dist/content/content.js.map +1 -0
  65. package/dist/data-display/avatar-sizes.d.ts +8 -0
  66. package/dist/data-display/avatar-sizes.js +12 -0
  67. package/dist/data-display/avatar-sizes.js.map +1 -0
  68. package/dist/data-display/avatar.d.ts +21 -0
  69. package/dist/data-display/avatar.js +53 -0
  70. package/dist/data-display/avatar.js.map +1 -0
  71. package/dist/data-display/chip.d.ts +20 -0
  72. package/dist/data-display/chip.js +64 -0
  73. package/dist/data-display/chip.js.map +1 -0
  74. package/dist/data-display/code-block.d.ts +14 -0
  75. package/dist/data-display/code-block.js +50 -0
  76. package/dist/data-display/code-block.js.map +1 -0
  77. package/dist/data-display/data-display.d.ts +73 -0
  78. package/dist/data-display/data-display.js +227 -0
  79. package/dist/data-display/data-display.js.map +1 -0
  80. package/dist/data-display/insights.d.ts +120 -0
  81. package/dist/data-display/insights.js +220 -0
  82. package/dist/data-display/insights.js.map +1 -0
  83. package/dist/data-table/cells.d.ts +86 -0
  84. package/dist/data-table/cells.js +163 -0
  85. package/dist/data-table/cells.js.map +1 -0
  86. package/dist/data-table/data-table.d.ts +112 -0
  87. package/dist/data-table/data-table.js +558 -0
  88. package/dist/data-table/data-table.js.map +1 -0
  89. package/dist/date-picker/date-picker.d.ts +61 -0
  90. package/dist/date-picker/date-picker.js +309 -0
  91. package/dist/date-picker/date-picker.js.map +1 -0
  92. package/dist/drawer/drawer.d.ts +32 -0
  93. package/dist/drawer/drawer.js +72 -0
  94. package/dist/drawer/drawer.js.map +1 -0
  95. package/dist/empty-state/empty-state.d.ts +25 -0
  96. package/dist/empty-state/empty-state.js +78 -0
  97. package/dist/empty-state/empty-state.js.map +1 -0
  98. package/dist/error-state/error-state.d.ts +19 -0
  99. package/dist/error-state/error-state.js +56 -0
  100. package/dist/error-state/error-state.js.map +1 -0
  101. package/dist/event-calendar/calendar-logic.test.d.ts +1 -0
  102. package/dist/event-calendar/dates.d.ts +35 -0
  103. package/dist/event-calendar/dates.js +55 -0
  104. package/dist/event-calendar/dates.js.map +1 -0
  105. package/dist/event-calendar/event-calendar.d.ts +125 -0
  106. package/dist/event-calendar/event-calendar.js +795 -0
  107. package/dist/event-calendar/event-calendar.js.map +1 -0
  108. package/dist/event-calendar/event-details.d.ts +50 -0
  109. package/dist/event-calendar/event-details.js +124 -0
  110. package/dist/event-calendar/event-details.js.map +1 -0
  111. package/dist/event-calendar/layout.d.ts +48 -0
  112. package/dist/event-calendar/layout.js +55 -0
  113. package/dist/event-calendar/layout.js.map +1 -0
  114. package/dist/file-upload/file-upload.d.ts +70 -0
  115. package/dist/file-upload/file-upload.js +307 -0
  116. package/dist/file-upload/file-upload.js.map +1 -0
  117. package/dist/filters/filters.d.ts +48 -0
  118. package/dist/filters/filters.js +103 -0
  119. package/dist/filters/filters.js.map +1 -0
  120. package/dist/form-field/form-field.d.ts +39 -0
  121. package/dist/form-field/form-field.js +88 -0
  122. package/dist/form-field/form-field.js.map +1 -0
  123. package/dist/hover-card/hover-card.d.ts +15 -0
  124. package/dist/hover-card/hover-card.js +27 -0
  125. package/dist/hover-card/hover-card.js.map +1 -0
  126. package/dist/hover-card/person-hover-card.d.ts +26 -0
  127. package/dist/hover-card/person-hover-card.js +85 -0
  128. package/dist/hover-card/person-hover-card.js.map +1 -0
  129. package/dist/icon/icon-tile.d.ts +20 -0
  130. package/dist/icon/icon-tile.js +55 -0
  131. package/dist/icon/icon-tile.js.map +1 -0
  132. package/dist/icon/icon.d.ts +14 -0
  133. package/dist/icon/icon.js +28 -0
  134. package/dist/icon/icon.js.map +1 -0
  135. package/dist/icon/icons.generated.d.ts +358 -0
  136. package/dist/icon/icons.generated.js +362 -0
  137. package/dist/icon/icons.generated.js.map +1 -0
  138. package/dist/index.d.ts +90 -0
  139. package/dist/index.js +88 -0
  140. package/dist/input/extra-inputs.d.ts +122 -0
  141. package/dist/input/extra-inputs.js +351 -0
  142. package/dist/input/extra-inputs.js.map +1 -0
  143. package/dist/input/field-styles.d.ts +5 -0
  144. package/dist/input/field-styles.js +14 -0
  145. package/dist/input/field-styles.js.map +1 -0
  146. package/dist/input/input.d.ts +50 -0
  147. package/dist/input/input.js +107 -0
  148. package/dist/input/input.js.map +1 -0
  149. package/dist/layout/layout.d.ts +41 -0
  150. package/dist/layout/layout.js +90 -0
  151. package/dist/layout/layout.js.map +1 -0
  152. package/dist/lib/control-styles.d.ts +9 -0
  153. package/dist/lib/control-styles.js +16 -0
  154. package/dist/lib/control-styles.js.map +1 -0
  155. package/dist/lib/cx.d.ts +2 -0
  156. package/dist/lib/cx.js +8 -0
  157. package/dist/lib/cx.js.map +1 -0
  158. package/dist/lib/glyphs.d.ts +13 -0
  159. package/dist/lib/glyphs.js +67 -0
  160. package/dist/lib/glyphs.js.map +1 -0
  161. package/dist/lib/segmented-styles.d.ts +8 -0
  162. package/dist/lib/segmented-styles.js +12 -0
  163. package/dist/lib/segmented-styles.js.map +1 -0
  164. package/dist/lib/select-on-arrow.d.ts +7 -0
  165. package/dist/lib/select-on-arrow.js +21 -0
  166. package/dist/lib/select-on-arrow.js.map +1 -0
  167. package/dist/lib/skip-link.d.ts +5 -0
  168. package/dist/lib/skip-link.js +13 -0
  169. package/dist/lib/skip-link.js.map +1 -0
  170. package/dist/lib/use-media-query.d.ts +4 -0
  171. package/dist/lib/use-media-query.js +15 -0
  172. package/dist/lib/use-media-query.js.map +1 -0
  173. package/dist/lists/lists.d.ts +76 -0
  174. package/dist/lists/lists.js +133 -0
  175. package/dist/lists/lists.js.map +1 -0
  176. package/dist/locale-switch/locale-switch.d.ts +29 -0
  177. package/dist/locale-switch/locale-switch.js +62 -0
  178. package/dist/locale-switch/locale-switch.js.map +1 -0
  179. package/dist/logo/logo.d.ts +31 -0
  180. package/dist/logo/logo.js +157 -0
  181. package/dist/logo/logo.js.map +1 -0
  182. package/dist/logo/product-logo.d.ts +19 -0
  183. package/dist/logo/product-logo.js +46 -0
  184. package/dist/logo/product-logo.js.map +1 -0
  185. package/dist/marketing/marketing.d.ts +176 -0
  186. package/dist/marketing/marketing.js +451 -0
  187. package/dist/marketing/marketing.js.map +1 -0
  188. package/dist/marketing/newsletter-form.d.ts +21 -0
  189. package/dist/marketing/newsletter-form.js +100 -0
  190. package/dist/marketing/newsletter-form.js.map +1 -0
  191. package/dist/mascot/mascot.d.ts +28 -0
  192. package/dist/mascot/mascot.js +28 -0
  193. package/dist/mascot/mascot.js.map +1 -0
  194. package/dist/mascot/mascots.generated.d.ts +284 -0
  195. package/dist/mascot/mascots.generated.js +287 -0
  196. package/dist/mascot/mascots.generated.js.map +1 -0
  197. package/dist/master-detail/master-detail.d.ts +30 -0
  198. package/dist/master-detail/master-detail.js +71 -0
  199. package/dist/master-detail/master-detail.js.map +1 -0
  200. package/dist/menu/account-menu.d.ts +44 -0
  201. package/dist/menu/account-menu.js +78 -0
  202. package/dist/menu/account-menu.js.map +1 -0
  203. package/dist/menu/context-menu.d.ts +31 -0
  204. package/dist/menu/context-menu.js +111 -0
  205. package/dist/menu/context-menu.js.map +1 -0
  206. package/dist/menu/dropdown-menu.d.ts +49 -0
  207. package/dist/menu/dropdown-menu.js +152 -0
  208. package/dist/menu/dropdown-menu.js.map +1 -0
  209. package/dist/menu/menu-styles.d.ts +8 -0
  210. package/dist/menu/menu-styles.js +6 -0
  211. package/dist/menu/menu-styles.js.map +1 -0
  212. package/dist/menu/menubar.d.ts +31 -0
  213. package/dist/menu/menubar.js +125 -0
  214. package/dist/menu/menubar.js.map +1 -0
  215. package/dist/modal/modal.d.ts +34 -0
  216. package/dist/modal/modal.js +58 -0
  217. package/dist/modal/modal.js.map +1 -0
  218. package/dist/notification-center/notification-center.d.ts +51 -0
  219. package/dist/notification-center/notification-center.js +244 -0
  220. package/dist/notification-center/notification-center.js.map +1 -0
  221. package/dist/otp-input/otp-input.d.ts +23 -0
  222. package/dist/otp-input/otp-input.js +61 -0
  223. package/dist/otp-input/otp-input.js.map +1 -0
  224. package/dist/overlay/overlay.d.ts +53 -0
  225. package/dist/overlay/overlay.js +103 -0
  226. package/dist/overlay/overlay.js.map +1 -0
  227. package/dist/page-header/page-header.d.ts +55 -0
  228. package/dist/page-header/page-header.js +114 -0
  229. package/dist/page-header/page-header.js.map +1 -0
  230. package/dist/page-header/record-headers.d.ts +51 -0
  231. package/dist/page-header/record-headers.js +134 -0
  232. package/dist/page-header/record-headers.js.map +1 -0
  233. package/dist/page-header/section-header.d.ts +21 -0
  234. package/dist/page-header/section-header.js +44 -0
  235. package/dist/page-header/section-header.js.map +1 -0
  236. package/dist/page-navigation/page-navigation.d.ts +59 -0
  237. package/dist/page-navigation/page-navigation.js +136 -0
  238. package/dist/page-navigation/page-navigation.js.map +1 -0
  239. package/dist/pagination/pagination.d.ts +28 -0
  240. package/dist/pagination/pagination.js +102 -0
  241. package/dist/pagination/pagination.js.map +1 -0
  242. package/dist/phone-input/phone-input.d.ts +34 -0
  243. package/dist/phone-input/phone-input.js +171 -0
  244. package/dist/phone-input/phone-input.js.map +1 -0
  245. package/dist/popover/popover.d.ts +23 -0
  246. package/dist/popover/popover.js +40 -0
  247. package/dist/popover/popover.js.map +1 -0
  248. package/dist/progress/progress.d.ts +21 -0
  249. package/dist/progress/progress.js +49 -0
  250. package/dist/progress/progress.js.map +1 -0
  251. package/dist/segmented-control/segmented-control.d.ts +32 -0
  252. package/dist/segmented-control/segmented-control.js +29 -0
  253. package/dist/segmented-control/segmented-control.js.map +1 -0
  254. package/dist/select/select.d.ts +48 -0
  255. package/dist/select/select.js +136 -0
  256. package/dist/select/select.js.map +1 -0
  257. package/dist/settings/permission-matrix.d.ts +29 -0
  258. package/dist/settings/permission-matrix.js +101 -0
  259. package/dist/settings/permission-matrix.js.map +1 -0
  260. package/dist/settings/settings-row.d.ts +32 -0
  261. package/dist/settings/settings-row.js +63 -0
  262. package/dist/settings/settings-row.js.map +1 -0
  263. package/dist/side-nav/side-nav.d.ts +36 -0
  264. package/dist/side-nav/side-nav.js +123 -0
  265. package/dist/side-nav/side-nav.js.map +1 -0
  266. package/dist/site-header/site-header.d.ts +32 -0
  267. package/dist/site-header/site-header.js +99 -0
  268. package/dist/site-header/site-header.js.map +1 -0
  269. package/dist/skeleton/skeleton.d.ts +16 -0
  270. package/dist/skeleton/skeleton.js +19 -0
  271. package/dist/skeleton/skeleton.js.map +1 -0
  272. package/dist/slider/slider.d.ts +15 -0
  273. package/dist/slider/slider.js +48 -0
  274. package/dist/slider/slider.js.map +1 -0
  275. package/dist/spinner/spinner.d.ts +15 -0
  276. package/dist/spinner/spinner.js +42 -0
  277. package/dist/spinner/spinner.js.map +1 -0
  278. package/dist/stepper/step-dots.d.ts +16 -0
  279. package/dist/stepper/step-dots.js +26 -0
  280. package/dist/stepper/step-dots.js.map +1 -0
  281. package/dist/stepper/stepper.d.ts +26 -0
  282. package/dist/stepper/stepper.js +113 -0
  283. package/dist/stepper/stepper.js.map +1 -0
  284. package/dist/tabs/tabs.d.ts +31 -0
  285. package/dist/tabs/tabs.js +66 -0
  286. package/dist/tabs/tabs.js.map +1 -0
  287. package/dist/templates/app-templates.d.ts +126 -0
  288. package/dist/templates/app-templates.js +153 -0
  289. package/dist/templates/app-templates.js.map +1 -0
  290. package/dist/templates/site-templates.d.ts +49 -0
  291. package/dist/templates/site-templates.js +110 -0
  292. package/dist/templates/site-templates.js.map +1 -0
  293. package/dist/toast/toast-store.d.ts +54 -0
  294. package/dist/toast/toast-store.js +49 -0
  295. package/dist/toast/toast-store.js.map +1 -0
  296. package/dist/toast/toaster.d.ts +15 -0
  297. package/dist/toast/toaster.js +124 -0
  298. package/dist/toast/toaster.js.map +1 -0
  299. package/dist/tooltip/tooltip.d.ts +29 -0
  300. package/dist/tooltip/tooltip.js +54 -0
  301. package/dist/tooltip/tooltip.js.map +1 -0
  302. package/package.json +88 -3
  303. package/styles.css +521 -0
  304. package/README.md +0 -3
@@ -0,0 +1,76 @@
1
+ import type { ElementType, ReactNode } from 'react';
2
+ import type { IconName } from '../icon/icon';
3
+ export interface Stat {
4
+ label: string;
5
+ value: ReactNode;
6
+ /** A small line under the value ("+2 vs last cycle"). */
7
+ sub?: ReactNode;
8
+ /** A "View" link under the stat. */
9
+ href?: string;
10
+ linkLabel?: string;
11
+ }
12
+ export interface StatGridProps {
13
+ stats: Stat[];
14
+ /** Columns from 1024px; defaults to the number of stats (max 4). Two columns from 640px. */
15
+ columns?: 2 | 3 | 4;
16
+ linkAs?: ElementType;
17
+ className?: string;
18
+ }
19
+ /**
20
+ * Several small stats in one card, with dividers between them ("For you", a client's summary). For a single big
21
+ * number with a trend use `MetricCard`.
22
+ */
23
+ export declare function StatGrid({ stats, columns, linkAs: Link, className }: StatGridProps): import("react").JSX.Element;
24
+ export interface ActionListProps {
25
+ children: ReactNode;
26
+ /** `plain` (default): rows split by lines, for inside a card. `bordered`: its own rounded border (sessions, devices). */
27
+ appearance?: 'plain' | 'bordered';
28
+ 'aria-label'?: string;
29
+ className?: string;
30
+ }
31
+ /** Rows of things to act on: tasks, recent kudos, suggestions, templates, connected devices. */
32
+ export declare function ActionList({ children, appearance, className, ...rest }: ActionListProps): import("react").JSX.Element;
33
+ export interface ActionListItemProps {
34
+ title: ReactNode;
35
+ /** A small line under the title ("Due tomorrow · Operations"). */
36
+ meta?: ReactNode;
37
+ /** An avatar or any leading media. */
38
+ media?: ReactNode;
39
+ /** An icon tile instead of `media`. */
40
+ icon?: IconName;
41
+ /** Trailing content: a button ("Set up"), a value ("gave 13 · received 13") or a badge. */
42
+ action?: ReactNode;
43
+ /** Makes the title a link to the item. */
44
+ href?: string;
45
+ linkAs?: ElementType;
46
+ className?: string;
47
+ }
48
+ export declare function ActionListItem({ title, meta, media, icon, action, href, linkAs: Link, className, }: ActionListItemProps): import("react").JSX.Element;
49
+ /** A list with check icons ("Includes", plan features, onboarding steps done). `PlanCard` uses it for its features. */
50
+ export declare function CheckList({ items, tone, marker, className, }: {
51
+ items: ReactNode[];
52
+ tone?: 'success' | 'primary';
53
+ /** `circle`: the check in a tinted circle. `plain`: the check alone (dense lists, plan features). */
54
+ marker?: 'circle' | 'plain';
55
+ className?: string;
56
+ }): import("react").JSX.Element;
57
+ export interface Device {
58
+ id: string;
59
+ name: string;
60
+ icon?: IconName;
61
+ /** "Quito · Last used 2 days ago" */
62
+ meta?: string;
63
+ /** This device: shown with a badge instead of "Disconnect". */
64
+ current?: boolean;
65
+ }
66
+ /** Connected devices or sessions with their last use and "Disconnect" (security settings). */
67
+ export declare function DeviceList({ devices, onDisconnect, labels, className, }: {
68
+ devices: Device[];
69
+ onDisconnect: (id: string) => void;
70
+ labels?: {
71
+ current?: string;
72
+ disconnect?: string;
73
+ list?: string;
74
+ };
75
+ className?: string;
76
+ }): import("react").JSX.Element;
@@ -0,0 +1,133 @@
1
+ import { cx as e } from "../lib/cx.js";
2
+ import { Button as t } from "../button/button.js";
3
+ import { Glyph as n } from "../lib/glyphs.js";
4
+ import { IconTile as r } from "../icon/icon-tile.js";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
+ //#region src/lists/lists.tsx
7
+ var o = {
8
+ 2: "lg:grid-cols-2",
9
+ 3: "lg:grid-cols-3",
10
+ 4: "lg:grid-cols-4"
11
+ };
12
+ function s({ stats: t, columns: r, linkAs: s = "a", className: c }) {
13
+ let l = r ?? Math.min(Math.max(t.length, 2), 4);
14
+ return /* @__PURE__ */ i("dl", {
15
+ className: e("m-0 grid overflow-hidden rounded-lg border border-border bg-surface font-sans sm:grid-cols-2", o[l], c),
16
+ children: t.map((e) => /* @__PURE__ */ a("div", {
17
+ className: "grid content-start gap-1 p-card shadow-[1px_0_0_var(--kf-border),0_1px_0_var(--kf-border)]",
18
+ children: [
19
+ /* @__PURE__ */ i("dt", {
20
+ className: "text-sm text-fg-muted",
21
+ children: e.label
22
+ }),
23
+ /* @__PURE__ */ i("dd", {
24
+ className: "m-0 font-display text-2xl font-bold tracking-tight text-fg tabular-nums",
25
+ children: e.value
26
+ }),
27
+ e.sub && /* @__PURE__ */ i("dd", {
28
+ className: "m-0 text-xs text-fg-muted",
29
+ children: e.sub
30
+ }),
31
+ e.href && /* @__PURE__ */ i("dd", {
32
+ className: "m-0 mt-1",
33
+ children: /* @__PURE__ */ a(s, {
34
+ href: e.href,
35
+ className: "inline-flex items-center gap-1 rounded-xs text-sm font-semibold text-link no-underline hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus",
36
+ children: [e.linkLabel ?? "View", /* @__PURE__ */ i(n, {
37
+ name: "chevron-right",
38
+ size: 14
39
+ })]
40
+ })
41
+ })
42
+ ]
43
+ }, e.label))
44
+ });
45
+ }
46
+ function c({ children: t, appearance: n = "plain", className: r, ...a }) {
47
+ return /* @__PURE__ */ i("ul", {
48
+ ...a,
49
+ className: e("m-0 list-none divide-y divide-border p-0 font-sans", n === "bordered" && "rounded-lg border border-border bg-surface px-4", r),
50
+ children: t
51
+ });
52
+ }
53
+ function l({ title: t, meta: n, media: o, icon: s, action: c, href: l, linkAs: u = "a", className: d }) {
54
+ return /* @__PURE__ */ a("li", {
55
+ className: e("flex items-center gap-3 py-3", d),
56
+ children: [
57
+ o ?? (s && /* @__PURE__ */ i(r, {
58
+ icon: s,
59
+ size: "sm",
60
+ tone: "muted"
61
+ })),
62
+ /* @__PURE__ */ a("div", {
63
+ className: "grid min-w-0 flex-1 gap-0.5",
64
+ children: [l ? /* @__PURE__ */ i(u, {
65
+ href: l,
66
+ className: "truncate rounded-xs text-[15px] font-semibold text-fg no-underline hover:text-link hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus",
67
+ children: t
68
+ }) : /* @__PURE__ */ i("span", {
69
+ className: "truncate text-[15px] font-semibold text-fg",
70
+ children: t
71
+ }), n && /* @__PURE__ */ i("span", {
72
+ className: "truncate text-sm text-fg-muted",
73
+ children: n
74
+ })]
75
+ }),
76
+ c && /* @__PURE__ */ i("div", {
77
+ className: "flex shrink-0 items-center gap-2 text-sm text-fg-muted",
78
+ children: c
79
+ })
80
+ ]
81
+ });
82
+ }
83
+ function u({ items: t, tone: r = "success", marker: o = "circle", className: s }) {
84
+ let c = r === "success" ? "text-success-icon" : "text-primary";
85
+ return /* @__PURE__ */ i("ul", {
86
+ className: e("m-0 grid list-none p-0 font-sans text-[15px] text-fg", o === "plain" ? "gap-2.5" : "gap-2", s),
87
+ children: t.map((t, s) => /* @__PURE__ */ a("li", {
88
+ className: "flex items-start gap-2.5",
89
+ children: [o === "plain" ? /* @__PURE__ */ i(n, {
90
+ name: "check",
91
+ size: 18,
92
+ className: e("mt-0.5 shrink-0", c)
93
+ }) : /* @__PURE__ */ i("span", {
94
+ "aria-hidden": "true",
95
+ className: e("mt-0.5 grid size-5 shrink-0 place-items-center rounded-full", r === "success" ? "bg-success-surface" : "bg-info-surface", c),
96
+ children: /* @__PURE__ */ i(n, {
97
+ name: "check",
98
+ size: 12,
99
+ strokeWidth: 3
100
+ })
101
+ }), /* @__PURE__ */ i("span", {
102
+ className: "min-w-0",
103
+ children: t
104
+ })]
105
+ }, s))
106
+ });
107
+ }
108
+ function d({ devices: e, onDisconnect: n, labels: r = {}, className: a }) {
109
+ return /* @__PURE__ */ i(c, {
110
+ appearance: "bordered",
111
+ "aria-label": r.list ?? "Devices",
112
+ className: a,
113
+ children: e.map((e) => /* @__PURE__ */ i(l, {
114
+ icon: e.icon ?? "c-website",
115
+ title: e.name,
116
+ meta: e.meta,
117
+ action: e.current ? /* @__PURE__ */ i("span", {
118
+ className: "rounded-full bg-success-surface px-2 py-0.5 text-xs font-semibold text-success-fg",
119
+ children: r.current ?? "This device"
120
+ }) : /* @__PURE__ */ i(t, {
121
+ size: "sm",
122
+ variant: "ghost",
123
+ onClick: () => n(e.id),
124
+ "aria-label": `${r.disconnect ?? "Disconnect"} ${e.name}`,
125
+ children: r.disconnect ?? "Disconnect"
126
+ })
127
+ }, e.id))
128
+ });
129
+ }
130
+ //#endregion
131
+ export { c as ActionList, l as ActionListItem, u as CheckList, d as DeviceList, s as StatGrid };
132
+
133
+ //# sourceMappingURL=lists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lists.js","names":["COLS","StatGrid","Link","cols","columns","stats","cx","className","s","Glyph","ActionList","rest","appearance","ActionListItem","media","icon","IconTile","href","title","meta","action","CheckList","color","tone","marker","items","item","i","DeviceList","labels","devices","d","Button","onDisconnect"],"sources":["../../src/lists/lists.tsx"],"sourcesContent":["import type { ElementType, ReactNode } from 'react';\nimport { cx } from '../lib/cx';\nimport { IconTile } from '../icon/icon-tile';\nimport { Glyph } from '../lib/glyphs';\nimport type { IconName } from '../icon/icon';\nimport { Button } from '../button/button';\n\n/* ---------------- StatGrid ---------------- */\n\nexport interface Stat {\n label: string;\n value: ReactNode;\n /** A small line under the value (\"+2 vs last cycle\"). */\n sub?: ReactNode;\n /** A \"View\" link under the stat. */\n href?: string;\n linkLabel?: string;\n}\n\nexport interface StatGridProps {\n stats: Stat[];\n /** Columns from 1024px; defaults to the number of stats (max 4). Two columns from 640px. */\n columns?: 2 | 3 | 4;\n linkAs?: ElementType;\n className?: string;\n}\n\nconst COLS = { 2: 'lg:grid-cols-2', 3: 'lg:grid-cols-3', 4: 'lg:grid-cols-4' };\n\n/**\n * Several small stats in one card, with dividers between them (\"For you\", a client's summary). For a single big\n * number with a trend use `MetricCard`.\n */\nexport function StatGrid({ stats, columns, linkAs: Link = 'a', className }: StatGridProps) {\n const cols = columns ?? (Math.min(Math.max(stats.length, 2), 4) as 2 | 3 | 4);\n return (\n // Each stat draws its right and bottom divider as a shadow; the outer ones are clipped by the card, so empty\n // cells in the last row stay plain surface instead of a gray block.\n <dl\n className={cx(\n 'm-0 grid overflow-hidden rounded-lg border border-border bg-surface font-sans sm:grid-cols-2',\n COLS[cols],\n className,\n )}\n >\n {stats.map((s) => (\n <div\n key={s.label}\n className=\"grid content-start gap-1 p-card shadow-[1px_0_0_var(--kf-border),0_1px_0_var(--kf-border)]\"\n >\n <dt className=\"text-sm text-fg-muted\">{s.label}</dt>\n <dd className=\"m-0 font-display text-2xl font-bold tracking-tight text-fg tabular-nums\">{s.value}</dd>\n {s.sub && <dd className=\"m-0 text-xs text-fg-muted\">{s.sub}</dd>}\n {s.href && (\n <dd className=\"m-0 mt-1\">\n <Link\n href={s.href}\n className=\"inline-flex items-center gap-1 rounded-xs text-sm font-semibold text-link no-underline hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus\"\n >\n {s.linkLabel ?? 'View'}\n <Glyph name=\"chevron-right\" size={14} />\n </Link>\n </dd>\n )}\n </div>\n ))}\n </dl>\n );\n}\n\n/* ---------------- ActionList ---------------- */\n\nexport interface ActionListProps {\n children: ReactNode;\n /** `plain` (default): rows split by lines, for inside a card. `bordered`: its own rounded border (sessions, devices). */\n appearance?: 'plain' | 'bordered';\n 'aria-label'?: string;\n className?: string;\n}\n\n/** Rows of things to act on: tasks, recent kudos, suggestions, templates, connected devices. */\nexport function ActionList({ children, appearance = 'plain', className, ...rest }: ActionListProps) {\n return (\n <ul\n {...rest}\n className={cx(\n 'm-0 list-none divide-y divide-border p-0 font-sans',\n appearance === 'bordered' && 'rounded-lg border border-border bg-surface px-4',\n className,\n )}\n >\n {children}\n </ul>\n );\n}\n\nexport interface ActionListItemProps {\n title: ReactNode;\n /** A small line under the title (\"Due tomorrow · Operations\"). */\n meta?: ReactNode;\n /** An avatar or any leading media. */\n media?: ReactNode;\n /** An icon tile instead of `media`. */\n icon?: IconName;\n /** Trailing content: a button (\"Set up\"), a value (\"gave 13 · received 13\") or a badge. */\n action?: ReactNode;\n /** Makes the title a link to the item. */\n href?: string;\n linkAs?: ElementType;\n className?: string;\n}\n\nexport function ActionListItem({\n title,\n meta,\n media,\n icon,\n action,\n href,\n linkAs: Link = 'a',\n className,\n}: ActionListItemProps) {\n return (\n <li className={cx('flex items-center gap-3 py-3', className)}>\n {media ?? (icon && <IconTile icon={icon} size=\"sm\" tone=\"muted\" />)}\n <div className=\"grid min-w-0 flex-1 gap-0.5\">\n {href ? (\n <Link\n href={href}\n className=\"truncate rounded-xs text-[15px] font-semibold text-fg no-underline hover:text-link hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus\"\n >\n {title}\n </Link>\n ) : (\n <span className=\"truncate text-[15px] font-semibold text-fg\">{title}</span>\n )}\n {meta && <span className=\"truncate text-sm text-fg-muted\">{meta}</span>}\n </div>\n {action && <div className=\"flex shrink-0 items-center gap-2 text-sm text-fg-muted\">{action}</div>}\n </li>\n );\n}\n\n/* ---------------- CheckList ---------------- */\n\n/** A list with check icons (\"Includes\", plan features, onboarding steps done). `PlanCard` uses it for its features. */\nexport function CheckList({\n items,\n tone = 'success',\n marker = 'circle',\n className,\n}: {\n items: ReactNode[];\n tone?: 'success' | 'primary';\n /** `circle`: the check in a tinted circle. `plain`: the check alone (dense lists, plan features). */\n marker?: 'circle' | 'plain';\n className?: string;\n}) {\n const color = tone === 'success' ? 'text-success-icon' : 'text-primary';\n return (\n <ul\n className={cx(\n 'm-0 grid list-none p-0 font-sans text-[15px] text-fg',\n marker === 'plain' ? 'gap-2.5' : 'gap-2',\n className,\n )}\n >\n {items.map((item, i) => (\n <li key={i} className=\"flex items-start gap-2.5\">\n {marker === 'plain' ? (\n <Glyph name=\"check\" size={18} className={cx('mt-0.5 shrink-0', color)} />\n ) : (\n <span\n aria-hidden=\"true\"\n className={cx(\n 'mt-0.5 grid size-5 shrink-0 place-items-center rounded-full',\n tone === 'success' ? 'bg-success-surface' : 'bg-info-surface',\n color,\n )}\n >\n <Glyph name=\"check\" size={12} strokeWidth={3} />\n </span>\n )}\n <span className=\"min-w-0\">{item}</span>\n </li>\n ))}\n </ul>\n );\n}\n\n/* ---------------- DeviceList ---------------- */\n\nexport interface Device {\n id: string;\n name: string;\n icon?: IconName;\n /** \"Quito · Last used 2 days ago\" */\n meta?: string;\n /** This device: shown with a badge instead of \"Disconnect\". */\n current?: boolean;\n}\n\n/** Connected devices or sessions with their last use and \"Disconnect\" (security settings). */\nexport function DeviceList({\n devices,\n onDisconnect,\n labels = {},\n className,\n}: {\n devices: Device[];\n onDisconnect: (id: string) => void;\n labels?: { current?: string; disconnect?: string; list?: string };\n className?: string;\n}) {\n return (\n <ActionList appearance=\"bordered\" aria-label={labels.list ?? 'Devices'} className={className}>\n {devices.map((d) => (\n <ActionListItem\n key={d.id}\n icon={d.icon ?? 'c-website'}\n title={d.name}\n meta={d.meta}\n action={\n d.current ? (\n <span className=\"rounded-full bg-success-surface px-2 py-0.5 text-xs font-semibold text-success-fg\">\n {labels.current ?? 'This device'}\n </span>\n ) : (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => onDisconnect(d.id)}\n aria-label={`${labels.disconnect ?? 'Disconnect'} ${d.name}`}\n >\n {labels.disconnect ?? 'Disconnect'}\n </Button>\n )\n }\n />\n ))}\n </ActionList>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { ElementType } from 'react';
2
+ export interface LocaleOption {
3
+ /** BCP 47 code ("en", "es"), used for `hreflang` and `lang`. */
4
+ code: string;
5
+ /** Short label shown in the segmented form ("EN"). */
6
+ short: string;
7
+ /** Full name in its own language ("English", "Español"). */
8
+ label: string;
9
+ /** The same page in this language. */
10
+ href: string;
11
+ }
12
+ export interface LocaleSwitchProps {
13
+ locales: LocaleOption[];
14
+ /** The current page's locale code. */
15
+ current: string;
16
+ /** `segmented` (headers): ES / EN links in one pill. `select` (footers): a compact dropdown with full names. */
17
+ variant?: 'segmented' | 'select';
18
+ /** Link component (e.g. Next.js `Link`). */
19
+ linkAs?: ElementType;
20
+ /** `select` only: navigate with your router; defaults to a full page load of the option's `href`. */
21
+ onNavigate?: (option: LocaleOption) => void;
22
+ label?: string;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * Switches the page's language. Links, not radio buttons: each language is the same page at another URL. No flags:
27
+ * a flag is a country, not a language (ADR-0013, decision 3).
28
+ */
29
+ export declare function LocaleSwitch({ locales, current, variant, linkAs: Link, onNavigate, label, className, }: LocaleSwitchProps): import("react").JSX.Element;
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { cx as e } from "../lib/cx.js";
3
+ import { Glyph as t } from "../lib/glyphs.js";
4
+ import { DropdownMenu as n, DropdownMenuContent as r, DropdownMenuRadioGroup as i, DropdownMenuRadioItem as a, DropdownMenuTrigger as o } from "../menu/dropdown-menu.js";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/locale-switch/locale-switch.tsx
7
+ function l({ locales: l, current: u, variant: d = "segmented", linkAs: f = "a", onNavigate: p, label: m = "Language", className: h }) {
8
+ let g = l.find((e) => e.code === u) ?? l[0];
9
+ return d === "select" ? /* @__PURE__ */ c(n, { children: [/* @__PURE__ */ s(o, {
10
+ asChild: !0,
11
+ children: /* @__PURE__ */ c("button", {
12
+ type: "button",
13
+ className: e("inline-flex min-h-9 cursor-pointer items-center gap-1.5 rounded-full border border-border bg-surface px-3 font-sans text-sm font-semibold text-fg hover:border-fg-muted/50", "focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus pointer-coarse:min-h-11", h),
14
+ children: [
15
+ /* @__PURE__ */ c("span", {
16
+ className: "sr-only",
17
+ children: [m, ": "]
18
+ }),
19
+ g?.label,
20
+ /* @__PURE__ */ s(t, {
21
+ name: "chevron-down",
22
+ size: 14
23
+ })
24
+ ]
25
+ })
26
+ }), /* @__PURE__ */ s(r, {
27
+ align: "start",
28
+ children: /* @__PURE__ */ s(i, {
29
+ value: u,
30
+ onValueChange: (e) => {
31
+ let t = l.find((t) => t.code === e);
32
+ t && (p ?? ((e) => window.location.assign(e.href)))(t);
33
+ },
34
+ children: l.map((e) => /* @__PURE__ */ s(a, {
35
+ value: e.code,
36
+ lang: e.code,
37
+ children: e.label
38
+ }, e.code))
39
+ })
40
+ })] }) : /* @__PURE__ */ s("nav", {
41
+ "aria-label": m,
42
+ className: e("inline-flex gap-0.5 rounded-full border border-border bg-surface p-1 font-sans", h),
43
+ children: l.map((t) => {
44
+ let n = t.code === u;
45
+ return /* @__PURE__ */ s(f, {
46
+ href: t.href,
47
+ hrefLang: t.code,
48
+ lang: t.code,
49
+ "aria-current": n ? "true" : void 0,
50
+ className: e("inline-grid min-h-8 min-w-10 place-items-center rounded-full px-2.5 text-xs font-bold no-underline pointer-coarse:min-h-11", "focus-visible:outline-3 focus-visible:outline-offset-1 focus-visible:outline-focus", n ? "bg-primary text-on-primary" : "text-fg-muted hover:text-fg"),
51
+ children: /* @__PURE__ */ c("span", { children: [t.short, /* @__PURE__ */ c("span", {
52
+ className: "sr-only",
53
+ children: [" · ", t.label]
54
+ })] })
55
+ }, t.code);
56
+ })
57
+ });
58
+ }
59
+ //#endregion
60
+ export { l as LocaleSwitch };
61
+
62
+ //# sourceMappingURL=locale-switch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locale-switch.js","names":["LocaleSwitch","Link","active","locales","l","current","variant","DropdownMenu","DropdownMenuTrigger","cx","className","label","Glyph","DropdownMenuContent","DropdownMenuRadioGroup","o","code","onNavigate","x","DropdownMenuRadioItem","on"],"sources":["../../src/locale-switch/locale-switch.tsx"],"sourcesContent":["'use client';\nimport type { ElementType } from 'react';\nimport { cx } from '../lib/cx';\nimport { Glyph } from '../lib/glyphs';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from '../menu/dropdown-menu';\n\n/* ---------------- LocaleSwitch ---------------- */\n\nexport interface LocaleOption {\n /** BCP 47 code (\"en\", \"es\"), used for `hreflang` and `lang`. */\n code: string;\n /** Short label shown in the segmented form (\"EN\"). */\n short: string;\n /** Full name in its own language (\"English\", \"Español\"). */\n label: string;\n /** The same page in this language. */\n href: string;\n}\n\nexport interface LocaleSwitchProps {\n locales: LocaleOption[];\n /** The current page's locale code. */\n current: string;\n /** `segmented` (headers): ES / EN links in one pill. `select` (footers): a compact dropdown with full names. */\n variant?: 'segmented' | 'select';\n /** Link component (e.g. Next.js `Link`). */\n linkAs?: ElementType;\n /** `select` only: navigate with your router; defaults to a full page load of the option's `href`. */\n onNavigate?: (option: LocaleOption) => void;\n label?: string;\n className?: string;\n}\n\n/**\n * Switches the page's language. Links, not radio buttons: each language is the same page at another URL. No flags:\n * a flag is a country, not a language (ADR-0013, decision 3).\n */\nexport function LocaleSwitch({\n locales,\n current,\n variant = 'segmented',\n linkAs: Link = 'a',\n onNavigate,\n label = 'Language',\n className,\n}: LocaleSwitchProps) {\n const active = locales.find((l) => l.code === current) ?? locales[0];\n if (variant === 'select') {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className={cx(\n 'inline-flex min-h-9 cursor-pointer items-center gap-1.5 rounded-full border border-border bg-surface px-3 font-sans text-sm font-semibold text-fg hover:border-fg-muted/50',\n 'focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus pointer-coarse:min-h-11',\n className,\n )}\n >\n <span className=\"sr-only\">{label}: </span>\n {active?.label}\n <Glyph name=\"chevron-down\" size={14} />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuRadioGroup\n value={current}\n onValueChange={(code) => {\n const o = locales.find((l) => l.code === code);\n if (o) (onNavigate ?? ((x: LocaleOption) => window.location.assign(x.href)))(o);\n }}\n >\n {locales.map((l) => (\n <DropdownMenuRadioItem key={l.code} value={l.code} lang={l.code}>\n {l.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n }\n return (\n <nav\n aria-label={label}\n className={cx('inline-flex gap-0.5 rounded-full border border-border bg-surface p-1 font-sans', className)}\n >\n {locales.map((l) => {\n const on = l.code === current;\n return (\n <Link\n key={l.code}\n href={l.href}\n hrefLang={l.code}\n lang={l.code}\n aria-current={on ? 'true' : undefined}\n className={cx(\n 'inline-grid min-h-8 min-w-10 place-items-center rounded-full px-2.5 text-xs font-bold no-underline pointer-coarse:min-h-11',\n 'focus-visible:outline-3 focus-visible:outline-offset-1 focus-visible:outline-focus',\n on ? 'bg-primary text-on-primary' : 'text-fg-muted hover:text-fg',\n )}\n >\n {/* Visible \"EN\" stays part of the accessible name (WCAG 2.5.3). */}\n <span>\n {l.short}\n <span className=\"sr-only\"> · {l.label}</span>\n </span>\n </Link>\n );\n })}\n </nav>\n );\n}\n"],"mappings":";;;;;;AA2CA,SAAgBA,EAAa,EAC3B,YACA,YACA,aAAU,aACV,QAAQC,IAAO,KACf,eACA,WAAQ,YACR,gBACoB;CACpB,IAAMC,IAASC,EAAQ,MAAM,MAAMC,EAAE,SAASC,CAAO,KAAKF,EAAQ;CAoClE,OAnCIG,MAAY,WAEZ,kBAACC,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD;EAAqB,SAAA;EACnB,UAAA,kBAAC,UAAD;GACE,MAAK;GACL,WAAWC,EACT,8KACA,8GACAC,CACF;GANF,UAAA;IAQE,kBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CAA2BC,GAAM,IAAQ;;IACxCT,GAAQ;IACT,kBAACU,GAAD;KAAO,MAAK;KAAe,MAAM;IAAK,CAAA;GAChC;;CACW,CAAA,GACrB,kBAACC,GAAD;EAAqB,OAAM;EACzB,UAAA,kBAACC,GAAD;GACE,OAAOT;GACP,gBAAgB,MAAS;IACvB,IAAMU,IAAIZ,EAAQ,MAAM,MAAMC,EAAE,SAASY,CAAI;IAC7C,AAAID,MAAIE,OAAgB,MAAoB,OAAO,SAAS,OAAOC,EAAE,IAAI,GAAA,CAAIH,CAAC;GAChF;GAEC,UAAAZ,EAAQ,KAAK,MACZ,kBAACgB,GAAD;IAAoC,OAAOf,EAAE;IAAM,MAAMA,EAAE;IACxD,UAAAA,EAAE;GACkB,GAFKA,EAAE,IAEP,CACxB;EACqB,CAAA;CACL,CAAA,CACT,EAAA,CAAA,IAIhB,kBAAC,OAAD;EACE,cAAYO;EACZ,WAAWF,EAAG,kFAAkFC,CAAS;EAExG,UAAAP,EAAQ,KAAK,MAAM;GAClB,IAAMiB,IAAKhB,EAAE,SAASC;GACtB,OACE,kBAACJ,GAAD;IAEE,MAAMG,EAAE;IACR,UAAUA,EAAE;IACZ,MAAMA,EAAE;IACR,gBAAcgB,IAAK,SAAS,KAAA;IAC5B,WAAWX,EACT,8HACA,sFACAW,IAAK,+BAA+B,6BACtC;IAGA,UAAA,kBAAC,QAAD,EAAA,UAAA,CACGhB,EAAE,OACH,kBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CAA0B,OAAIA,EAAE,KAAY;IACxC,CAAA,CAAA,EAAA,CAAA;GACF,GAhBCA,EAAE,IAgBH;EAEV,CAAC;CACE,CAAA;AAET"}
@@ -0,0 +1,31 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export declare const logoVariants: readonly ["full", "mark", "lockup", "wordmark"];
3
+ export type LogoVariant = (typeof logoVariants)[number];
4
+ export interface LogoProps extends HTMLAttributes<HTMLSpanElement> {
5
+ /**
6
+ * - `full` (default): KODFFE, the eye is the O and the open D is the D. Minimum width 96 px.
7
+ * - `mark`: the eye and open D alone (avatars, favicon, the collapsed header). Minimum size 16 px.
8
+ * - `lockup`: the mark next to the plain wordmark, for wide formats. Minimum width 96 px.
9
+ * - `wordmark`: the letters alone.
10
+ */
11
+ variant?: LogoVariant;
12
+ /**
13
+ * `scroll` (`full` only): for sticky headers. The full logo shows at the top of the page and collapses to the mark
14
+ * after 80 px of scroll; hovering or focusing the logo's link brings the name back. `none` (default): static.
15
+ */
16
+ collapse?: 'none' | 'scroll';
17
+ /** Heavier strokes (x1.3) and a bigger pupil for the mark at 32 px and under. Only for `mark`. */
18
+ small?: boolean;
19
+ /**
20
+ * Classes for the eye ring and open D only: `text-primary` follows the theme; `text-mint-500` suits a fixed dark
21
+ * background. The letters and the pupil take the inherited text color.
22
+ */
23
+ markClassName?: string;
24
+ }
25
+ /**
26
+ * The Kodffe logo in every layout, option B "Open D" (docs/adr/0012-open-d-logo.md). Size it with `font-size`
27
+ * (e.g. `text-2xl`). Always named "Kodffe". The `full` logo is built from three pieces (K | mark | F F E) so the mark
28
+ * can stay while the letters fade; only transform and opacity animate (no layout shift). Do not outline it, add
29
+ * shadows or change the proportions.
30
+ */
31
+ export declare function Logo({ variant, collapse, small, markClassName, className, style, ...rest }: LogoProps): import("react").JSX.Element;
@@ -0,0 +1,157 @@
1
+ "use client";
2
+ import { cx as e } from "../lib/cx.js";
3
+ import { Fragment as t, jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { useEffect as i, useState as a } from "react";
5
+ //#region src/logo/logo.tsx
6
+ var o = [
7
+ "full",
8
+ "mark",
9
+ "lockup",
10
+ "wordmark"
11
+ ], s = "M3.5 0V-36H9.53V-19.3L23.9 -36H31.3L15.83 -18.35L32.35 0H24.7L9.53 -16.8V0Z", c = "M93.44 0V-36H115.12V-29.98H99.47V-21H112.12V-15H99.47V0ZM120.12 0V-36H141.79V-29.98H126.14V-21H138.79V-15H126.14V0ZM146.79 0V-36H170.04V-30.35H152.82V-21.48H167.04V-15.83H152.82V-5.65H170.04V0Z", l = "M3.5 0V-36H9.53V-19.3L23.9 -36H31.3L15.83 -18.35L32.35 0H24.7L9.53 -16.8V0ZM49.65 0.75Q44.25 0.75 40.4 -1.61Q36.55 -3.98 34.49 -8.2Q32.42 -12.43 32.42 -18Q32.42 -23.58 34.49 -27.8Q36.55 -32.02 40.4 -34.39Q44.25 -36.75 49.65 -36.75Q55.05 -36.75 58.91 -34.39Q62.78 -32.02 64.84 -27.8Q66.9 -23.58 66.9 -18Q66.9 -12.43 64.84 -8.2Q62.78 -3.98 58.91 -1.61Q55.05 0.75 49.65 0.75ZM49.65 -4.93Q53.28 -4.9 55.69 -6.53Q58.1 -8.15 59.31 -11.1Q60.53 -14.05 60.53 -18Q60.53 -21.95 59.31 -24.86Q58.1 -27.78 55.69 -29.4Q53.28 -31.03 49.65 -31.08Q46.03 -31.1 43.62 -29.48Q41.23 -27.85 40.01 -24.9Q38.8 -21.95 38.8 -18Q38.8 -14.05 40 -11.14Q41.2 -8.22 43.61 -6.6Q46.03 -4.98 49.65 -4.93ZM72.4 0V-36H83.88Q84.33 -36 85.66 -35.98Q87 -35.95 88.25 -35.8Q92.53 -35.25 95.49 -32.76Q98.45 -30.28 99.97 -26.43Q101.5 -22.58 101.5 -18Q101.5 -13.43 99.97 -9.58Q98.45 -5.73 95.49 -3.24Q92.53 -0.75 88.25 -0.2Q87.03 -0.05 85.68 -0.03Q84.33 0 83.88 0ZM78.53 -5.68H83.88Q84.62 -5.68 85.79 -5.73Q86.95 -5.78 87.85 -5.95Q90.4 -6.43 92 -8.23Q93.6 -10.03 94.36 -12.6Q95.12 -15.18 95.12 -18Q95.12 -20.93 94.35 -23.5Q93.58 -26.08 91.96 -27.83Q90.35 -29.58 87.85 -30.05Q86.95 -30.25 85.79 -30.29Q84.62 -30.33 83.88 -30.33H78.53ZM107.93 0V-36H129.6V-29.98H113.95V-21H126.6V-15H113.95V0ZM134.6 0V-36H156.28V-29.98H140.63V-21H153.28V-15H140.63V0ZM161.28 0V-36H184.53V-30.35H167.3V-21.48H181.53V-15.83H167.3V-5.65H184.53V0Z", u = 65.6, d = 80;
12
+ function f({ markClassName: e, small: i = !1 }) {
13
+ let a = i ? 7.8 : 6;
14
+ return /* @__PURE__ */ r(t, { children: [
15
+ /* @__PURE__ */ n("circle", {
16
+ className: e,
17
+ cx: "18.75",
18
+ cy: "-18",
19
+ r: "15.75",
20
+ fill: "none",
21
+ stroke: "currentColor",
22
+ strokeWidth: a
23
+ }),
24
+ /* @__PURE__ */ n("circle", {
25
+ className: "kf-eye__pupil",
26
+ cx: "22.53",
27
+ cy: "-18",
28
+ r: i ? 6.52 : 5.67,
29
+ fill: "currentColor"
30
+ }),
31
+ /* @__PURE__ */ n("path", {
32
+ className: e,
33
+ d: "M41.1 -33H47.6A15 15 0 0 1 47.6 -3H41.1",
34
+ fill: "none",
35
+ stroke: "currentColor",
36
+ strokeWidth: a,
37
+ strokeLinecap: "butt"
38
+ })
39
+ ] });
40
+ }
41
+ var p = (e, t = "1em") => ({
42
+ display: "block",
43
+ flex: "none",
44
+ width: e,
45
+ height: t
46
+ });
47
+ function m({ variant: t = "full", collapse: o = "none", small: m = !1, markClassName: h, className: g, style: _, ...v }) {
48
+ let [y, b] = a(!1);
49
+ i(() => {
50
+ if (o !== "scroll" || t !== "full") return;
51
+ let e = 0, n = () => {
52
+ e = 0, b(window.scrollY > d);
53
+ }, r = () => {
54
+ e ||= window.requestAnimationFrame(n);
55
+ };
56
+ return n(), window.addEventListener("scroll", r, { passive: !0 }), () => {
57
+ window.removeEventListener("scroll", r), e && window.cancelAnimationFrame(e);
58
+ };
59
+ }, [o, t]);
60
+ let x = (t) => ({
61
+ role: "img",
62
+ "aria-label": "Kodffe",
63
+ className: e("kf-logo", t, g),
64
+ style: {
65
+ display: "inline-flex",
66
+ alignItems: "flex-start",
67
+ ..._
68
+ },
69
+ ...v
70
+ }), S = {
71
+ "aria-hidden": !0,
72
+ focusable: "false"
73
+ };
74
+ if (t === "mark") return /* @__PURE__ */ n("span", {
75
+ ...x("kf-logo--mark"),
76
+ children: /* @__PURE__ */ n("svg", {
77
+ viewBox: "-3.2 -43 72 50",
78
+ className: "kf-logo__mark",
79
+ style: p("1.44em"),
80
+ ...S,
81
+ children: /* @__PURE__ */ n(f, {
82
+ markClassName: h,
83
+ small: m
84
+ })
85
+ })
86
+ });
87
+ if (t === "lockup") {
88
+ let e = 66 / u;
89
+ return /* @__PURE__ */ n("span", {
90
+ ...x("kf-logo--lockup"),
91
+ children: /* @__PURE__ */ r("svg", {
92
+ viewBox: "0 0 293 80",
93
+ style: p("5.86em", "1.6em"),
94
+ ...S,
95
+ children: [/* @__PURE__ */ n("g", {
96
+ transform: `translate(40 40) scale(${e}) translate(${-65.6 / 2} 18)`,
97
+ children: /* @__PURE__ */ n(f, { markClassName: h })
98
+ }), /* @__PURE__ */ n("path", {
99
+ transform: "translate(94 57)",
100
+ d: l,
101
+ fill: "currentColor"
102
+ })]
103
+ })
104
+ });
105
+ }
106
+ return t === "wordmark" ? /* @__PURE__ */ n("span", {
107
+ ...x("kf-logo--wordmark"),
108
+ children: /* @__PURE__ */ n("svg", {
109
+ viewBox: "0 0 188 50",
110
+ style: p("3.76em"),
111
+ ...S,
112
+ children: /* @__PURE__ */ n("path", {
113
+ transform: "translate(0 43)",
114
+ d: l,
115
+ fill: "currentColor"
116
+ })
117
+ })
118
+ }) : /* @__PURE__ */ r("span", {
119
+ ...x(e(o === "scroll" && "kf-logo--collapsible", y && "is-collapsed")),
120
+ children: [
121
+ /* @__PURE__ */ n("svg", {
122
+ viewBox: "0 -43 32.42 50",
123
+ className: "kf-logo__k",
124
+ style: p("0.6484em"),
125
+ ...S,
126
+ children: /* @__PURE__ */ n("path", {
127
+ d: s,
128
+ fill: "currentColor"
129
+ })
130
+ }),
131
+ /* @__PURE__ */ n("svg", {
132
+ viewBox: "0 -43 66.38 50",
133
+ className: "kf-logo__slot",
134
+ style: p("1.3276em"),
135
+ ...S,
136
+ children: /* @__PURE__ */ n("g", {
137
+ transform: "translate(2.6 0)",
138
+ children: /* @__PURE__ */ n(f, { markClassName: h })
139
+ })
140
+ }),
141
+ /* @__PURE__ */ n("svg", {
142
+ viewBox: "87.02 -43 86.52 50",
143
+ className: "kf-logo__ffe",
144
+ style: p("1.7304em"),
145
+ ...S,
146
+ children: /* @__PURE__ */ n("path", {
147
+ d: c,
148
+ fill: "currentColor"
149
+ })
150
+ })
151
+ ]
152
+ });
153
+ }
154
+ //#endregion
155
+ export { m as Logo, o as logoVariants };
156
+
157
+ //# sourceMappingURL=logo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.js","names":["_Fragment","logoVariants","K","FFE","WORDMARK","MARK_W","COLLAPSE_AFTER","Mark","stroke","small","Fragment","markClassName","box","Logo","rest","collapsed","setCollapsed","useState","collapse","variant","frame","update","onScroll","root","cx","extra","className","style","svg","k"],"sources":["../../src/logo/logo.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useState, type CSSProperties, type HTMLAttributes } from 'react';\nimport { cx } from '../lib/cx';\n\nexport const logoVariants = ['full', 'mark', 'lockup', 'wordmark'] as const;\nexport type LogoVariant = (typeof logoVariants)[number];\n\nexport interface LogoProps extends HTMLAttributes<HTMLSpanElement> {\n /**\n * - `full` (default): KODFFE, the eye is the O and the open D is the D. Minimum width 96 px.\n * - `mark`: the eye and open D alone (avatars, favicon, the collapsed header). Minimum size 16 px.\n * - `lockup`: the mark next to the plain wordmark, for wide formats. Minimum width 96 px.\n * - `wordmark`: the letters alone.\n */\n variant?: LogoVariant;\n /**\n * `scroll` (`full` only): for sticky headers. The full logo shows at the top of the page and collapses to the mark\n * after 80 px of scroll; hovering or focusing the logo's link brings the name back. `none` (default): static.\n */\n collapse?: 'none' | 'scroll';\n /** Heavier strokes (x1.3) and a bigger pupil for the mark at 32 px and under. Only for `mark`. */\n small?: boolean;\n /**\n * Classes for the eye ring and open D only: `text-primary` follows the theme; `text-mint-500` suits a fixed dark\n * background. The letters and the pupil take the inherited text color.\n */\n markClassName?: string;\n}\n\n// Manrope Bold outlines, baseline at y = 0, 1 em = 50 units (docs/design/logo/generate.py).\nconst K = 'M3.5 0V-36H9.53V-19.3L23.9 -36H31.3L15.83 -18.35L32.35 0H24.7L9.53 -16.8V0Z';\nconst FFE =\n 'M93.44 0V-36H115.12V-29.98H99.47V-21H112.12V-15H99.47V0ZM120.12 0V-36H141.79V-29.98H126.14V-21H138.79V-15H126.14V0ZM146.79 0V-36H170.04V-30.35H152.82V-21.48H167.04V-15.83H152.82V-5.65H170.04V0Z';\n// The plain wordmark \"KODFFE\" (lockup and wordmark), from docs/design/logo/svg/kodffe-eye-wordmark-*.svg.\nconst WORDMARK =\n 'M3.5 0V-36H9.53V-19.3L23.9 -36H31.3L15.83 -18.35L32.35 0H24.7L9.53 -16.8V0ZM49.65 0.75Q44.25 0.75 40.4 -1.61Q36.55 -3.98 34.49 -8.2Q32.42 -12.43 32.42 -18Q32.42 -23.58 34.49 -27.8Q36.55 -32.02 40.4 -34.39Q44.25 -36.75 49.65 -36.75Q55.05 -36.75 58.91 -34.39Q62.78 -32.02 64.84 -27.8Q66.9 -23.58 66.9 -18Q66.9 -12.43 64.84 -8.2Q62.78 -3.98 58.91 -1.61Q55.05 0.75 49.65 0.75ZM49.65 -4.93Q53.28 -4.9 55.69 -6.53Q58.1 -8.15 59.31 -11.1Q60.53 -14.05 60.53 -18Q60.53 -21.95 59.31 -24.86Q58.1 -27.78 55.69 -29.4Q53.28 -31.03 49.65 -31.08Q46.03 -31.1 43.62 -29.48Q41.23 -27.85 40.01 -24.9Q38.8 -21.95 38.8 -18Q38.8 -14.05 40 -11.14Q41.2 -8.22 43.61 -6.6Q46.03 -4.98 49.65 -4.93ZM72.4 0V-36H83.88Q84.33 -36 85.66 -35.98Q87 -35.95 88.25 -35.8Q92.53 -35.25 95.49 -32.76Q98.45 -30.28 99.97 -26.43Q101.5 -22.58 101.5 -18Q101.5 -13.43 99.97 -9.58Q98.45 -5.73 95.49 -3.24Q92.53 -0.75 88.25 -0.2Q87.03 -0.05 85.68 -0.03Q84.33 0 83.88 0ZM78.53 -5.68H83.88Q84.62 -5.68 85.79 -5.73Q86.95 -5.78 87.85 -5.95Q90.4 -6.43 92 -8.23Q93.6 -10.03 94.36 -12.6Q95.12 -15.18 95.12 -18Q95.12 -20.93 94.35 -23.5Q93.58 -26.08 91.96 -27.83Q90.35 -29.58 87.85 -30.05Q86.95 -30.25 85.79 -30.29Q84.62 -30.33 83.88 -30.33H78.53ZM107.93 0V-36H129.6V-29.98H113.95V-21H126.6V-15H113.95V0ZM134.6 0V-36H156.28V-29.98H140.63V-21H153.28V-15H140.63V0ZM161.28 0V-36H184.53V-30.35H167.3V-21.48H181.53V-15.83H167.3V-5.65H184.53V0Z';\nconst MARK_W = 65.6;\nconst COLLAPSE_AFTER = 80;\n\n/** The mark: a true-O eye and an open D (no stem, flat ends), eye's left edge at x = 0, centred on y = -18. */\nfunction Mark({ markClassName, small = false }: { markClassName?: string; small?: boolean }) {\n const stroke = small ? 7.8 : 6;\n return (\n <>\n <circle\n className={markClassName}\n cx=\"18.75\"\n cy=\"-18\"\n r=\"15.75\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={stroke}\n />\n <circle className=\"kf-eye__pupil\" cx=\"22.53\" cy=\"-18\" r={small ? 6.52 : 5.67} fill=\"currentColor\" />\n <path\n className={markClassName}\n d=\"M41.1 -33H47.6A15 15 0 0 1 47.6 -3H41.1\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={stroke}\n strokeLinecap=\"butt\"\n />\n </>\n );\n}\n\n// Sizes are inline so the logo renders correctly without styles.css; styles.css only adds the collapse motion.\nconst box = (width: string, height = '1em'): CSSProperties => ({ display: 'block', flex: 'none', width, height });\n\n/**\n * The Kodffe logo in every layout, option B \"Open D\" (docs/adr/0012-open-d-logo.md). Size it with `font-size`\n * (e.g. `text-2xl`). Always named \"Kodffe\". The `full` logo is built from three pieces (K | mark | F F E) so the mark\n * can stay while the letters fade; only transform and opacity animate (no layout shift). Do not outline it, add\n * shadows or change the proportions.\n */\nexport function Logo({\n variant = 'full',\n collapse = 'none',\n small = false,\n markClassName,\n className,\n style,\n ...rest\n}: LogoProps) {\n const [collapsed, setCollapsed] = useState(false);\n\n useEffect(() => {\n if (collapse !== 'scroll' || variant !== 'full') return;\n let frame = 0;\n const update = () => {\n frame = 0;\n setCollapsed(window.scrollY > COLLAPSE_AFTER);\n };\n const onScroll = () => {\n if (!frame) frame = window.requestAnimationFrame(update);\n };\n update();\n window.addEventListener('scroll', onScroll, { passive: true });\n return () => {\n window.removeEventListener('scroll', onScroll);\n if (frame) window.cancelAnimationFrame(frame);\n };\n }, [collapse, variant]);\n\n const root = (extra?: string | false) => ({\n role: 'img' as const,\n 'aria-label': 'Kodffe',\n className: cx('kf-logo', extra, className),\n style: { display: 'inline-flex', alignItems: 'flex-start', ...style },\n ...rest,\n });\n const svg = { 'aria-hidden': true as const, focusable: 'false' as const };\n\n if (variant === 'mark') {\n return (\n <span {...root('kf-logo--mark')}>\n <svg viewBox=\"-3.2 -43 72 50\" className=\"kf-logo__mark\" style={box('1.44em')} {...svg}>\n <Mark markClassName={markClassName} small={small} />\n </svg>\n </span>\n );\n }\n\n if (variant === 'lockup') {\n // Mark centred on (40, 40), 66 units wide, next to the wordmark (80-unit-tall box, 1.6 em).\n const k = 66 / MARK_W;\n return (\n <span {...root('kf-logo--lockup')}>\n <svg viewBox=\"0 0 293 80\" style={box('5.86em', '1.6em')} {...svg}>\n <g transform={`translate(40 40) scale(${k}) translate(${-MARK_W / 2} 18)`}>\n <Mark markClassName={markClassName} />\n </g>\n <path transform=\"translate(94 57)\" d={WORDMARK} fill=\"currentColor\" />\n </svg>\n </span>\n );\n }\n\n if (variant === 'wordmark') {\n return (\n <span {...root('kf-logo--wordmark')}>\n <svg viewBox=\"0 0 188 50\" style={box('3.76em')} {...svg}>\n <path transform=\"translate(0 43)\" d={WORDMARK} fill=\"currentColor\" />\n </svg>\n </span>\n );\n }\n\n return (\n <span {...root(cx(collapse === 'scroll' && 'kf-logo--collapsible', collapsed && 'is-collapsed'))}>\n <svg viewBox=\"0 -43 32.42 50\" className=\"kf-logo__k\" style={box('0.6484em')} {...svg}>\n <path d={K} fill=\"currentColor\" />\n </svg>\n <svg viewBox=\"0 -43 66.38 50\" className=\"kf-logo__slot\" style={box('1.3276em')} {...svg}>\n <g transform=\"translate(2.6 0)\">\n <Mark markClassName={markClassName} />\n </g>\n </svg>\n <svg viewBox=\"87.02 -43 86.52 50\" className=\"kf-logo__ffe\" style={box('1.7304em')} {...svg}>\n <path d={FFE} fill=\"currentColor\" />\n </svg>\n </span>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface ProductLogoProps {
3
+ /** Product name, set in the display font (Manrope). */
4
+ name: string;
5
+ /** The product's own mark, drawn inside the square tile (an `<svg>` with `currentColor`). */
6
+ mark: ReactNode;
7
+ /** Tile background, e.g. `var(--kf-primary)`; the mark takes `markColor`. */
8
+ tileColor?: string;
9
+ markColor?: string;
10
+ /** Adds a small "by Kodffe" line. */
11
+ byKodffe?: boolean;
12
+ size?: 'sm' | 'md' | 'lg';
13
+ className?: string;
14
+ }
15
+ /**
16
+ * A product lockup: a square tile with the product's mark, the name and an optional "by Kodffe". Each product
17
+ * brings its own mark; the name always uses the library's display font (ADR-0013, decision 9).
18
+ */
19
+ export declare function ProductLogo({ name, mark, tileColor, markColor, byKodffe, size, className, }: ProductLogoProps): import("react").JSX.Element;
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import { cx as e } from "../lib/cx.js";
3
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
+ //#region src/logo/product-logo.tsx
5
+ var r = {
6
+ sm: {
7
+ tile: "size-7 rounded-[7px]",
8
+ name: "text-base"
9
+ },
10
+ md: {
11
+ tile: "size-9 rounded-sm",
12
+ name: "text-lg"
13
+ },
14
+ lg: {
15
+ tile: "size-12 rounded-md",
16
+ name: "text-2xl"
17
+ }
18
+ };
19
+ function i({ name: i, mark: a, tileColor: o = "var(--kf-primary)", markColor: s = "#fff", byKodffe: c, size: l = "md", className: u }) {
20
+ let d = r[l];
21
+ return /* @__PURE__ */ n("span", {
22
+ className: e("inline-flex items-center gap-2.5 font-display text-fg", u),
23
+ children: [/* @__PURE__ */ t("span", {
24
+ "aria-hidden": "true",
25
+ className: e("flex shrink-0 items-center justify-center [&>svg]:size-[64%]", d.tile),
26
+ style: {
27
+ background: o,
28
+ color: s
29
+ },
30
+ children: a
31
+ }), /* @__PURE__ */ n("span", {
32
+ className: "grid leading-none",
33
+ children: [/* @__PURE__ */ t("span", {
34
+ className: e("font-bold tracking-tight", d.name),
35
+ children: i
36
+ }), c && /* @__PURE__ */ t("span", {
37
+ className: "mt-1 font-sans text-[11px] font-medium text-fg-muted",
38
+ children: "by Kodffe"
39
+ })]
40
+ })]
41
+ });
42
+ }
43
+ //#endregion
44
+ export { i as ProductLogo };
45
+
46
+ //# sourceMappingURL=product-logo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-logo.js","names":["SIZES","ProductLogo","s","size","cx","className","tileColor","markColor","mark","name","byKodffe"],"sources":["../../src/logo/product-logo.tsx"],"sourcesContent":["'use client';\nimport type { ReactNode } from 'react';\nimport { cx } from '../lib/cx';\n\n/* ---------------- ProductLogo ---------------- */\n\nexport interface ProductLogoProps {\n /** Product name, set in the display font (Manrope). */\n name: string;\n /** The product's own mark, drawn inside the square tile (an `<svg>` with `currentColor`). */\n mark: ReactNode;\n /** Tile background, e.g. `var(--kf-primary)`; the mark takes `markColor`. */\n tileColor?: string;\n markColor?: string;\n /** Adds a small \"by Kodffe\" line. */\n byKodffe?: boolean;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n}\n\nconst SIZES = {\n sm: { tile: 'size-7 rounded-[7px]', name: 'text-base' },\n md: { tile: 'size-9 rounded-sm', name: 'text-lg' },\n lg: { tile: 'size-12 rounded-md', name: 'text-2xl' },\n};\n\n/**\n * A product lockup: a square tile with the product's mark, the name and an optional \"by Kodffe\". Each product\n * brings its own mark; the name always uses the library's display font (ADR-0013, decision 9).\n */\nexport function ProductLogo({\n name,\n mark,\n tileColor = 'var(--kf-primary)',\n markColor = '#fff',\n byKodffe,\n size = 'md',\n className,\n}: ProductLogoProps) {\n const s = SIZES[size];\n return (\n <span className={cx('inline-flex items-center gap-2.5 font-display text-fg', className)}>\n <span\n aria-hidden=\"true\"\n // The mark fills 64% of the tile (percent sizes need the tile's own definite size, so flex, not grid).\n className={cx('flex shrink-0 items-center justify-center [&>svg]:size-[64%]', s.tile)}\n style={{ background: tileColor, color: markColor }}\n >\n {mark}\n </span>\n <span className=\"grid leading-none\">\n <span className={cx('font-bold tracking-tight', s.name)}>{name}</span>\n {byKodffe && <span className=\"mt-1 font-sans text-[11px] font-medium text-fg-muted\">by Kodffe</span>}\n </span>\n </span>\n );\n}\n"],"mappings":";;;;AAoBA,IAAMA,IAAQ;CACZ,IAAI;EAAE,MAAM;EAAwB,MAAM;CAAY;CACtD,IAAI;EAAE,MAAM;EAAqB,MAAM;CAAU;CACjD,IAAI;EAAE,MAAM;EAAsB,MAAM;CAAW;AACrD;AAMA,SAAgBC,EAAY,EAC1B,SACA,SACA,eAAY,qBACZ,eAAY,QACZ,aACA,UAAO,MACP,gBACmB;CACnB,IAAMC,IAAIF,EAAMG;CAChB,OACE,kBAAC,QAAD;EAAM,WAAWC,EAAG,yDAAyDC,CAAS;EAAtF,UAAA,CACE,kBAAC,QAAD;GACE,eAAY;GAEZ,WAAWD,EAAG,gEAAgEF,EAAE,IAAI;GACpF,OAAO;IAAE,YAAYI;IAAW,OAAOC;GAAU;GAEhD,UAAAC;EACG,CAAA,GACN,kBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,kBAAC,QAAD;IAAM,WAAWJ,EAAG,4BAA4BF,EAAE,IAAI;IAAI,UAAAO;GAAW,CAAA,GACpEC,KAAY,kBAAC,QAAD;IAAM,WAAU;IAAuD,UAAA;GAAe,CAAA,CAC/F;EACF,CAAA,CAAA;;AAEV"}