@cueplusplus/ui 0.1.0 → 0.2.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 +684 -0
- package/dist/brand/_chassis.d.ts +54 -0
- package/dist/brand/_chassis.js +81 -0
- package/dist/brand/_geometry.js +41 -0
- package/dist/brand/cue-logotype.d.ts +29 -0
- package/dist/brand/cue-logotype.js +60 -0
- package/dist/brand/cue-mark.d.ts +30 -0
- package/dist/brand/cue-mark.js +61 -0
- package/dist/brand/index.d.ts +5 -0
- package/dist/brand/plussie.d.ts +67 -0
- package/dist/brand/plussie.js +125 -0
- package/dist/chat/agent-pile.js +1 -1
- package/dist/chat/ask-box.js +2 -2
- package/dist/chat/delegation-card.js +1 -1
- package/dist/chat/message.js +2 -2
- package/dist/chrome/_edge-scroller.d.ts +45 -0
- package/dist/chrome/_edge-scroller.js +138 -0
- package/dist/chrome/_glyphs.js +19 -1
- package/dist/chrome/_tabs-scroll.d.ts +12 -7
- package/dist/chrome/_tabs-scroll.js +12 -7
- package/dist/chrome/app-bar.d.ts +103 -0
- package/dist/chrome/app-bar.js +175 -0
- package/dist/chrome/app-shell.d.ts +125 -0
- package/dist/chrome/app-shell.js +167 -0
- package/dist/chrome/footer.d.ts +75 -0
- package/dist/chrome/footer.js +68 -0
- package/dist/chrome/index.d.ts +5 -1
- package/dist/chrome/index.js +4 -1
- package/dist/chrome/navigation-menu.js +1 -1
- package/dist/chrome/page-shell.d.ts +1 -1
- package/dist/chrome/page-shell.js +15 -5
- package/dist/chrome/segmented-control.d.ts +19 -0
- package/dist/chrome/segmented-control.js +40 -21
- package/dist/chrome/tabs.js +21 -65
- package/dist/chrome/toolbar.d.ts +22 -0
- package/dist/chrome/toolbar.js +26 -5
- package/dist/configurator/_overrides.js +4 -2
- package/dist/configurator/configurator.js +1 -1
- package/dist/configurator/panel-sections.js +10 -4
- package/dist/dmx/channel-matrix.js +1 -1
- package/dist/forms/otp-field.js +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.js +15 -9
- package/dist/instruments/data-table.js +1 -1
- package/dist/layout/carousel.js +1 -1
- package/dist/layout/container.d.ts +1 -1
- package/dist/layout/container.js +6 -3
- package/dist/layout/index.d.ts +3 -3
- package/dist/layout/index.js +3 -3
- package/dist/layout/pagination.js +1 -1
- package/dist/layout/sidebar.d.ts +23 -3
- package/dist/layout/sidebar.js +18 -7
- package/dist/midi/spectrum-visualizer.js +1 -1
- package/dist/midi/timeline-ruler.js +1 -1
- package/dist/overlays/command-palette.js +1 -1
- package/dist/overlays/dialog.js +1 -1
- package/dist/overlays/dropdown-menu.js +2 -2
- package/dist/overlays/hover-card.js +1 -1
- package/dist/overlays/index.js +1 -1
- package/dist/overlays/popover.js +1 -1
- package/dist/overlays/sheet.js +1 -1
- package/dist/overlays/toast.js +1 -1
- package/dist/primitives/index.d.ts +1 -1
- package/dist/primitives/index.js +1 -1
- package/dist/primitives/status-dot.d.ts +7 -0
- package/dist/primitives/status-dot.js +8 -1
- package/dist/styles.css +47 -1
- package/dist/system/density.d.ts +4 -1
- package/dist/system/density.js +12 -4
- package/dist/system/index.d.ts +2 -2
- package/dist/system/portal.d.ts +5 -5
- package/dist/system/portal.js +11 -6
- package/dist/system/prepaint.d.ts +15 -8
- package/dist/system/prepaint.js +26 -9
- package/dist/system/theme-provider.d.ts +36 -10
- package/dist/system/theme-provider.js +56 -18
- package/dist/system/use-density.d.ts +3 -3
- package/dist/system/use-density.js +16 -6
- package/dist/system/use-theme.d.ts +4 -2
- package/dist/system/use-theme.js +4 -2
- package/dist/theming/_presets.js +80 -5
- package/dist/theming/create-theme.js +13 -4
- package/package.json +4 -3
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { useIsomorphicLayoutEffect } from "./use-isomorphic-layout-effect.js";
|
|
4
|
-
import { DENSITY_LEVELS, DensityContext } from "./density.js";
|
|
4
|
+
import { DEFAULT_DENSITY as DEFAULT_DENSITY$1, DENSITY_LEVELS, DensityContext } from "./density.js";
|
|
5
5
|
import { DEFAULT_STORAGE_KEY } from "./prepaint.js";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
import { DEFAULT_FONT, FONTS } from "@cueplusplus/tokens";
|
|
8
9
|
//#region src/system/theme-provider.tsx
|
|
9
10
|
const MODES = [
|
|
10
11
|
"dark",
|
|
11
12
|
"light",
|
|
12
13
|
"system"
|
|
13
14
|
];
|
|
15
|
+
const FONT_NAMES = FONTS;
|
|
14
16
|
/** Dark-first portfolio defaults; the same triple bare `:root` in `theme.css` paints. */
|
|
15
17
|
const DEFAULT_THEME = "cue";
|
|
16
|
-
const DEFAULT_DENSITY = "normal";
|
|
17
18
|
const DEFAULT_MODE = "dark";
|
|
18
19
|
const DEFAULT_RESOLVED_MODE = "dark";
|
|
19
20
|
/**
|
|
@@ -56,62 +57,82 @@ function writePreferences(storageKey, preferences) {
|
|
|
56
57
|
}
|
|
57
58
|
/**
|
|
58
59
|
* Root of the theme system: stamps `data-theme`, `data-density` and `data-mode`
|
|
59
|
-
* so the token layer resolves,
|
|
60
|
-
* persisted user preference.
|
|
60
|
+
* so the token layer resolves, puts `data-font` on `<html>`, publishes
|
|
61
|
+
* `--cue-font-scale`, and owns the persisted user preference.
|
|
61
62
|
*
|
|
62
63
|
* Rendering: `<div data-cue-root data-theme data-density data-mode style={{ colorScheme, --cue-font-scale }}>`
|
|
63
|
-
* (or the single child when `asChild`). The `theme`/`density`/`mode` props are
|
|
64
|
+
* (or the single child when `asChild`). The `theme`/`density`/`font`/`mode` props are
|
|
64
65
|
* *initial* values — the provider holds the state so `setTheme` and friends can
|
|
65
66
|
* drive it — but a changed prop is adopted after mount, so a controlling parent
|
|
66
67
|
* still works.
|
|
67
68
|
*
|
|
69
|
+
* The font pairing is the one axis that never lands on this element: it has no
|
|
70
|
+
* island form, `<html>` is its only home, and restating it here would shadow the
|
|
71
|
+
* pre-paint stamp for the whole page on the first frame. See the `stamp` object
|
|
72
|
+
* below.
|
|
73
|
+
*
|
|
68
74
|
* Persistence and the pre-paint contract: setters write
|
|
69
|
-
* `{ theme, density, mode }` to `localStorage[storageKey]`, which is exactly what
|
|
75
|
+
* `{ theme, density, font, mode }` to `localStorage[storageKey]`, which is exactly what
|
|
70
76
|
* {@link prepaintScript} reads to stamp `<html>` before the first paint. The
|
|
71
77
|
* outermost provider keeps `<html>` in sync while the app runs (and restores the
|
|
72
78
|
* previous stamp on unmount) so the page ground, UA scrollbars and form controls
|
|
73
|
-
* follow the theme; nested providers never touch `<html
|
|
79
|
+
* follow the theme; nested providers never touch `<html>`, and never own the
|
|
80
|
+
* font pairing — `useTheme().font` and `setFont` inside one are the root's.
|
|
74
81
|
*
|
|
75
82
|
* @example
|
|
76
|
-
* <ThemeProvider theme="terminal" density="ultra-compact" mode="system">
|
|
83
|
+
* <ThemeProvider theme="terminal" density="ultra-compact" font="plex" mode="system">
|
|
77
84
|
* <App />
|
|
78
85
|
* </ThemeProvider>
|
|
79
86
|
*/
|
|
80
|
-
function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp = DEFAULT_DENSITY, mode: modeProp = DEFAULT_MODE, fontScale = 1, storageKey = DEFAULT_STORAGE_KEY, asChild = false, className, style: styleProp, children }) {
|
|
81
|
-
const
|
|
87
|
+
function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp = DEFAULT_DENSITY$1, mode: modeProp = DEFAULT_MODE, font: fontProp = DEFAULT_FONT, fontScale = 1, storageKey = DEFAULT_STORAGE_KEY, asChild = false, className, style: styleProp, children }) {
|
|
88
|
+
const ambient = React.useContext(ThemeContext);
|
|
89
|
+
const isRoot = ambient === null;
|
|
82
90
|
const [theme, setThemeState] = React.useState(themeProp);
|
|
83
91
|
const [density, setDensityState] = React.useState(densityProp);
|
|
84
92
|
const [mode, setModeState] = React.useState(modeProp);
|
|
93
|
+
const [rootFont, setRootFontState] = React.useState(() => {
|
|
94
|
+
if (!isRoot) return fontProp;
|
|
95
|
+
const stored = readPreferences(storageKey).font;
|
|
96
|
+
return isOneOf(FONT_NAMES, stored) ? stored : fontProp;
|
|
97
|
+
});
|
|
98
|
+
const font = isRoot ? rootFont : ambient.font;
|
|
85
99
|
const [systemMode, setSystemMode] = React.useState(DEFAULT_RESOLVED_MODE);
|
|
86
100
|
const resolvedMode = mode === "system" ? systemMode : mode;
|
|
87
101
|
const restoredKey = React.useRef(null);
|
|
102
|
+
const [restored, setRestored] = React.useState(false);
|
|
88
103
|
useIsomorphicLayoutEffect(() => {
|
|
89
104
|
if (!isRoot || restoredKey.current === storageKey) return;
|
|
90
105
|
restoredKey.current = storageKey;
|
|
106
|
+
setRestored(true);
|
|
91
107
|
const stored = readPreferences(storageKey);
|
|
92
108
|
if (typeof stored.theme === "string" && stored.theme.length > 0) setThemeState(stored.theme);
|
|
93
109
|
if (isOneOf(DENSITY_LEVELS, stored.density)) setDensityState(stored.density);
|
|
110
|
+
if (isOneOf(FONT_NAMES, stored.font)) setRootFontState(stored.font);
|
|
94
111
|
if (isOneOf(MODES, stored.mode)) setModeState(stored.mode);
|
|
95
112
|
}, [isRoot, storageKey]);
|
|
96
113
|
const lastProps = React.useRef({
|
|
97
114
|
theme: themeProp,
|
|
98
115
|
density: densityProp,
|
|
99
|
-
mode: modeProp
|
|
116
|
+
mode: modeProp,
|
|
117
|
+
font: fontProp
|
|
100
118
|
});
|
|
101
119
|
React.useEffect(() => {
|
|
102
120
|
const previous = lastProps.current;
|
|
103
121
|
lastProps.current = {
|
|
104
122
|
theme: themeProp,
|
|
105
123
|
density: densityProp,
|
|
106
|
-
mode: modeProp
|
|
124
|
+
mode: modeProp,
|
|
125
|
+
font: fontProp
|
|
107
126
|
};
|
|
108
127
|
if (previous.theme !== themeProp) setThemeState(themeProp);
|
|
109
128
|
if (previous.density !== densityProp) setDensityState(densityProp);
|
|
110
129
|
if (previous.mode !== modeProp) setModeState(modeProp);
|
|
130
|
+
if (previous.font !== fontProp) setRootFontState(fontProp);
|
|
111
131
|
}, [
|
|
112
132
|
themeProp,
|
|
113
133
|
densityProp,
|
|
114
|
-
modeProp
|
|
134
|
+
modeProp,
|
|
135
|
+
fontProp
|
|
115
136
|
]);
|
|
116
137
|
useIsomorphicLayoutEffect(() => {
|
|
117
138
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
@@ -124,13 +145,15 @@ function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp
|
|
|
124
145
|
const stateRef = React.useRef({
|
|
125
146
|
theme,
|
|
126
147
|
density,
|
|
127
|
-
mode
|
|
148
|
+
mode,
|
|
149
|
+
font
|
|
128
150
|
});
|
|
129
151
|
useIsomorphicLayoutEffect(() => {
|
|
130
152
|
stateRef.current = {
|
|
131
153
|
theme,
|
|
132
154
|
density,
|
|
133
|
-
mode
|
|
155
|
+
mode,
|
|
156
|
+
font
|
|
134
157
|
};
|
|
135
158
|
});
|
|
136
159
|
const persist = React.useCallback((patch) => {
|
|
@@ -154,24 +177,34 @@ function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp
|
|
|
154
177
|
setModeState(next);
|
|
155
178
|
persist({ mode: next });
|
|
156
179
|
}, [persist]);
|
|
180
|
+
const rootSetFont = React.useCallback((next) => {
|
|
181
|
+
setRootFontState(next);
|
|
182
|
+
persist({ font: next });
|
|
183
|
+
}, [persist]);
|
|
184
|
+
/** Nested providers hand the axis straight up: one document, one pairing. */
|
|
185
|
+
const setFont = isRoot ? rootSetFont : ambient.setFont;
|
|
157
186
|
const originalStamp = React.useRef(null);
|
|
158
187
|
useIsomorphicLayoutEffect(() => {
|
|
159
|
-
if (!isRoot || typeof document === "undefined") return;
|
|
188
|
+
if (!isRoot || !restored || typeof document === "undefined") return;
|
|
160
189
|
const root = document.documentElement;
|
|
161
190
|
originalStamp.current ??= {
|
|
162
191
|
theme: root.getAttribute("data-theme"),
|
|
163
192
|
density: root.getAttribute("data-density"),
|
|
193
|
+
font: root.getAttribute("data-font"),
|
|
164
194
|
mode: root.getAttribute("data-mode"),
|
|
165
195
|
colorScheme: root.style.colorScheme
|
|
166
196
|
};
|
|
167
197
|
root.setAttribute("data-theme", theme);
|
|
168
198
|
root.setAttribute("data-density", density);
|
|
199
|
+
root.setAttribute("data-font", font);
|
|
169
200
|
root.setAttribute("data-mode", resolvedMode);
|
|
170
201
|
root.style.colorScheme = resolvedMode;
|
|
171
202
|
}, [
|
|
172
203
|
isRoot,
|
|
204
|
+
restored,
|
|
173
205
|
theme,
|
|
174
206
|
density,
|
|
207
|
+
font,
|
|
175
208
|
resolvedMode
|
|
176
209
|
]);
|
|
177
210
|
React.useEffect(() => () => {
|
|
@@ -185,6 +218,7 @@ function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp
|
|
|
185
218
|
};
|
|
186
219
|
restore("data-theme", original.theme);
|
|
187
220
|
restore("data-density", original.density);
|
|
221
|
+
restore("data-font", original.font);
|
|
188
222
|
restore("data-mode", original.mode);
|
|
189
223
|
root.style.colorScheme = original.colorScheme;
|
|
190
224
|
}, []);
|
|
@@ -192,18 +226,22 @@ function ThemeProvider({ theme: themeProp = DEFAULT_THEME, density: densityProp
|
|
|
192
226
|
theme,
|
|
193
227
|
density,
|
|
194
228
|
mode,
|
|
229
|
+
font,
|
|
195
230
|
resolvedMode,
|
|
196
231
|
setTheme,
|
|
197
232
|
setDensity,
|
|
198
|
-
setMode
|
|
233
|
+
setMode,
|
|
234
|
+
setFont
|
|
199
235
|
}), [
|
|
200
236
|
theme,
|
|
201
237
|
density,
|
|
202
238
|
mode,
|
|
239
|
+
font,
|
|
203
240
|
resolvedMode,
|
|
204
241
|
setTheme,
|
|
205
242
|
setDensity,
|
|
206
|
-
setMode
|
|
243
|
+
setMode,
|
|
244
|
+
setFont
|
|
207
245
|
]);
|
|
208
246
|
const style = React.useMemo(() => ({
|
|
209
247
|
colorScheme: resolvedMode,
|
|
@@ -4,11 +4,11 @@ import { Density } from "@cueplusplus/tokens";
|
|
|
4
4
|
type ControlSize = "sm" | "md" | "lg";
|
|
5
5
|
/**
|
|
6
6
|
* The density level that actually applies here: the nearest `<Density>` island,
|
|
7
|
-
* else the `<ThemeProvider>` level, else `"
|
|
7
|
+
* else the `<ThemeProvider>` level, else `"compact"`.
|
|
8
8
|
*
|
|
9
9
|
* This is a JS read for code that cannot use CSS (virtualised row heights,
|
|
10
10
|
* canvas drawing, layout math). Styling should use the density tokens or the
|
|
11
|
-
* `density-ultra:`
|
|
11
|
+
* per-rung variants (`density-ultra-compact:` … `density-ultra-large:`) instead.
|
|
12
12
|
*
|
|
13
13
|
* @returns The applicable density level.
|
|
14
14
|
*/
|
|
@@ -26,7 +26,7 @@ declare function useDensity(): Density;
|
|
|
26
26
|
* @param size - Which rung to measure. Defaults to `"md"`.
|
|
27
27
|
* @returns Height in CSS pixels.
|
|
28
28
|
* @example
|
|
29
|
-
* const rowHeight = useControlHeight("sm"); // → 20 at
|
|
29
|
+
* const rowHeight = useControlHeight("sm"); // → 20 at compact density
|
|
30
30
|
*/
|
|
31
31
|
declare function useControlHeight(size?: ControlSize): number;
|
|
32
32
|
//#endregion
|
|
@@ -14,15 +14,25 @@ const CONTROL_REM = {
|
|
|
14
14
|
md: 1.25,
|
|
15
15
|
lg: 1.5
|
|
16
16
|
},
|
|
17
|
-
|
|
17
|
+
"compact": {
|
|
18
18
|
sm: 1.25,
|
|
19
19
|
md: 1.5,
|
|
20
20
|
lg: 1.75
|
|
21
21
|
},
|
|
22
|
-
|
|
22
|
+
"normal": {
|
|
23
23
|
sm: 1.75,
|
|
24
24
|
md: 2,
|
|
25
25
|
lg: 2.25
|
|
26
|
+
},
|
|
27
|
+
"large": {
|
|
28
|
+
sm: 2.25,
|
|
29
|
+
md: 2.5,
|
|
30
|
+
lg: 2.75
|
|
31
|
+
},
|
|
32
|
+
"ultra-large": {
|
|
33
|
+
sm: 2.75,
|
|
34
|
+
md: 3,
|
|
35
|
+
lg: 3.25
|
|
26
36
|
}
|
|
27
37
|
};
|
|
28
38
|
/** Root font size assumed before the first measurement, so SSR and hydration agree. */
|
|
@@ -46,16 +56,16 @@ function measureControlHeight(density, size) {
|
|
|
46
56
|
}
|
|
47
57
|
/**
|
|
48
58
|
* The density level that actually applies here: the nearest `<Density>` island,
|
|
49
|
-
* else the `<ThemeProvider>` level, else `"
|
|
59
|
+
* else the `<ThemeProvider>` level, else `"compact"`.
|
|
50
60
|
*
|
|
51
61
|
* This is a JS read for code that cannot use CSS (virtualised row heights,
|
|
52
62
|
* canvas drawing, layout math). Styling should use the density tokens or the
|
|
53
|
-
* `density-ultra:`
|
|
63
|
+
* per-rung variants (`density-ultra-compact:` … `density-ultra-large:`) instead.
|
|
54
64
|
*
|
|
55
65
|
* @returns The applicable density level.
|
|
56
66
|
*/
|
|
57
67
|
function useDensity() {
|
|
58
|
-
return React.useContext(DensityContext) ?? "
|
|
68
|
+
return React.useContext(DensityContext) ?? "compact";
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
61
71
|
* The pixel height of a control at the ambient density — what a virtualiser or a
|
|
@@ -70,7 +80,7 @@ function useDensity() {
|
|
|
70
80
|
* @param size - Which rung to measure. Defaults to `"md"`.
|
|
71
81
|
* @returns Height in CSS pixels.
|
|
72
82
|
* @example
|
|
73
|
-
* const rowHeight = useControlHeight("sm"); // → 20 at
|
|
83
|
+
* const rowHeight = useControlHeight("sm"); // → 20 at compact density
|
|
74
84
|
*/
|
|
75
85
|
function useControlHeight(size = "md") {
|
|
76
86
|
const density = useDensity();
|
|
@@ -3,10 +3,12 @@ import { ThemeContextValue } from "./theme-provider.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Read and drive the ambient theme state.
|
|
5
5
|
*
|
|
6
|
-
* Returns `{ theme, density, mode, resolvedMode, setTheme, setDensity,
|
|
6
|
+
* Returns `{ theme, density, mode, font, resolvedMode, setTheme, setDensity,
|
|
7
|
+
* setMode, setFont }`.
|
|
7
8
|
* `density` here is the *provider's* level — a nested `<Density>` island does not
|
|
8
9
|
* change it; use `useDensity()` when you want the level that actually applies
|
|
9
|
-
* where you are.
|
|
10
|
+
* where you are. `font` is the pairing the provider holds; it has no island, so
|
|
11
|
+
* it is always the one in force.
|
|
10
12
|
*
|
|
11
13
|
* @throws If called outside a `<ThemeProvider>`. Theme state is never guessed:
|
|
12
14
|
* a missing provider is a wiring bug, not a fallback situation.
|
package/dist/system/use-theme.js
CHANGED
|
@@ -5,10 +5,12 @@ import * as React from "react";
|
|
|
5
5
|
/**
|
|
6
6
|
* Read and drive the ambient theme state.
|
|
7
7
|
*
|
|
8
|
-
* Returns `{ theme, density, mode, resolvedMode, setTheme, setDensity,
|
|
8
|
+
* Returns `{ theme, density, mode, font, resolvedMode, setTheme, setDensity,
|
|
9
|
+
* setMode, setFont }`.
|
|
9
10
|
* `density` here is the *provider's* level — a nested `<Density>` island does not
|
|
10
11
|
* change it; use `useDensity()` when you want the level that actually applies
|
|
11
|
-
* where you are.
|
|
12
|
+
* where you are. `font` is the pairing the provider holds; it has no island, so
|
|
13
|
+
* it is always the one in force.
|
|
12
14
|
*
|
|
13
15
|
* @throws If called outside a `<ThemeProvider>`. Theme state is never guessed:
|
|
14
16
|
* a missing provider is a wiring bug, not a fallback situation.
|
package/dist/theming/_presets.js
CHANGED
|
@@ -453,6 +453,59 @@ const THEME_PRESETS = {
|
|
|
453
453
|
"scrim": "rgba(0,0,0,0.35)"
|
|
454
454
|
},
|
|
455
455
|
fonts: { "font-mono": "\"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" }
|
|
456
|
+
},
|
|
457
|
+
quotamate: {
|
|
458
|
+
dark: {
|
|
459
|
+
"bg": "#0e0e12",
|
|
460
|
+
"sunken": "#09090c",
|
|
461
|
+
"surface-1": "#15151a",
|
|
462
|
+
"surface-2": "#212127",
|
|
463
|
+
"surface-3": "#2b2b31",
|
|
464
|
+
"fg": "#f4f4f6",
|
|
465
|
+
"fg-muted": "#8b8b95",
|
|
466
|
+
"fg-subtle": "#62626b",
|
|
467
|
+
"border": "#2b2b31",
|
|
468
|
+
"border-strong": "#3d3d45",
|
|
469
|
+
"border-overlay": "#3d3d45",
|
|
470
|
+
"accent": "#00e5e5",
|
|
471
|
+
"accent-hover": "#33ffff",
|
|
472
|
+
"accent-fg": "#0e0e12",
|
|
473
|
+
"ok": "#22d967",
|
|
474
|
+
"busy": "#00e5e5",
|
|
475
|
+
"warn": "#ffab1a",
|
|
476
|
+
"danger": "#e13d3d",
|
|
477
|
+
"info": "#4a9eff",
|
|
478
|
+
"selection": "rgba(0,229,229,0.18)",
|
|
479
|
+
"focus": "#00e5e5",
|
|
480
|
+
"data-ground": "#000000",
|
|
481
|
+
"scrim": "rgba(0,0,0,0.55)"
|
|
482
|
+
},
|
|
483
|
+
light: {
|
|
484
|
+
"bg": "#fcfcfd",
|
|
485
|
+
"sunken": "#f1f1f4",
|
|
486
|
+
"surface-1": "#ffffff",
|
|
487
|
+
"surface-2": "#f4f4f6",
|
|
488
|
+
"surface-3": "#e7e7ec",
|
|
489
|
+
"fg": "#18181b",
|
|
490
|
+
"fg-muted": "#6e6e78",
|
|
491
|
+
"fg-subtle": "#9a9aa4",
|
|
492
|
+
"border": "#e2e2e8",
|
|
493
|
+
"border-strong": "#c9c9d2",
|
|
494
|
+
"border-overlay": "#c9c9d2",
|
|
495
|
+
"accent": "#0d7d7d",
|
|
496
|
+
"accent-hover": "#0a6767",
|
|
497
|
+
"accent-fg": "#ffffff",
|
|
498
|
+
"ok": "#17914a",
|
|
499
|
+
"busy": "#0d7d7d",
|
|
500
|
+
"warn": "#b46a00",
|
|
501
|
+
"danger": "#c22d2d",
|
|
502
|
+
"info": "#1f6fd4",
|
|
503
|
+
"selection": "rgba(13,125,125,0.16)",
|
|
504
|
+
"focus": "#0d7d7d",
|
|
505
|
+
"data-ground": "#ffffff",
|
|
506
|
+
"scrim": "rgba(0,0,0,0.55)"
|
|
507
|
+
},
|
|
508
|
+
fonts: { "font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace" }
|
|
456
509
|
}
|
|
457
510
|
};
|
|
458
511
|
/**
|
|
@@ -461,11 +514,16 @@ const THEME_PRESETS = {
|
|
|
461
514
|
* has to beat, and `:root` carries the *default* theme's font override. A silent
|
|
462
515
|
* theme would inherit cue's JetBrains Mono instead of the base stack, so every
|
|
463
516
|
* block says what it owns. Mirrors `build.mjs`'s `THEME_FONTS` handling.
|
|
517
|
+
*
|
|
518
|
+
* Every entry is a generic or a face the platform ships. The sans stack named
|
|
519
|
+
* `Geist` until 2026-08-19 and no webfont was ever loaded to make it resolve, so
|
|
520
|
+
* it was a name most visitors silently fell straight past; Geist is now the head
|
|
521
|
+
* of the `geist` font pairing, which an app can actually deliver.
|
|
464
522
|
*/
|
|
465
523
|
const BASE_FONT_STACKS = {
|
|
466
|
-
"font-sans": "
|
|
524
|
+
"font-sans": "ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", \"Helvetica Neue\", sans-serif",
|
|
467
525
|
"font-mono": "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace",
|
|
468
|
-
"font-display": "
|
|
526
|
+
"font-display": "ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif"
|
|
469
527
|
};
|
|
470
528
|
/**
|
|
471
529
|
* The radius multiplier each density level contributes.
|
|
@@ -478,10 +536,27 @@ const BASE_FONT_STACKS = {
|
|
|
478
536
|
*/
|
|
479
537
|
const DENSITY_RADIUS_SCALE = {
|
|
480
538
|
"ultra-compact": .75,
|
|
539
|
+
"compact": 1,
|
|
481
540
|
"normal": 1,
|
|
482
|
-
"large": 1
|
|
541
|
+
"large": 1,
|
|
542
|
+
"ultra-large": 1
|
|
483
543
|
};
|
|
484
544
|
/** The default density, whose radius scale the uncrossed theme block carries. */
|
|
485
|
-
const DEFAULT_DENSITY_LEVEL = "
|
|
545
|
+
const DEFAULT_DENSITY_LEVEL = "compact";
|
|
546
|
+
/**
|
|
547
|
+
* What every theme block sets `--cue-font-mono` to.
|
|
548
|
+
*
|
|
549
|
+
* Neither the theme axis nor the font axis writes the shared token: each
|
|
550
|
+
* publishes its own half (`--cue-font-theme-mono` and `--cue-font-pairing-mono`)
|
|
551
|
+
* and this is the resolution, pairing first. It has to be a `var()` chain rather
|
|
552
|
+
* than a value because the two attributes land on different elements — a
|
|
553
|
+
* provider stamps `data-theme` on a `<div>` inside the `<html>` the pre-paint
|
|
554
|
+
* script stamped `data-font` on, and for an inherited property the nearer
|
|
555
|
+
* declaration would otherwise win whatever the stylesheet's order is.
|
|
556
|
+
*
|
|
557
|
+
* Mirrors `packages/tokens/build.mjs`'s `RESOLVED_MONO`, and
|
|
558
|
+
* `test/theming/presets.test.ts` fails when the two drift.
|
|
559
|
+
*/
|
|
560
|
+
const RESOLVED_MONO = "var(--cue-font-pairing-mono, var(--cue-font-theme-mono))";
|
|
486
561
|
//#endregion
|
|
487
|
-
export { BASE_FONT_STACKS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, THEME_COLOR_TOKENS, THEME_PRESETS };
|
|
562
|
+
export { BASE_FONT_STACKS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, RESOLVED_MONO, THEME_COLOR_TOKENS, THEME_PRESETS };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatColorString } from "../color/_convert.js";
|
|
2
|
-
import { BASE_FONT_STACKS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, THEME_COLOR_TOKENS, THEME_PRESETS } from "./_presets.js";
|
|
2
|
+
import { BASE_FONT_STACKS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, RESOLVED_MONO, THEME_COLOR_TOKENS, THEME_PRESETS } from "./_presets.js";
|
|
3
3
|
import { WCAG_AA_TEXT, contrastRatio, contrastReport } from "./contrast.js";
|
|
4
4
|
import { assertCssValue, assertThemeName, derivedTokens, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
|
|
5
5
|
import { clampChroma, converter, formatHex, inGamut, parse } from "culori";
|
|
@@ -321,14 +321,23 @@ function resolveLight(dark, preset) {
|
|
|
321
321
|
provisional
|
|
322
322
|
};
|
|
323
323
|
}
|
|
324
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* The font declarations a theme block carries.
|
|
326
|
+
*
|
|
327
|
+
* The monospace lands on `--cue-font-theme-mono`, not on `--cue-font-mono`, and
|
|
328
|
+
* the shared token is restated as the resolution of the two axes' halves —
|
|
329
|
+
* exactly what a shipped preset's block does. A generated theme that wrote the
|
|
330
|
+
* shared token directly would beat a `[data-font]` pairing stamped on any
|
|
331
|
+
* ancestor, purely by sitting nearer to the text, and a consumer's own preset
|
|
332
|
+
* would be the one preset in the system the typeface picker could not move.
|
|
333
|
+
*/
|
|
325
334
|
function resolveFonts(anchors, preset) {
|
|
326
335
|
const fonts = anchors.fonts ?? {};
|
|
327
336
|
const declared = [];
|
|
328
337
|
if (fonts.sans !== void 0) declared.push(["font-sans", fonts.sans]);
|
|
329
|
-
declared.push(["font-mono", fonts.mono ?? lookup(preset, "font-mono") ?? BASE_FONT_STACKS["font-mono"]]);
|
|
338
|
+
declared.push(["font-theme-mono", fonts.mono ?? lookup(preset, "font-mono") ?? BASE_FONT_STACKS["font-mono"]]);
|
|
330
339
|
if (fonts.display !== void 0) declared.push(["font-display", fonts.display]);
|
|
331
|
-
return declared.map(([name, value]) => [tokenProperty(name), assertCssValue(name, value)]);
|
|
340
|
+
return [...declared.map(([name, value]) => [tokenProperty(name), assertCssValue(name, value)]), [tokenProperty("font-mono"), RESOLVED_MONO]];
|
|
332
341
|
}
|
|
333
342
|
/** Round a product of two scales to something a stylesheet can print. */
|
|
334
343
|
const scaled = (a, b) => String(Number((a * b).toFixed(4)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueplusplus/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CUE++ design system components: Tailwind v4 styled wrappers over Base UI, driven by @cueplusplus/tokens.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -107,13 +107,14 @@
|
|
|
107
107
|
}
|
|
108
108
|
},
|
|
109
109
|
"files": [
|
|
110
|
-
"dist"
|
|
110
|
+
"dist",
|
|
111
|
+
"CHANGELOG.md"
|
|
111
112
|
],
|
|
112
113
|
"dependencies": {
|
|
113
114
|
"clsx": "^2.1.1",
|
|
114
115
|
"culori": "^4.0.2",
|
|
115
116
|
"tailwind-merge": "^3.6.0",
|
|
116
|
-
"@cueplusplus/tokens": "0.
|
|
117
|
+
"@cueplusplus/tokens": "0.2.0"
|
|
117
118
|
},
|
|
118
119
|
"peerDependencies": {
|
|
119
120
|
"@base-ui/react": "^1.7.0",
|