@cosxai/ui 0.25.0 → 1.0.0-alpha.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 (165) hide show
  1. package/CHANGELOG.md +597 -0
  2. package/NOTICE +11 -0
  3. package/README.md +52 -0
  4. package/package.json +57 -7
  5. package/src/brand.css +13 -0
  6. package/src/components/Badge.tsx +68 -0
  7. package/src/components/Button.tsx +178 -0
  8. package/src/components/Card.tsx +42 -0
  9. package/src/components/Checkbox.tsx +67 -0
  10. package/src/components/Dialog.tsx +111 -0
  11. package/src/components/Field.tsx +66 -0
  12. package/src/components/Figure.tsx +30 -0
  13. package/src/components/Icon.tsx +32 -0
  14. package/src/components/IconButton.tsx +55 -0
  15. package/src/components/Input.tsx +74 -0
  16. package/src/components/Logo.tsx +62 -0
  17. package/src/components/Marker.tsx +26 -0
  18. package/src/components/MetaLabel.tsx +38 -0
  19. package/src/components/Radio.tsx +72 -0
  20. package/src/components/Select.tsx +351 -0
  21. package/src/components/Spinner.tsx +20 -0
  22. package/src/components/Switch.tsx +67 -0
  23. package/src/components/Table.tsx +196 -0
  24. package/src/components/Tabs.tsx +124 -0
  25. package/src/components/Tag.tsx +35 -0
  26. package/src/components/Textarea.tsx +46 -0
  27. package/src/components/ThemeProvider.tsx +80 -0
  28. package/src/components/Toast.tsx +159 -0
  29. package/src/components/Tooltip.tsx +57 -0
  30. package/src/components/core.test.tsx +242 -0
  31. package/src/components/forms-control.ts +36 -0
  32. package/src/components/forms.test.tsx +280 -0
  33. package/src/components/logo-art.ts +4 -0
  34. package/src/components/overlays.test.tsx +245 -0
  35. package/src/components/useButtonAction.ts +57 -0
  36. package/src/fonts.css +20 -0
  37. package/src/index.ts +34 -24
  38. package/src/lib/cn.ts +23 -6
  39. package/src/modes.css +10 -0
  40. package/src/styles.css +19 -0
  41. package/src/theme.css +143 -0
  42. package/src/tokens/colors.css +112 -0
  43. package/src/tokens/compat.css +36 -0
  44. package/src/tokens/elevation.css +14 -0
  45. package/src/tokens/motion.css +42 -0
  46. package/src/tokens/spacing.css +55 -0
  47. package/src/tokens/typography-cjk.css +42 -0
  48. package/src/tokens/typography.css +81 -0
  49. package/src/actionbar/ActionBar.tsx +0 -837
  50. package/src/actionbar/ActionBarButton.tsx +0 -111
  51. package/src/actionbar/ActionBarMenuGroup.tsx +0 -116
  52. package/src/actionbar/ActionBarModeHandle.tsx +0 -171
  53. package/src/actionbar/ActionBarProvider.tsx +0 -91
  54. package/src/actionbar/actionbar-context.ts +0 -45
  55. package/src/actionbar/index.ts +0 -28
  56. package/src/actionbar/types.ts +0 -147
  57. package/src/actionbar/useActionBarItems.ts +0 -60
  58. package/src/actionbar/useActionBarMode.ts +0 -144
  59. package/src/actionbar/useActionBarPanel.ts +0 -56
  60. package/src/actionbar/useActionBarStatusDot.ts +0 -49
  61. package/src/actionbar/useActionBarToast.ts +0 -33
  62. package/src/ambient/AmbientBackdrop.tsx +0 -74
  63. package/src/ambient/CommandInput.tsx +0 -107
  64. package/src/ambient/SuperbarStrip.tsx +0 -36
  65. package/src/ambient/index.ts +0 -6
  66. package/src/bento/BentoCell.tsx +0 -66
  67. package/src/bento/BentoGrid.tsx +0 -42
  68. package/src/bento/index.ts +0 -2
  69. package/src/command/CommandPalette.tsx +0 -329
  70. package/src/command/CommandProvider.tsx +0 -57
  71. package/src/command/command-context.ts +0 -12
  72. package/src/command/index.ts +0 -6
  73. package/src/command/rank.ts +0 -50
  74. package/src/command/types.ts +0 -37
  75. package/src/command/useCommandSource.ts +0 -37
  76. package/src/dialogs/DialogsProvider.tsx +0 -216
  77. package/src/dialogs/Modal.tsx +0 -260
  78. package/src/dialogs/Toast.tsx +0 -85
  79. package/src/dialogs/dialogs-context.ts +0 -6
  80. package/src/dialogs/index.ts +0 -10
  81. package/src/dialogs/types.ts +0 -37
  82. package/src/dialogs/useDialogs.ts +0 -8
  83. package/src/editorial/EditorialSpotlight.tsx +0 -63
  84. package/src/editorial/Folio.tsx +0 -52
  85. package/src/editorial/PlateMarker.tsx +0 -33
  86. package/src/editorial/RomanSection.tsx +0 -65
  87. package/src/editorial/RunningMarginalia.tsx +0 -65
  88. package/src/editorial/index.ts +0 -10
  89. package/src/hooks/index.ts +0 -5
  90. package/src/hooks/useKeyboardHotkey.ts +0 -93
  91. package/src/hooks/useReducedMotion.ts +0 -20
  92. package/src/hooks/useViewport.ts +0 -61
  93. package/src/layout/Breadcrumb.tsx +0 -74
  94. package/src/layout/LeftNavRail.tsx +0 -139
  95. package/src/layout/MobileTabBar.tsx +0 -101
  96. package/src/layout/NavItem.tsx +0 -128
  97. package/src/layout/NavSearchTrigger.tsx +0 -88
  98. package/src/layout/NavSection.tsx +0 -40
  99. package/src/layout/RightSidebarPanel.tsx +0 -111
  100. package/src/layout/Shell.tsx +0 -91
  101. package/src/layout/SidePanel.tsx +0 -316
  102. package/src/layout/StickyBanner.tsx +0 -83
  103. package/src/layout/Topbar.tsx +0 -68
  104. package/src/layout/index.ts +0 -24
  105. package/src/layout/useNavRailState.ts +0 -69
  106. package/src/lib/time-utils.ts +0 -44
  107. package/src/neobrutalism/Marquee.tsx +0 -81
  108. package/src/neobrutalism/Sticker.tsx +0 -71
  109. package/src/neobrutalism/index.ts +0 -4
  110. package/src/primitives/Avatar.tsx +0 -53
  111. package/src/primitives/Button.tsx +0 -67
  112. package/src/primitives/Card.tsx +0 -41
  113. package/src/primitives/Checkbox.tsx +0 -87
  114. package/src/primitives/Chip.tsx +0 -191
  115. package/src/primitives/Combobox.tsx +0 -507
  116. package/src/primitives/CopyField.tsx +0 -139
  117. package/src/primitives/CountBadge.tsx +0 -50
  118. package/src/primitives/Input.tsx +0 -204
  119. package/src/primitives/Kbd.tsx +0 -45
  120. package/src/primitives/MentionCombobox.tsx +0 -612
  121. package/src/primitives/MultiCombobox.tsx +0 -327
  122. package/src/primitives/PageHeader.tsx +0 -77
  123. package/src/primitives/Radio.tsx +0 -131
  124. package/src/primitives/SegmentedControl.tsx +0 -135
  125. package/src/primitives/Select.tsx +0 -779
  126. package/src/primitives/SignaturePad.tsx +0 -461
  127. package/src/primitives/Tag.tsx +0 -56
  128. package/src/primitives/Textarea.tsx +0 -62
  129. package/src/primitives/ToggleSwitch.tsx +0 -79
  130. package/src/primitives/Tooltip.tsx +0 -213
  131. package/src/primitives/index.ts +0 -53
  132. package/src/pwa/InstallPromptBanner.tsx +0 -132
  133. package/src/pwa/index.ts +0 -4
  134. package/src/pwa/manifest.template.json +0 -20
  135. package/src/pwa/registerSW.ts +0 -55
  136. package/src/riso/Halftone.tsx +0 -85
  137. package/src/riso/Misregister.tsx +0 -63
  138. package/src/riso/RisoStamp.tsx +0 -76
  139. package/src/riso/index.ts +0 -3
  140. package/src/sketch/HandUnderline.tsx +0 -53
  141. package/src/sketch/RoughArrow.tsx +0 -91
  142. package/src/sketch/RoughBox.tsx +0 -73
  143. package/src/sketch/StickyNote.tsx +0 -56
  144. package/src/sketch/index.ts +0 -4
  145. package/src/styles/base.css +0 -186
  146. package/src/styles/chrome-ambient.css +0 -235
  147. package/src/styles/chrome-bento.css +0 -192
  148. package/src/styles/chrome-editorial.css +0 -168
  149. package/src/styles/chrome-neobrutalism.css +0 -326
  150. package/src/styles/chrome-riso.css +0 -339
  151. package/src/styles/chrome-sketch.css +0 -362
  152. package/src/styles/chrome-swiss.css +0 -265
  153. package/src/styles/chrome-terminal.css +0 -272
  154. package/src/styles/fonts.css +0 -1034
  155. package/src/styles/index.css +0 -343
  156. package/src/styles/tokens.css +0 -941
  157. package/src/terminal/AsciiBox.tsx +0 -65
  158. package/src/terminal/BrailleSpinner.tsx +0 -46
  159. package/src/terminal/index.ts +0 -4
  160. package/src/theme/ThemeProvider.tsx +0 -99
  161. package/src/theme/index.ts +0 -6
  162. package/src/theme/inline-script.ts +0 -40
  163. package/src/theme/theme-context.ts +0 -7
  164. package/src/theme/types.ts +0 -39
  165. package/src/theme/useTheme.ts +0 -8
package/CHANGELOG.md ADDED
@@ -0,0 +1,597 @@
1
+ ## 1.0.0-alpha.1 (2026-09-29)
2
+
3
+ All 22 COSX Design System 3.0 primitives, checked against their
4
+ design.cosx.co pages in light / ink × English / Chinese.
5
+
6
+ - Core: Button (six types, grounds, 32/38/44, shortcut and count,
7
+ disabled-with-reason, busy/done in place + `useButtonAction`),
8
+ IconButton, Badge (shape carries urgency; compact dot keeps the
9
+ wording), Tag, Card, Figure, Marker, MetaLabel, Logo, Icon (Lucide),
10
+ Spinner, ThemeProvider (system / light / ink).
11
+ - Forms: Field (label, hint, error, prefix/suffix, read-only), Input,
12
+ Textarea, Select (portal, auto placement, keyboard, groups, disabled
13
+ rows, meta, filter above 8), Checkbox (indeterminate), RadioGroup, Switch.
14
+ - Tabs (underline, pills), Dialog, Toast (`toast()` + `<Toaster>`),
15
+ Tooltip, Table (sortable, washes for attention/overdue, cards on phones).
16
+ - `--brand-field` / `--brand-mark` for workspace brands; the 3.0 ink
17
+ marker rule for `data-mode="ink"`; an `ink:` Tailwind variant.
18
+ - 65 tests (behaviour + axe). Spec/source differences and what was
19
+ built: docs/workdocs/2026-09-29_feature-ui-1.0.
20
+
21
+ ## 1.0.0-alpha.0 (2026-09-29)
22
+
23
+ **A new system — breaking for every 0.x consumer.** 0.x continues on the
24
+ `ui-0.x` branch (dist-tag `v0`); stay on `^0.25` until you move.
25
+
26
+ - COSX Design System 3.0 foundations: the 3.0 token files unchanged
27
+ (`styles.css`), Geist + Noto Sans SC self-hosted (Fontsource, OFL-1.1),
28
+ ink mode via `data-mode="ink"` / `.ink-mode`.
29
+ - Tailwind v4 theme (`theme.css`): 3.0 colours, radii, type scale,
30
+ tracking and easings as utilities; Tailwind's own palette, shadows and
31
+ easings removed.
32
+ - `cn()` — clsx + tailwind-merge taught the kit's size names.
33
+ - Removed: every 0.x component, the `--ck-*` variables and the design
34
+ presets (editorial, neobrutalism, riso, sketch, terminal, ambient,
35
+ bento, pwa). Components return in the next alphas on Radix.
36
+
37
+ ## 0.25.0 (2026-09-08)
38
+
39
+ - **fix(primitives)**: `Chip` no longer steals focus on mousedown — the
40
+ label button and the × button call `preventDefault()` in
41
+ `onMouseDown` (the action stays on `onClick`). A host with an inline
42
+ editor open for another chip no longer sees the editor blur (commit +
43
+ unmount) swallow the first click.
44
+ - **feat(primitives)**: `MultiCombobox` gains `layout?: "stacked" |
45
+ "inline"` (default `stacked` — 0.24.0 behaviour unchanged). `inline`
46
+ is a mail-client "To" field: one Input-like frame
47
+ (`.ck-multi-combobox-field`, `:focus-within` ring, `:has([aria-invalid])`
48
+ critical border + revealed `invalidHint` line, `:has(:disabled)` dim)
49
+ whose `<ul role="list">` is the flex-wrap row — chips as `<li>`s, the
50
+ bare search input in a last presentational `<li>` — so typing
51
+ continues after the last chip and wraps with it; the label renders
52
+ above as an eyebrow, the dropdown spans the frame, the invalid hint is
53
+ `aria-describedby`-linked to the input, editor + hint stay below.
54
+ Clicking the frame's empty area focuses the input. Testids unchanged
55
+ (`-entries` is the `<ul>` inside the frame); new `${testid}-field` on
56
+ the frame.
57
+ - **feat(primitives)**: `Combobox` gains `bare?: boolean` (input without
58
+ its own field chrome, root `position: static` + `flex: 1 1 140px` so a
59
+ host-drawn `position: relative` frame anchors the dropdown; no label /
60
+ error text rendered), `inputId?: string` (for a host `<label
61
+ htmlFor>`) and `inputDescribedBy?: string` (merged into the input's
62
+ `aria-describedby`). `Input` gains `bare?: boolean` (only the `<input
63
+ class="ck-input ck-input--bare">`, no wrapper / label / helper / error
64
+ / chrome; a boosted-specificity rule in `styles/index.css` strips the
65
+ chrome presets' `!important` input styling). `Input` now spreads
66
+ `...rest` before its `error`-derived `aria-invalid` / `aria-describedby`
67
+ (so `error` stays authoritative; a consumer `aria-describedby` is
68
+ merged with the helper/error id rather than replaced).
69
+ - **fix(combobox)**: the 150 ms blur timer no longer closes the list /
70
+ auto-commits when the input has been re-focused within the delay
71
+ (e.g. a host frame click handing focus straight back).
72
+
73
+ ## 0.24.0 (2026-09-08)
74
+
75
+ - **feat(combobox)**: `clearOnCommit?: boolean` — after `onCommit`
76
+ fires, the input resets to "" and the dropdown closes instead of
77
+ echoing the committed text (multi-value mode; default false, the
78
+ single-value behaviour is unchanged). `Combobox` is now
79
+ `forwardRef<ComboboxHandle>` with `ComboboxHandle = { focus(); clear() }`
80
+ (`clear` for parents rejecting a duplicate commit).
81
+ - **feat(primitives)**: New `<Chip>` — pill for one selected value
82
+ (recipient, filter, token): `--ck-bg-muted` slab, 13px sans, tones
83
+ `neutral` / `accent` / `warning` / `critical`, `selected`, `disabled`,
84
+ optional `onClick` (label becomes a button) and `onRemove` (× button,
85
+ `removeLabel` aria-label, default "Remove"). Root
86
+ `<span class="ck-chip" data-ck-chip data-tone data-selected data-disabled>`
87
+ is the styling hook for chrome presets — no per-chrome CSS in this
88
+ release. Deliberately NOT `ck-tag` (the uppercase mono status label).
89
+ - **feat(primitives)**: New `<MultiCombobox>` — `Combobox` in
90
+ `clearOnCommit` mode + a `<ul role="list">` of `Chip`s + an optional
91
+ inline editor slot (`renderEntryEditor`, mounts under the row for the
92
+ entry with `editing: true`) + a `hint` line. Backspace in the EMPTY
93
+ search input removes the last entry (`backspaceRemovesLast`, default
94
+ true; scoped to the embedded input so editors are unaffected). Value is
95
+ parent-owned via `entries` / `onCommit` / `onRemoveEntry`; paste /
96
+ comma splitting is the consumer's job inside `onCommit`. Forwards
97
+ `MultiComboboxHandle` (= `ComboboxHandle`). Testids:
98
+ `${testid}-combobox-input` / `-dropdown` / `-free-entry`,
99
+ `${testid}-entries`, `${testid}-entry-${key}`, `${testid}-editor`.
100
+ - **feat(layout)**: `LeftNavRail` gains `footerTop?: ReactNode` — a
101
+ block pinned between the scrolling nav body and the footer rule,
102
+ always visible above the divider. Graduated from product-meta's local
103
+ pnpm patch (its agent drawer trigger), which meta can now drop.
104
+
105
+ ## 0.23.2 (2026-08-19)
106
+
107
+ - **fix(hooks)**: `useKeyboardHotkey` calls `e.preventDefault()` once
108
+ every guard passes — a hotkey that opens a dialog with an
109
+ autofocused input no longer sees its own keystroke inserted as text.
110
+
111
+ ## 0.21.0 (2026-08-10)
112
+ - **feat(dialogs)**: `Modal` gains `phonePresentation?: "center" | "page" | "sheet"` — how the modal presents on PHONE viewports (no effect elsewhere). `"page"` renders full-screen and slides in from the right like a pushed native page (for content dialogs the user enters to work in); `"sheet"` rises from the bottom edge (light pickers / short forms); `"center"` (default) keeps today's card. New full-distance motion primitives `ck-anim-page-push` / `ck-anim-sheet-up` back it, both killed under `prefers-reduced-motion`. Backward compatible: omit the prop and nothing changes. Graduated from product-meta's matter split dialogs (fact detail / evidence ruling), whose in-dialog "document takes over + Back" phone flow nests naturally inside a pushed page.
113
+
114
+ ## 0.23.1 (2026-08-19)
115
+
116
+ - **fix(select)**: `fit="auto"` triggers size to the WIDEST option
117
+ (invisible same-cell sizer) instead of the current selection — the
118
+ popover matches trigger width, so long options no longer truncate
119
+ after picking a short one.
120
+ ## 0.23.0 (2026-08-19)
121
+
122
+ - **feat(combobox)**: `searchOnFocus?: boolean` — run `search("")` on
123
+ focus so clicking the field presents the option list before any
124
+ typing (select-with-search for small known sets). Off by default.
125
+ ## 0.22.0 (2026-08-19)
126
+
127
+ - SegmentedControl: unfilled track — hairline border delimits the
128
+ group, the raised selected segment carries the contrast. The
129
+ muted-filled track read as a heavy block on warm canvases.
130
+ ## 0.20.0 (2026-07-28)
131
+ - **feat(command)**: `CommandItem` gains an optional `disabled?: boolean`. A disabled row still renders (dimmed) but is skipped by arrow-key nav, never auto-selected (the default highlight lands on the first enabled row), ignores hover/click, and `run` never fires on it. For "coming soon" teasers — distinct from `secret`, which hides the row entirely. Backward compatible: omit it and rows behave exactly as before.
132
+
133
+ ## 0.19.0 (2026-07-28)
134
+ - **feat(command)**: `<CommandPalette>` gains an optional `onQueryChange?: (query: string) => void` prop — fires on every keystroke and on the reset-to-empty when the palette opens. Lets a consumer drive an ASYNC command source (debounce → fetch → register results via `useCommandSource`) that the built-in client-side filter can't provide. Backward compatible: omit it and the palette behaves exactly as before. Unblocks product-meta's global document search in the ⌘K palette.
135
+
136
+ ## 0.18.1 (2026-07-23)
137
+ - fix(hooks): useKeyboardHotkey honors `data-hotkey-passthrough="true"` containers — hotkeys stay live when focus sits in an editable the user never types into (canvas spreadsheet focus traps)
138
+
139
+ # Changelog
140
+
141
+ ## 0.13.0 (2026-07-17)
142
+
143
+ - **feat(primitives)**: New `<CopyField>` — read-only value field with an embedded Copy button inside the frame (mirrors `Input`'s suffix-addon chrome: 36px height, muted slab, hairline divider). Value renders mono + ellipsized; Copy flips to "Copied ✓" for a beat (`copiedForMs`, default 1500ms) and writes via the Clipboard API with a silent select-the-text fallback for non-secure origins. For share links, signing URLs, tokens, API keys — anywhere the next action is overwhelmingly "copy this". Graduated from product-meta's Signing tab where the URL box + separate COPY button read as two disconnected controls.
144
+
145
+ ## 0.11.0 (2026-07-14)
146
+
147
+ - **feat(layout)**: New `<SidePanel>` — right-docked, backdrop-less panel that slides in from the right edge and pushes main content left via `--ck-sidepanel-width` (stamped on `:root` while open). For editors, activity feeds, share managers, and other admin surfaces where the reader should keep interacting with the main content while the panel is open (Notion / Linear / Slack right-panel UX). Distinct from `<RightSidebarPanel>` (scoped floating card at edge offset): SidePanel is full-height and reshapes layout via a CSS var; RightSidebarPanel is a smaller ephemeral card. Portal-mounted to `document.body` (escapes transform ancestors), `role="complementary"`, ESC-to-close, focus lands on the close button (no focus trap — main surface stays interactive). Prototyped in product-meta's block_doc editor; graduated after Ben validated the API against the Edit document surface. Layout consumers opt into the push-left by summing `var(--ck-sidepanel-width, 0px)` into their right inset.
148
+
149
+ ## 0.10.4 (2026-07-09)
150
+
151
+ - **fix(actionbar)**: Admin mode is now session-only (was persisted to `localStorage` under `<storageKey>:admin`). Two problems the persistence caused:
152
+ - Page reload → session came back in elevated state without an explicit user opt-in.
153
+ - Cross-doc navigation → user turned admin on for Doc A, opened Doc B, and landed in admin mode carrying nothing but a red-tinted bar (bug Ben spotted on the block_doc viewer).
154
+ - **fix(actionbar)**: Auto-reset admin mode when the set of `adminOnly` item keys changes. Consumers that call `useActionBarItems` with different items on a new route reliably clear the elevated state — no per-page "reset on unmount" wiring needed. Same-page updates (item labels change, hint keys change) don't reset because the *key* set stays constant.
155
+
156
+ ## 0.10.3 (2026-07-09)
157
+
158
+ - **feat(actionbar)**: New `hiddenInAdmin?: boolean` on `ActionBarItem`. Symmetric counterpart to `adminOnly` — pages that want an exclusive "different toolset" UX (block_doc viewer's Manage share + Activity + History replacing Share) mark their regular items `hiddenInAdmin: true` so they clear when the shield toggle flips on. Pages that want the additive layering shape just leave it unset. Per-item flag so different consumers on the same page can pick independently.
159
+
160
+ ## 0.10.2 (2026-07-09)
161
+
162
+ - **fix(actionbar)**: Restore shield glyph on the admin-mode toggle (better semantic weight than the sliders variant tried in 0.10.1). Still borderless / transparent bg — no outer ring, just the icon.
163
+ - **feat(actionbar)**: Softer motion when entering / exiting admin mode. Bar background + border tint now transition on a spring curve (260 ms, `cubic-bezier(0.34, 1.56, 0.64, 1)`), the shield icon picks up a 1.06× scale bump alongside its stroke-colour shift, and admin items animate in with a translate + scale spring on reveal. Toggle-off is instant (React unmount) — future release may add a matching exit transition if needed.
164
+
165
+ ## 0.10.1 (2026-07-09)
166
+
167
+ - **fix(actionbar)**: Admin-mode toggle drops its bordered-circle chrome so the button reads with the same visual weight as neighbouring items (Theme icon, Share icon, etc.). Now: transparent bg, no border, muted stroke by default, accent-coloured icon when active. Glyph swapped from shield → sliders (two tracks with knobs) so the affordance reads as "reveal more controls" instead of "security/protected" — matches what admin mode actually does.
168
+
169
+ ## 0.10.0 (2026-07-09)
170
+
171
+ - **feat(actionbar)**: New `adminOnly?: boolean` on `ActionBarItem`. Marking an item admin-only hides it behind an auto-appearing shield toggle button (rendered between the drag grip and the first item). When active, admin items reveal + the bar picks up a subtle accent-tinted background so the "elevated privileges" state reads at a glance. State persists per `storageKey`. Toggle only renders when at least one registered item has adminOnly=true — a bar with no admin items looks exactly as it always did. Reusable across viewers (block_doc, PDF, etc.) so cross-kind admin surfaces stay consistent.
172
+ - **feat(actionbar)**: Bar background + border animate on admin-mode transition (180 ms ease-out). Accent-mixed 8% opacity in oklab space so the tint reads on both light and dark themes without a full palette override.
173
+
174
+ ## 0.9.0 (2026-07-08)
175
+
176
+ - **feat(actionbar)**: New `ActionBarModeHandle` component + `useActionBarMode` hook for the block_doc viewer's admin peek-out affordance. The handle sits ~6px above the ActionBar's top edge at rest (a slim 44×6 accent-coloured pill) and grows to 68×24 on hover / focus while revealing a label — click swaps the ActionBar's item set between named modes ("viewer" / "manage" / "draft"). `useActionBarMode({ modes, defaultMode, storageKey })` manages the state, persists to localStorage, and registers the active mode's items into the ActionBar registry atomically (previous set unregistered on mode change). Gate the handle behind a capability bit with `visible={can.manage}` — non-privileged principals never see the affordance. Lands with product-mesh M4.5 Phase F; consumer wiring in product-meta comes with Phase G-I.
177
+
178
+ ## 0.7.1 (2026-07-01)
179
+
180
+ - **fix(MentionCombobox)**: Removed `padding: 0 4px` + `font-weight: 500` from the highlight chip. Both added glyph advance to the overlay that the underlying textarea didn't have, so every character after `@Ben Zhang` drifted right of its true position (visible mis-alignment reported at ~8px, matching the chip's horizontal padding). The chip now conveys "highlighted" purely via background + colour + a tight border-radius; overlay and textarea characters occupy identical widths so the caret stays where the user typed it.
181
+
182
+ ## 0.7.0 (2026-07-01)
183
+
184
+ - **feat(MentionCombobox)**: New `mentionNames?: readonly string[]` prop. When provided and non-empty, the primitive draws a mirrored overlay behind the textarea that highlights each `@Name` (matched longest-first, whitespace-bounded) as an accent-tinted chip while the user is still composing. Textarea's own glyphs are hidden with `color: transparent` + `-webkit-text-fill-color: transparent`; `caret-color` keeps the cursor visible. Scroll syncs via a `onScroll` handler so long comments stay aligned. Omit or pass empty to opt out — the primitive renders exactly as before. Consumers typically derive the list from their captured pick-list (product-meta CommentComposer stores `PickedMention[]` for its bracket-form wire serializer and passes `picks.map(p => p.name)` here).
185
+ - **feat(MentionCombobox)**: Export `splitByMentionNames` helper used internally by the overlay — useful for consumers that render the same body outside the composer (e.g. a preview panel).
186
+
187
+ ## 0.6.0 (2026-06-28)
188
+
189
+ - **feat(MentionCombobox)**: New headless generic primitive. Same @-trigger + debounced-search + keyboard-nav behaviour previously in product-meta, hoisted up so mesh + future consumers can share it. Consumers plug in `loadCandidates`, `getItemKey`, `getInsertionText`, `renderItem`. Ref exposes `focus()` for mount-on-open flows.
190
+
191
+ ## 0.5.0 (2026-06-26)
192
+
193
+ - **feat(Button)**: New `loading?: boolean` prop. When true the
194
+ button renders a leading CSS ring spinner (`.ck-btn-spinner`
195
+ inherits `currentColor` so it reads on every variant), is set
196
+ natively `disabled`, and exposes `aria-busy="true"`. Distinct
197
+ from `disabled` — `disabled` means "you can't take this action
198
+ right now", `loading` means "we're already taking this action".
199
+ Wire both together for async submits behind form validation:
200
+ `<Button disabled={!name.trim()} loading={submitting}>`.
201
+ - **refactor(Button)**: `forwardRef` to the underlying `<button>`
202
+ so consumers can wire focus management (e.g. autoFocus on dialog
203
+ mount). Was a Phase 0 stub; aligns with the
204
+ `.claude/rules/code-style.md` `forwardRef-for-all-interactive-
205
+ elements` rule.
206
+
207
+ ## 0.4.11 (2026-06-25)
208
+
209
+ - **fix(ActionBar)**: The keyboard-shortcut `hint` badge stayed
210
+ visible on phone-width viewports even though the matching label
211
+ had already collapsed via `@media (max-width: 767px)`. Result:
212
+ on a phone, the bar read as `[icon] C` instead of just `[icon]`
213
+ — a dangling badge advertising a shortcut nobody can press
214
+ without a keyboard. Tag the hint span with `ck-actionbar-hint`
215
+ and add it to the same `display: none` media rule.
216
+
217
+ ## 0.4.10 (2026-06-24)
218
+
219
+ - **feat(tokens)**: New `--ck-shadow-overlay` token for floating
220
+ surfaces that need to read as "lifted off the page" rather than
221
+ "card on a page". Default light value ~2× the punch of shadow-3
222
+ (32-px blur on the soft layer + 8-px blur on the tight layer);
223
+ per-chrome overrides for dark, editorial light, and dark-editorial
224
+ so the shadow stays legible on charcoal surfaces.
225
+ - **fix(Modal)**: Bump card to `--ck-radius-lg` (12 px) + the new
226
+ `--ck-shadow-overlay`, and stretch slot paddings to a unified
227
+ 24-px gutter (`20px 24px` header, `20px 24px 24px` body,
228
+ `16px 24px` footer). Consumers reported the previous chrome read
229
+ as "another tier of card" — these changes give the modal an
230
+ unambiguous hierarchy step above the page.
231
+
232
+ ## 0.4.9 (2026-06-24)
233
+
234
+ - **fix(Modal)**: Bump backdrop blur from 2 px to 8 px so the page
235
+ surface visibly drops out of focus when a modal opens. The earlier
236
+ value read as "barely there" — consumers cross-referencing
237
+ agent-dataroom (4 px `backdrop-blur-sm`) asked for distinctly
238
+ more separation; 8 px lands on the "the world stopped" side of
239
+ the curve while still letting the surface tint show through.
240
+ Also adds the `-webkit-` prefix so Safari < 18 (and any WebKit
241
+ embed) gets the same effect instead of falling through to no
242
+ blur at all.
243
+
244
+ ## 0.4.8 (2026-06-24)
245
+
246
+ - **fix(chrome-editorial)**: NavItem rows in the LeftNavRail now get a
247
+ hover background like every other chrome. Editorial was the only one
248
+ missing the `[data-ck-navitem]:not([data-active="true"]):hover` rule
249
+ — rail items read as static even though they're navigable, which
250
+ product-meta consumers noticed against the cards / actionbar items
251
+ that DO tint on hover. Sketch / ambient / riso / neobrutalism /
252
+ terminal already had the equivalent rule; this brings editorial up
253
+ to parity using the shared `--ck-bg-muted` token.
254
+ - **fix(Modal)**: ModalHeader's close `×` button picks up a
255
+ `--ck-bg-muted` background on hover + a subtle border-radius. The
256
+ previous styles only set color + cursor, so the corner control
257
+ looked like static decoration. Same hover token + transition as the
258
+ rest of the kit's interactive surfaces so the affordance reads
259
+ consistently across light / dark / chrome variants.
260
+
261
+ ## 0.4.7 (2026-06-24)
262
+
263
+ - **fix(actionbar)**: `useActionBarItems` no longer freezes the
264
+ registered items on first mount when the array length + item
265
+ `key`s are stable. Previously the hook wrapped the caller's
266
+ array in `useMemo(() => items, [items.length, keys.join('|')])`
267
+ to throttle re-registration; that gate suppressed identity
268
+ updates whenever length + keys matched, which is exactly the
269
+ case for a selection-mode toolbar with fixed buttons whose
270
+ `onClick`s close over changing state (e.g. the current selection
271
+ set). The registered items kept their first render's closures,
272
+ so every click after the first shipped stale state to the
273
+ consumer — observed in product-meta as Trash bulk-restore +
274
+ bulk-purge sending only the first-selected id even when the
275
+ user had picked many. The gate is gone; the provider's existing
276
+ shallow item-identity dedup in `register()` is now the single
277
+ source of truth. Consumers MUST `useMemo` their items array
278
+ (already in the JSDoc) — without it, every render allocates new
279
+ item objects and the effect loops `register → setState →
280
+ re-render → register`. The hook docstring now spells out the
281
+ trade-off explicitly.
282
+
283
+ ## 0.4.6 (2026-06-20)
284
+
285
+ - **fix(actionbar)**: ActionBarMenuGroup child-button hover bumps
286
+ from 18% accent blend to 30%. The 18% overlay introduced in 0.4.5
287
+ layered on top of the wrapper's `--ck-accent-muted` (already
288
+ 8–14% accent per chrome) composited to roughly 30% effective
289
+ saturation — at that low saturation a translucent orange / coral /
290
+ blue all wash out to salmon, so the hover lost its hue identity
291
+ next to the solid `--ck-accent` text it sits beside. 30% pushes
292
+ the overlay past the "dilution reads as pink" point so the
293
+ workspace's actual brand colour is recognisable on hover.
294
+
295
+ ## 0.4.5 (2026-06-20)
296
+
297
+ - **fix(actionbar)**: child-button hover state inside an open
298
+ disclosure group now uses an accent-blended overlay instead of
299
+ neutral `--ck-bg-muted` gray. The default `.ck-actionbar-btn:hover`
300
+ rule paints gray; that's correct for buttons sitting on the app
301
+ background, but the disclosure wrapper paints itself
302
+ `--ck-accent-muted` (a coral / indigo pill in editorial / base
303
+ chromes) while open, so child hover was stacking gray on top of
304
+ the brand-tinted pill — visually muddy and the hover signal lost
305
+ its hue. `ActionBarMenuGroup` now stamps a
306
+ `ck-actionbar-group--open` class on its wrapper while expanded;
307
+ a scoped rule overrides the child hover to
308
+ `color-mix(in oklab, var(--ck-accent) 18%, transparent)` so the
309
+ hover stays in the accent family. Closed groups + standalone
310
+ buttons are untouched. Consumers don't need to change anything —
311
+ bump the dep version and the fix lands.
312
+
313
+ ## 0.4.4 (2026-06-15)
314
+
315
+ - **fix(chrome)**: primary-button text colour now reads from
316
+ `var(--ck-accent-fg, <chrome default>)` in editorial / ambient /
317
+ sketch / riso / neobrutalism chromes. Previously every chrome
318
+ hardcoded its text color to pair with its OWN signature accent
319
+ (editorial near-black on coral; ambient white on saturated blue;
320
+ sketch paper-white on sketch-blue; riso near-black on pink;
321
+ neobrutalism black on pastel). When a consumer stamped a runtime
322
+ brand override via `--ck-accent-light-override`, the accent
323
+ background flipped to the brand colour but the text stayed on
324
+ the chrome default — a dark brand colour against a chrome's
325
+ near-black text yielded illegible buttons (e.g. editorial coral
326
+ → `#0F0F0F` text was fine, but brand `#000000` → `#0F0F0F` text
327
+ was invisible). Consumer apps can now compute a contrast-aware
328
+ foreground colour from the chosen accent (e.g. via WCAG relative
329
+ luminance) and stamp `--ck-accent-fg` alongside the override knob,
330
+ and every chrome respects it. Without an override the chrome's
331
+ documented default fg still applies, so no visual change for
332
+ in-the-box usage. Swiss chrome already used `var(--ck-bg-canvas)`
333
+ for primary text (not tied to accent), so it's untouched.
334
+
335
+ ## 0.4.3 (2026-06-15)
336
+
337
+ - **fix(tokens)**: respect the documented `--ck-accent-light-override` /
338
+ `--ck-accent-dark-override` brand-override knob in every chrome that
339
+ hardcoded accent shades. Previously `editorial`, `riso`, and `sketch`
340
+ set `--ck-accent` to a literal hex (and `--ck-accent-hover` / `-active`
341
+ to hand-tuned shades), which silently bypassed the override chain —
342
+ consumer apps stamping their brand colour via the documented mechanism
343
+ saw chrome stay on the platform palette. Each chrome now sets its
344
+ signature colour as the `var(--ck-accent-light-override, <chrome
345
+ default>)` fallback and derives hover/active via `color-mix(in oklab,
346
+ var(--ck-accent), black 10% / 18%)` (matching the default `:root`
347
+ formula). Sketch additionally swaps two `rgba(... blue-literal ...)`
348
+ muted/border values for `color-mix(var(--ck-accent) NN%, transparent)`
349
+ so they track the brand. Net: stamping `--ck-accent-light-override` on
350
+ `documentElement.style` now cascades to the whole accent family across
351
+ every chrome, single source of truth restored. Consumer impact:
352
+ product-meta's `BrandProvider` can drop its accent-family mirror
353
+ workaround (a98324f) and go back to stamping just the override knob.
354
+
355
+ ## 0.4.2 (2026-06-06)
356
+
357
+ - **fix(input)**: add `minWidth: 0` to the `.ck-input-field` wrapper so it
358
+ can shrink past its inner `.ck-input-addon-wrap`'s nowrap suffix when
359
+ hosted inside a flex/grid parent. Long suffixes (e.g. `.meta.test.cosx.dev`)
360
+ previously set a min-content floor that pushed the field past mobile
361
+ viewports — product-meta's onboarding (`CreateWorkspace`, `ActivatePersonal`)
362
+ and the workspace-name form on Landing all overflowed on iPhone Pro
363
+ widths. Per-page CSS workarounds in consumer apps only covered one
364
+ shell scope; fixing it at the primitive catches every page.
365
+ - **fix(input)**: real disabled visual on `.ck-input` / `.ck-textarea` —
366
+ `opacity: 0.55`, `cursor: not-allowed`, muted background. Mirrors the
367
+ existing `.ck-btn:disabled` treatment. Disabled inputs on a cream /
368
+ dark canvas were previously almost indistinguishable from editable
369
+ ones (the QA report flagged this for product-meta's Profile Email
370
+ field which is read-only pending the email-change verification flow).
371
+ The `.ck-input-addon-wrap:has(:disabled)` selector dims the suffix /
372
+ prefix along with the input so a disabled `slug + suffix` stack reads
373
+ as one disabled unit. `:has()` is supported across all evergreen
374
+ browsers since 2023.
375
+
376
+ ## 0.4.1 (2026-06-01)
377
+
378
+ - **feat(fonts)**: add Noto Serif SC to the editorial-chrome `--ck-font-serif`
379
+ fallback stack so CJK names render in a serif matching Playfair Display
380
+ rather than the system *sans-serif* CJK fallback (PingFang SC / SimSun) the
381
+ browser would otherwise pick. Names like "本杰明 Zoë" now read as one
382
+ coherent typographic line cross-platform without depending on the user
383
+ having Songti SC / Source Han Serif SC installed locally.
384
+
385
+ Bandwidth shape: Noto Serif SC ships from Google Fonts as ~100
386
+ unicode-range subsets. Pure-Latin pages pay only the CSS file (~5-10KB
387
+ gzip) — no .woff2 binaries fetch. Pages with Chinese names pay an
388
+ additional ~200-400KB of CJK subset binaries (only the ranges they use).
389
+ Sans + mono slots intentionally stay system-only — PingFang SC /
390
+ Microsoft YaHei is what users expect for UI body copy.
391
+
392
+ ## 0.4.0 (2026-06-01)
393
+
394
+ - **feat(fonts)**: load Geist + Geist Mono + Playfair Display + Caveat from
395
+ Google Fonts CDN instead of the prior self-hosted `@font-face` blocks.
396
+ Closes a long-standing "Failed to decode downloaded font: …/fonts/Geist-Regular.otf"
397
+ warning that fired on every page load in consumer SPAs that didn't ship
398
+ the OTF files under their `/fonts/` route (the Cloudflare SPA fallback
399
+ was returning index.html with `Content-Type: text/html` and the browser's
400
+ font decoder was rejecting it). Consumers no longer need to provision
401
+ `public/fonts/` themselves.
402
+ - **feat(fonts)**: append CJK system-font fallbacks to `--ck-font-sans` /
403
+ `--ck-font-mono` / `--ck-font-serif`. Names containing 中文 / 日本語 /
404
+ 한국어 (e.g. "本杰明 Zoë") now render in the matching serif/sans
405
+ system font (PingFang SC on macOS, Microsoft YaHei on Windows, etc.)
406
+ instead of dropping into the browser's last-resort glyph. No new web
407
+ font is loaded — every modern OS already ships at least one of the
408
+ listed CJK families.
409
+
410
+ ## 0.3.4 (2026-05-31)
411
+
412
+ - **fix(actionbar)**: bar now has symmetric horizontal padding
413
+ (`0 6px` instead of `0 6px 0 0`). Previously the right-only padding
414
+ pushed the leading items a few pixels left of the bar's true
415
+ centre — the grip touched the left curve while the rightmost
416
+ element (now the status dot) had visible breathing room. With
417
+ 0.3.3's leading spacer the imbalance was small but visible.
418
+ - **fix(actionbar)**: leading + trailing spacers now both use
419
+ `minWidth: 0` instead of `0` and `12` respectively. Cosmetic
420
+ alignment — `0` is the right neutral value for a spacer that's
421
+ expected to grow into available room rather than enforce a
422
+ minimum gap.
423
+
424
+ ## 0.3.3 (2026-05-31)
425
+
426
+ - **fix(actionbar)**: leading items centre between the grip and the
427
+ status dot when no trailing items are present. Previously a solo
428
+ leading item (e.g. "Theme · Light") visually packed next to the
429
+ grip leaving an unbalanced gap before the status dot. Now a
430
+ balancing flex spacer is inserted to the LEFT of the leading
431
+ group whenever it's the only content + the right side holds only
432
+ a status dot. When trailing items ARE present, they retain their
433
+ right-anchor role and leading goes back to natural left packing.
434
+
435
+ ## 0.3.2 (2026-05-31)
436
+
437
+ - **fix(actionbar)**: bar now renders when the only consumer is
438
+ `useActionBarStatusDot` (no items registered). The empty-state
439
+ guard previously checked `items.length === 0` only, so a
440
+ status-dot-only surface would render nothing.
441
+
442
+ ## 0.3.1 (2026-05-31)
443
+
444
+ - **feat(actionbar)**: bar-intrinsic `statusDot` slot at the right
445
+ edge, mirroring the left-edge drag grip. Registered via
446
+ `useActionBarStatusDot({color, title?, onClick?, pulse?} | null)`.
447
+ Unlike `useActionBarItems`, the status dot is system chrome — not
448
+ page-level content — so the API is a single hook with last-call-
449
+ wins semantics (no source-key fan-out). Driven by `product-meta`
450
+ needing a fixed sync indicator that visually anchors to the bar
451
+ rather than registering as a registry item (the trailing slot
452
+ worked but conflated system status with page actions). Exports
453
+ `ActionBarStatusDot` type + `useActionBarStatusDot` hook.
454
+
455
+ ## 0.3.0 (2026-05-31)
456
+
457
+ - **feat(actionbar)**: `ActionBarItem` gains a `slot?: 'leading' |
458
+ 'trailing'` field. Trailing items render after a flex spacer so
459
+ they pin to the right edge of the bar regardless of registration
460
+ order — system status indicators (sync, identity, connection)
461
+ belong here, where page items registering later can't shuffle
462
+ them. Default `'leading'` preserves existing behavior; this is a
463
+ purely additive change. Exports `ActionBarItemSlot` from the
464
+ bucket index. Driven by product-meta needing a stable home for
465
+ the SWR sync-status indicator on dash AND inside workspace SPAs.
466
+
467
+ ## 0.2.10 (2026-05-30)
468
+
469
+ - **fix**: Add `text-decoration: none` to `.ck-btn` so `<a class="ck-btn">`
470
+ CTAs render flat instead of inheriting the browser's default
471
+ anchor underline. Consumers using Tailwind preflight had this
472
+ masked because Tailwind resets `<a>` decoration globally; mesh's
473
+ embedded auth pages (no Tailwind) surfaced the underline on the
474
+ verify-email success page's "Continue to [Product] →" anchor.
475
+
476
+ ## 0.2.9 (2026-05-30)
477
+
478
+ - **fix**: Add a global `*, *::before, *::after { box-sizing: border-box }`
479
+ reset to `base.css`. Every modern CSS reset ships this; without it,
480
+ `min-height: 100vh` + padding extends elements beyond the viewport
481
+ (default `content-box` stacks padding on top of the declared min-height)
482
+ and creates a sneaky scroll on any consumer page that combines those
483
+ two properties. Caught in mesh's `body.mesh-auth-page` where a green
484
+ flash alert pushed a reset-password page slightly past 100vh and the
485
+ whole page became scrollable. Consumers using Tailwind preflight
486
+ already had this rule via Tailwind; consumers without it (like mesh's
487
+ embedded auth pages) now pick it up here.
488
+
489
+ ## 0.2.8 (2026-05-29)
490
+
491
+ - **feat**: `ActionBarButton` wraps the `icon` prop in a
492
+ `.ck-actionbar-icon` span. Lifts unicode glyphs (◐ ☀ ☾ ◇) to
493
+ 16 px and applies a 1 px optical-centre nudge so they read on
494
+ par with the heavier label text. Callers can drop any local
495
+ wrappers and pass icons as bare strings / SVG nodes.
496
+
497
+ ## 0.2.7 (2026-05-28)
498
+
499
+ - **fix**: `Select` popover used to close on ANY scroll event
500
+ (including the option list's own internal scroll), so a user
501
+ trying to scroll through a long list would see the popover
502
+ vanish under their cursor. Scrolls that originate inside the
503
+ popover are now filtered out; outer / page scrolls reposition
504
+ the popover against the trigger instead of closing it.
505
+
506
+ ## 0.2.6 (2026-05-28)
507
+
508
+ - **fix**: `Select` popover was clipped by ancestors with
509
+ `overflow: hidden` (Card, Drawer, Dialog). Popover now renders
510
+ via `createPortal` to `document.body` with `position: fixed`
511
+ computed against the trigger's bounding rect — escapes any
512
+ parent's clip box and stacks above sibling content. Closes on
513
+ page scroll to avoid drifting off the trigger.
514
+ - **feat**: `Select` gains `searchable` + `searchPlaceholder`.
515
+ When `searchable={true}` the popover renders a search input
516
+ pinned at the top that filters options by case-insensitive
517
+ label substring. Keyboard model on the input matches Radix /
518
+ shadcn Combobox: Arrows navigate filtered list, Enter commits
519
+ highlighted, Esc closes, Tab advances focus.
520
+
521
+ ## 0.2.5 (2026-05-28)
522
+
523
+ - **feat**: new `Select` primitive. Custom listbox (NOT native
524
+ `<select>`) so the popup styling actually responds to chrome
525
+ overrides — native `<select>` popups are browser-locked on every
526
+ OS, which used to punch through the design system with macOS
527
+ blue on terminal/editorial dark mode.
528
+ - Trigger renders the same shape as `Input`; chromes that restyle
529
+ `.ck-input` automatically pick up `.ck-select-trigger` siblings.
530
+ - ARIA combobox / listbox roles; full keyboard support
531
+ (Space/Enter open, Arrows + Home/End navigate, Enter commits,
532
+ Esc closes restoring previous value, Tab advances, A-Z/0-9
533
+ typeahead with 500 ms reset).
534
+ - Optional `name` prop emits a hidden `<input>` so plain `<form>`
535
+ submits still carry the value.
536
+ - Terminal chrome override included; other chromes inherit
537
+ sensible defaults via token consumption (extend at the chrome
538
+ file as their look diverges).
539
+
540
+ ## 0.2.4 (2026-05-28)
541
+
542
+ - **fix**: bare `<a>` elements now default to `--ck-accent` (with
543
+ a clean 1 px underline + `var(--ck-accent-hover)` on hover) and
544
+ hold accent through the `:visited` state. Without this rule the
545
+ browser's default visited-purple bled through every chrome —
546
+ visible on terminal (green-on-black landed indigo-on-black) and
547
+ editorial (coral landed purple). Components that style their own
548
+ anchors (`NavItem`, `TopBar` nav, breadcrumbs) keep winning via
549
+ more-specific selectors; this is a strictly-additive base rule.
550
+
551
+ ## 0.2.3 (2026-05-28)
552
+
553
+ - **fix**: `Input` with `prefix` / `suffix` rendered with no left
554
+ padding under the `swiss` chrome — swiss strips `padding-left`
555
+ from `.ck-input` for the underline-only standalone look, but
556
+ that made the input text collide with the addon slab. Restored
557
+ padding for the `.ck-input--with-addon` variant; the swiss
558
+ underline is now drawn on the OUTER wrap so the whole field
559
+ (addons + input) reads as one underlined bar.
560
+
561
+ ## 0.2.2 (2026-05-28)
562
+
563
+ > Note: `ui-v0.2.1` exists as a git tag but was never published to
564
+ > npm (publish workflow waiting for OTP at the time the fixes below
565
+ > were folded in). Consumers should ignore 0.2.1 — use 0.2.2.
566
+
567
+ - **fix**: `Input` addon (`prefix` / `suffix`) visual contrast.
568
+ Previously the addon shared the input's `--ck-bg-surface`
569
+ background and relied on a hard `1px` divider for separation,
570
+ which read as a vertical cut across an otherwise homogeneous
571
+ field. Switched to `--ck-bg-muted` (the canonical "recessed
572
+ slab" token) and removed the divider — the bg shift carries the
573
+ separation across every chrome and dark variant.
574
+ - **fix**: `Input` default height 34 → 36 px to line up with the
575
+ default `Button` height (also 36 px under editorial; matches the
576
+ shadcn/ui + Mantine convention). The previous 2 px short-fall
577
+ made the field read as a size smaller than buttons next to it.
578
+
579
+ ## 0.2.0 (2026-05-28)
580
+
581
+ - **breaking**: remove `frutiger` chrome. The preset's tokens, CSS,
582
+ and components (`SkyBackdrop`, `GlossyOrb`) are deleted; the
583
+ `Chrome` type union no longer includes `"frutiger"`. Consumers
584
+ using it should switch to `ambient` (closest spiritual match).
585
+ - **fix**: ThemeProvider now persists every built-in chrome through
586
+ reloads, not just `classic` / `seamless` (cosxai/product-design#2).
587
+ `BUILTIN_CHROMES` is the new source of truth — exported from
588
+ `@cosxai/ui` so consumers can use it for chrome pickers.
589
+ - **feat**: `Input` gains optional `prefix` + `suffix` props for
590
+ inline addons inside the bordered field (e.g. `acme.cosx.dev`
591
+ workspace pickers, currency symbols, search icons). The native
592
+ HTML `prefix` RDFa attribute is now Omitted from the `InputProps`
593
+ surface — consumers needing it can drop to a raw `<input>`.
594
+
595
+ ## 0.1.0 (2026-05-26)
596
+
597
+ - Initial public release on npm.
package/NOTICE ADDED
@@ -0,0 +1,11 @@
1
+ @cosxai/ui
2
+ © 2026 COSINE X LTD. All rights reserved. Proprietary — see package.json
3
+ ("UNLICENSED"): no licence is granted to use, copy or modify this package.
4
+
5
+ The COSX Design System 3.0 tokens in src/tokens/ are COSX's own.
6
+
7
+ Fonts are dependencies, not part of this package, each under its own licence:
8
+ Geist (Vercel) — SIL Open Font License 1.1, via @fontsource-variable/geist
9
+ Noto Sans SC (Google) — SIL Open Font License 1.1, via @fontsource-variable/noto-sans-sc
10
+
11
+ Code adapted from third-party projects will be listed here with its licence.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # @cosxai/ui
2
+
3
+ The COSX Design System 3.0 in React: tokens, a Tailwind v4 theme and
4
+ (from the 1.0 alphas on) components. The design lives at
5
+ [design.cosx.co](https://design.cosx.co); this package is its
6
+ implementation.
7
+
8
+ > **1.x is a new system.** 0.x (the `--ck-*` kit with its presets) is
9
+ > maintained on the `ui-0.x` branch and published under the `v0` dist-tag;
10
+ > `^0.x` ranges keep getting its fixes. 1.0 alphas are published under
11
+ > `next` — install with `@cosxai/ui@next`.
12
+
13
+ ## Use
14
+
15
+ ```css
16
+ /* app.css — Tailwind v4 app */
17
+ @import "tailwindcss";
18
+ @import "@cosxai/ui/theme.css";
19
+ @import "@cosxai/ui/styles.css";
20
+ @source "../node_modules/@cosxai/ui/src";
21
+ ```
22
+
23
+ Without Tailwind, `@cosxai/ui/styles.css` alone gives the tokens and fonts
24
+ as CSS variables (`var(--bg-page)`, `var(--radius-md)` …).
25
+
26
+ **Ink (dark) mode:** `data-mode="ink"` on `<html>`, or `.ink-mode` on a
27
+ subtree. The semantic utilities (`bg-page`, `text-fg`, `border-rule` …)
28
+ switch with it — no `dark:` variants.
29
+
30
+ ## What the theme gives you
31
+
32
+ | Kind | Utilities | Notes |
33
+ |---|---|---|
34
+ | Surfaces | `bg-page` `bg-sunk` `bg-well` `bg-chrome` `bg-field` | switch with ink mode |
35
+ | Type colour | `text-fg` `text-fg-secondary` `text-fg-label` | |
36
+ | Materials | `bg-paper` `bg-linen` `bg-ink` `bg-yellow` `bg-yellow-accent` … | fixed |
37
+ | Status | `attention` `error` (+ `-wash`, `error-text`) | yellow, ink and one red only |
38
+ | Radius | `rounded-xs` 4 · `sm` 6 · `md` 8 · `lg` 16 · `xl` 24 · `pill` | |
39
+ | Size | `text-meta` 12 · `small` 13.5 · `ui` 14 · `body` 15 · `h3` 18 · `lede` 20 · `title` 22 · `figure` 32 · `h2` `h1` `display` | |
40
+ | Family | `font-sans`, `font-sans-cjk` | Geist + Noto Sans SC, self-hosted |
41
+ | Motion | `ease-standard` `ease-out` `ease-in` | |
42
+
43
+ Tailwind's own palette, radii, shadows and easings are removed on purpose:
44
+ 3.0 has no shadows and one yellow.
45
+
46
+ ## Develop
47
+
48
+ `pnpm test` (theme ↔ tokens agreement, `cn`), `pnpm check:css` (a real
49
+ Tailwind build of the theme), `pnpm typecheck`.
50
+
51
+ `src/tokens/` are copied unchanged from the "COSX Design System 3.0"
52
+ Claude Design project — change them there, copy again.