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