@dxos/ui-theme 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/lib/browser/index.mjs +0 -62
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +0 -62
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/plugin/node-cjs/main.css +311 -189
  8. package/dist/plugin/node-cjs/main.css.map +3 -3
  9. package/dist/plugin/node-cjs/meta.json +1 -1
  10. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs +2 -1
  11. package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs.map +3 -3
  12. package/dist/plugin/node-esm/main.css +311 -189
  13. package/dist/plugin/node-esm/main.css.map +3 -3
  14. package/dist/plugin/node-esm/meta.json +1 -1
  15. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs +2 -1
  16. package/dist/plugin/node-esm/plugins/ThemePlugin.mjs.map +3 -3
  17. package/dist/types/src/Theme.stories.d.ts.map +1 -1
  18. package/dist/types/src/defs.d.ts +0 -5
  19. package/dist/types/src/defs.d.ts.map +1 -1
  20. package/dist/types/src/fragments/hover.d.ts +0 -2
  21. package/dist/types/src/fragments/hover.d.ts.map +1 -1
  22. package/dist/types/src/fragments/index.d.ts +0 -1
  23. package/dist/types/src/fragments/index.d.ts.map +1 -1
  24. package/dist/types/src/plugins/ThemePlugin.d.ts.map +1 -1
  25. package/dist/types/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +11 -6
  27. package/src/Theme.stories.tsx +3 -1
  28. package/src/css/DESIGN_SYSTEM.md +89 -51
  29. package/src/css/base/base.css +39 -0
  30. package/src/css/components/button.css +21 -16
  31. package/src/css/components/callout.css +140 -0
  32. package/src/css/components/input.css +36 -0
  33. package/src/css/components/state.css +1 -11
  34. package/src/css/components/state.md +1 -1
  35. package/src/css/components/surface.css +165 -58
  36. package/src/css/components/tag.css +13 -1
  37. package/src/css/integrations/codemirror.css +15 -0
  38. package/src/css/layout/main.css +26 -2
  39. package/src/css/layout/native.css +1 -0
  40. package/src/css/theme/roles.css +92 -0
  41. package/src/css/theme/spacing.css +63 -54
  42. package/src/css/theme/surfaces.css +215 -0
  43. package/src/css/theme/text.css +6 -2
  44. package/src/css/utilities.css +13 -9
  45. package/src/defs.ts +0 -25
  46. package/src/fragments/hover.ts +0 -3
  47. package/src/fragments/index.ts +0 -1
  48. package/src/main.css +8 -10
  49. package/src/plugins/ThemePlugin.ts +50 -40
  50. package/src/plugins/main.css +1 -1
  51. package/src/tokens.css +22 -0
  52. package/dist/types/src/fragments/density.d.ts +0 -4
  53. package/dist/types/src/fragments/density.d.ts.map +0 -1
  54. package/src/css/components/panel.css +0 -117
  55. package/src/css/theme/semantic.css +0 -244
  56. package/src/fragments/AUDIT.md +0 -55
  57. package/src/fragments/density.ts +0 -43
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Surfaces — the elevation ladder and the aspects derived from it.
3
+ *
4
+ * Two kinds of token live here, and the distinction is the whole design:
5
+ * - **Levels** are fixed stops on the neutral ramp. A named surface aliases exactly one level.
6
+ * - **Aspects** are NOT levels. They are computed from whichever surface hosts them
7
+ * (`--surface-bg`), so the same role reads correctly at any elevation.
8
+ *
9
+ * Everything else (text emphasis, accent, separators, scrollbar, grid paper) is in `roles.css`.
10
+ *
11
+ * Naming convention: --color-{name}-{part}[-{state}] — see DESIGN_SYSTEM.md.
12
+ */
13
+
14
+ @theme {
15
+ /*
16
+ * Elevation ladder — six named levels, strictly monotonic, z-order low → high.
17
+ * Dark: lighter = higher. Light: inverted (lighter = higher, toward white).
18
+ *
19
+ * Chrome sits BELOW the document canvas and content surfaces sit ABOVE it, so the app reads as
20
+ * paper on a desk rather than paper in a well (see AUDIT.md D1/D2).
21
+ *
22
+ * Level Dark Light Roles
23
+ * sunken n-950 0.905 deck gaps, wells, scrim base, L0/R0 outer rails
24
+ * chrome n-925 0.935 sidebars, topbar, statusbar, L1/R1 sidebars
25
+ * base n-900 0.980 document canvas, attention zone
26
+ * raised n-850 0.990 cards, side panels
27
+ * overlay n-800 0.995 dialogs, sheets, drawers
28
+ * popup n-775 1.000 popover, menu, toast, tooltip
29
+ *
30
+ * Toolbars, groups and inputs are deliberately NOT levels: they are aspects derived from
31
+ * whichever surface hosts them (see "Aspects" below), so a toolbar in a card and a toolbar on the
32
+ * canvas each read as a bar on their own host instead of jumping to a fixed global tone.
33
+ *
34
+ * These are private (--dx-*) knobs — use named surface tokens in components, not these directly.
35
+ */
36
+ --dx-surface-sunken: light-dark(var(--color-neutral-150), var(--color-neutral-950));
37
+ --dx-surface-chrome: light-dark(var(--color-neutral-100), var(--color-neutral-925));
38
+ --dx-surface-base: light-dark(oklch(0.98 0.001 190), var(--color-neutral-900));
39
+ --dx-surface-raised: light-dark(oklch(0.99 0.001 190), var(--color-neutral-850));
40
+ --dx-surface-overlay: light-dark(oklch(0.995 0.001 190), var(--color-neutral-800));
41
+ --dx-surface-popup: light-dark(oklch(1 0 0), var(--color-neutral-775));
42
+
43
+ /**
44
+ * Level: sunken
45
+ */
46
+
47
+ --color-deck-surface: var(--dx-surface-sunken);
48
+
49
+ /*
50
+ * Outer rails frame the panels rather than belonging to them, so they read one step behind the
51
+ * L1/R1 sidebars they flank. They carry their own light-dark pair rather than aliasing a level:
52
+ * dark keeps the deck's depth, while light sits higher, because near white the same ladder step
53
+ * reads much heavier than it does near black.
54
+ */
55
+ --color-l0-surface: light-dark(oklch(0.935 0.001 190), var(--dx-surface-sunken));
56
+ --color-r0-surface: light-dark(oklch(0.935 0.001 190), var(--dx-surface-sunken));
57
+
58
+ /* Translucent dimming layer; tracks the lowest level rather than a raw neutral. */
59
+ --color-scrim-surface: light-dark(
60
+ oklch(from var(--dx-surface-sunken) l c h / 0.5),
61
+ oklch(from var(--dx-surface-sunken) l c h / 0.25)
62
+ );
63
+
64
+ /**
65
+ * Level: chrome
66
+ */
67
+
68
+ --color-sidebar-surface: var(--dx-surface-chrome);
69
+ --color-header-surface: var(--dx-surface-chrome);
70
+ --color-l1-surface: light-dark(oklch(0.955 0.001 190), var(--dx-surface-chrome));
71
+ --color-r1-surface: light-dark(oklch(0.955 0.001 190), var(--dx-surface-chrome));
72
+
73
+ /* Sidebar body text. Default sits one ramp stop below the current/selected emphasis. */
74
+ --color-sidebar-fg: light-dark(var(--color-neutral-700), var(--color-neutral-250));
75
+ --color-sidebar-current-fg: light-dark(var(--color-neutral-950), var(--color-neutral-75));
76
+
77
+ /**
78
+ * Level: base
79
+ */
80
+
81
+ --color-base-surface: var(--dx-surface-base);
82
+ --color-base-fg: light-dark(var(--color-neutral-950), var(--color-neutral-150));
83
+
84
+ --color-attention-surface: var(--dx-surface-base);
85
+ --color-attention-contains: oklch(from var(--color-accent-bg) l c h / 0.3);
86
+
87
+ /**
88
+ * Level: raised
89
+ */
90
+
91
+ --color-card-surface: var(--dx-surface-raised);
92
+
93
+ /**
94
+ * Level: overlay
95
+ */
96
+
97
+ --color-modal-surface: var(--dx-surface-overlay);
98
+
99
+ /**
100
+ * Level: popup
101
+ */
102
+
103
+ --color-popover-surface: var(--dx-surface-popup);
104
+
105
+ /*
106
+ * Aspects — surfaces and states derived from the HOST surface (`--surface-bg`) rather than pinned
107
+ * to a level, via relative oklch stepped by `--dx-lift` (+1 dark, -1 light; see base.css), so each
108
+ * stays visible at any elevation in either theme. The values here resolve against the root
109
+ * `--surface-bg` (= base-surface, set in base.css), which is the correct default for content
110
+ * outside any surface zone; every zone re-derives them for its own subtree in
111
+ * components/surface.css.
112
+ *
113
+ * Offsets (lightness): bar 0.025, well 0.05, hover-subtle 0.02, hover 0.08, current 0.10,
114
+ * current-hover 0.12. Keep these in sync with the re-derivation blocks in surface.css.
115
+ */
116
+
117
+ /* Bar: a sticky strip on its host (toolbar, statusbar). */
118
+ --color-toolbar-surface: oklch(
119
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-bar) * 0.025) c h
120
+ );
121
+
122
+ /* Well: an inset region on its host (group, input, focus). */
123
+ --color-group-surface: oklch(
124
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-well) * 0.05) c h
125
+ );
126
+ --color-input-surface: var(--color-group-surface);
127
+ --color-focus-surface: var(--color-group-surface);
128
+
129
+ /* Subtle alternate of group-surface for zebra striping (e.g. calendar months). */
130
+ --color-group-alt-surface: oklch(
131
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * 0.08) c h
132
+ );
133
+
134
+ /* Placeholder ink is an aspect too: a fixed neutral reads correctly on one surface only, so it
135
+ * lifts away from the host like the state surfaces do (offset 0.30). */
136
+ --color-placeholder: oklch(
137
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-placeholder) * 0.3) c
138
+ h
139
+ );
140
+
141
+ /*
142
+ * Scrollbar thumb. Fixed neutrals collided with the surface they scrolled over: in light mode the
143
+ * thumb was neutral-200, the same value as the base canvas, so it was invisible. Deriving it from
144
+ * the host keeps the same contrast on any surface, in either theme (offsets 0.23 / 0.30 / 0.34).
145
+ */
146
+ --color-scrollbar-thumb: oklch(
147
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-scrollbar) * 0.23) c
148
+ h
149
+ );
150
+ --color-scrollbar-thumb-hover: oklch(
151
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-scrollbar) * 0.3) c h
152
+ );
153
+ --color-scrollbar-thumb-active: oklch(
154
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-scrollbar) * 0.34) c
155
+ h
156
+ );
157
+
158
+ /*
159
+ * Separators. Fixed neutrals collided with the surfaces they divide once the ladder inverted:
160
+ * in light mode `separator` was neutral-200, the same value as the base canvas, and
161
+ * `subdued-separator` matched the sidebar exactly — both invisible. The offsets below reproduce
162
+ * the dark-mode appearance they had (0.166 / 0.115 / 0.089 / 0.064 above the base surface) and
163
+ * mirror it in light.
164
+ */
165
+ --color-separator: oklch(
166
+ from var(--surface-bg, var(--color-base-surface))
167
+ calc(l + var(--dx-lift) * var(--dx-attenuate-separator-line) * 0.166) c h
168
+ );
169
+ --color-input-separator: oklch(
170
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-separator) * 0.115) c
171
+ h
172
+ );
173
+ --color-subdued-separator: oklch(
174
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-separator) * 0.089) c
175
+ h
176
+ );
177
+ --color-primary-separator: oklch(
178
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-separator) * 0.064) c
179
+ h
180
+ );
181
+
182
+ /* State aspects — same host-derived mechanism; see the offsets above. */
183
+ --color-hover-surface: oklch(
184
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-state) * 0.08) c h
185
+ );
186
+ --color-hover-fg: light-dark(var(--color-neutral-950), var(--color-neutral-150));
187
+
188
+ /*
189
+ * Subtle region hover for large highlight targets (list rows, tiles) where the full
190
+ * control-strength hover reads as too heavy. Consumed by `.dx-hover`;
191
+ * controls (buttons) keep --color-hover-surface.
192
+ */
193
+ --color-hover-surface-subtle: oklch(
194
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-state) * 0.02) c h
195
+ );
196
+
197
+ /* current (aria-current) */
198
+ --color-current-surface: oklch(
199
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-state) * 0.1) c h
200
+ );
201
+ --color-current-surface-hover: oklch(
202
+ from var(--surface-bg, var(--color-base-surface)) calc(l + var(--dx-lift) * var(--dx-attenuate-state) * 0.12) c h
203
+ );
204
+ --color-current-fg: light-dark(var(--color-neutral-950), var(--color-neutral-50));
205
+
206
+ /* selected (aria-selected) tracks current */
207
+ --color-selected-surface: var(--color-current-surface);
208
+ --color-selected-surface-hover: var(--color-current-surface-hover);
209
+ --color-selected-fg: var(--color-current-fg);
210
+
211
+ /* Inverted surface (tooltips, snackbars) — a deliberate flip, not a level. */
212
+ --color-inverse-surface: light-dark(var(--color-neutral-800), var(--color-neutral-200));
213
+ --color-inverse-fg: light-dark(var(--color-neutral-50), var(--color-neutral-950));
214
+ --color-inverse-text: light-dark(var(--color-neutral-50), var(--color-neutral-950));
215
+ }
@@ -3,8 +3,12 @@
3
3
  */
4
4
 
5
5
  @theme {
6
- /* Font families */
7
- --font-body: 'Inter Variable', ui-sans-serif, system-ui, sans-serif;
6
+ /*
7
+ * Font families
8
+ * https://fonts.google.com/?preview.layout=grid&categoryFilters=Technology:%2FTechnology%2FVariable;Sans+Serif:%2FSans%2F*
9
+ * Nunito
10
+ */
11
+ --font-body: 'Nunito Variable', 'Inter Variable', ui-sans-serif, system-ui, sans-serif;
8
12
  --font-mono: 'JetBrains Mono Variable', ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;
9
13
 
10
14
  /* Font size scale (Tailwind v4 defaults — clean rem/px values) */
@@ -37,13 +37,6 @@
37
37
  @apply dx-expander overflow-hidden;
38
38
  }
39
39
 
40
- /**
41
- * Column that fills the available space (extends dx-expander with overflow clipping).
42
- */
43
- @utility dx-column {
44
- @apply flex-1 min-w-0 w-full;
45
- }
46
-
47
40
  /**
48
41
  * Fullscreen
49
42
  */
@@ -52,10 +45,21 @@
52
45
  }
53
46
 
54
47
  /**
55
- * Visual warning to indicate incorrect usage of `slottable`.
48
+ * Visual warning to indicate incorrect usage of `slottable` — an `asChild` whose child is not
49
+ * composable, so Radix `Slot` silently drops the injected className and ref.
50
+ *
51
+ * An outline rather than a border: it is applied to the part's own element (not a wrapper), so it
52
+ * must not consume layout space or fight a border the part already has. Dev builds only.
56
53
  */
57
54
  @utility dx-slot-warning {
58
- @apply border border-rose-500 border-dashed;
55
+ /* No layout box of its own: these parts live in grids and subgrids, where an extra element
56
+ re-flows the layout — the diagnostic must not corrupt the thing it is diagnosing. */
57
+ display: contents;
58
+
59
+ & > * {
60
+ outline: 2px dashed var(--color-rose-500);
61
+ outline-offset: -2px;
62
+ }
59
63
  }
60
64
 
61
65
  /**
package/src/defs.ts CHANGED
@@ -2,36 +2,11 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { type ChromaticPalette } from '@dxos/ui-types';
6
-
7
5
  /**
8
6
  * Translation namespace for OS-level translations.
9
7
  */
10
8
  export const osTranslations = 'org.dxos.i18n.os';
11
9
 
12
- /**
13
- * Available color hues for UI components.
14
- */
15
- export const hues: ChromaticPalette[] = [
16
- 'red',
17
- 'orange',
18
- 'amber',
19
- 'yellow',
20
- 'lime',
21
- 'green',
22
- 'emerald',
23
- 'teal',
24
- 'cyan',
25
- 'sky',
26
- 'blue',
27
- 'indigo',
28
- 'violet',
29
- 'purple',
30
- 'fuchsia',
31
- 'pink',
32
- 'rose',
33
- ] as const;
34
-
35
10
  export const hueShades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;
36
11
 
37
12
  // NOTE: These are the JS-side style-API role keys (see ColorStyles in util/hash-styles.ts), kept
@@ -2,9 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- export const ghostHover = 'hover:bg-hover-surface';
6
- export const ghostFocusWithin = 'focus-within:bg-hover-surface';
7
-
8
5
  export const hoverableControls =
9
6
  '[--controls-opacity:1] hover-hover:[--controls-opacity:0] hover-hover:hover:[--controls-opacity:1]';
10
7
 
@@ -4,7 +4,6 @@
4
4
 
5
5
  // TODO(burdon): Remove export of fragments.
6
6
 
7
- export * from './density';
8
7
  export * from './disabled';
9
8
  export * from './hover';
10
9
  export * from './text';
package/src/main.css CHANGED
@@ -23,14 +23,8 @@
23
23
  */
24
24
  @import 'tailwindcss' source(none);
25
25
 
26
- /* @theme extensions */
27
- @import './css/theme/animation.css';
28
- @import './css/theme/border.css';
29
- @import './css/theme/palette.css';
30
- @import './css/theme/semantic.css';
31
- @import './css/theme/spacing.css';
32
- @import './css/theme/styles.css';
33
- @import './css/theme/text.css';
26
+ /* Theme extensions; also exported as tokens.css for stylesheets built outside this repo. */
27
+ @import './tokens.css';
34
28
 
35
29
  /* @layer dx-base, dx-tokens */
36
30
  @import './css/base/base.css';
@@ -43,7 +37,8 @@
43
37
  @import './css/components/dialog.css';
44
38
  @import './css/components/focus.css';
45
39
  @import './css/components/icon.css';
46
- @import './css/components/panel.css';
40
+ @import './css/components/input.css';
41
+ @import './css/components/callout.css';
47
42
  @import './css/components/link.css';
48
43
  @import './css/components/scrollbar.css';
49
44
  @import './css/components/state.css';
@@ -134,13 +129,16 @@
134
129
  * Class-based dark mode.
135
130
  * The `.dark` class on an ancestor toggles `dark:` utilities.
136
131
  */
137
- @variant dark (&:where(.dark, .dark *));
132
+ @custom-variant dark (&:where(.dark, .dark *));
138
133
 
139
134
  /** Mobile */
140
135
  @custom-variant pointer-coarse (@media (pointer: coarse));
136
+
141
137
  /** Web */
142
138
  @custom-variant pointer-fine (@media (pointer: fine));
139
+
143
140
  /** Supports mouse/trackpad (Web) */
144
141
  @custom-variant hover-hover (@media (hover: hover));
142
+
145
143
  /** Active navigation */
146
144
  @custom-variant is-current (&[aria-current]:not([aria-current='false']));
@@ -68,51 +68,61 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin[] => {
68
68
  // Trailing-edge debounce handle for theme CSS reloads (see `handleHotUpdate`).
69
69
  let themeReloadTimer: ReturnType<typeof setTimeout> | undefined;
70
70
 
71
+ // Under Vitest there is no HMR, and a live watcher leaks per-file `fs_event` handles: Tailwind's
72
+ // `@source` scan registers every scanned source file as a Vite watch dependency, and those handles
73
+ // are never released on close, hanging single-pass `vitest run` teardown. A non-null `server.watch`
74
+ // here also overrides the `watch: null` set by the test configs, so gate it: disable the watcher
75
+ // entirely under Vitest (`VITEST` is exported to the whole process tree), keep the HMR-ignore
76
+ // patterns for interactive `storybook dev` / `vite dev` (where `VITEST` is unset).
77
+ const isVitest = process.env.VITEST === 'true';
78
+
71
79
  const themePlugin: Plugin = {
72
80
  name: 'vite-plugin-dxos-ui-theme',
73
81
  config: (): UserConfig => {
74
82
  return {
75
83
  server: {
76
- watch: {
77
- // Stop build outputs from driving HMR — they are the root of the
78
- // `main.css` HMR storm.
79
- //
80
- // Tailwind's `@source` scanning (see `src/main.css`) registers its
81
- // scanned source files as Vite watch dependencies of the compiled
82
- // theme CSS. Tailwind's own scanner respects `.gitignore` (and the
83
- // `@source not` directives), so it never *scans* `dist/`. BUT the
84
- // scanner hands Vite a coarse `dir-dependency` glob — e.g.
85
- // `{**/*.html,**/*.ts,**/*.tsx}` and Vite re-expands that glob
86
- // itself, ignoring only `node_modules` (not `.gitignore`, not the
87
- // `@source not` negations). The re-expansion therefore sweeps in
88
- // every `packages/*/dist/**/*.d.ts` (`.d.ts` matches `**/*.ts`),
89
- // making each emitted declaration file a watch-dependency of
90
- // `main.css`. A single package rebuild emits dozens of `.d.ts` in a
91
- // tight burst, and each write re-invalidates the theme 40+ HMR
92
- // pings for `main.css` in one second, repeating on every rebuild.
93
- //
94
- // Ignoring build outputs in the watcher is also semantically
95
- // correct: in dev the workspace resolves `@dxos/*` via the `source`
96
- // export condition (see `vite-plugin-import-source`), so `dist/`
97
- // is never consumed at runtime and its churn should never trigger
98
- // HMR. Vite concatenates these patterns with its built-in ignores
99
- // (`**/node_modules/**`, `**/.git/**`, …), so this is purely
100
- // additive.
101
- //
102
- // `<root>/.claude/**` covers agent worktrees checked out under the
103
- // repo root (`.claude/worktrees/<name>/packages/**`): they are full
104
- // source copies, so the glob re-expansion above sweeps them in and
105
- // every agent-side edit burst or checkout invalidates the theme in
106
- // the user's dev server. The pattern is anchored at the resolved
107
- // repo root (not `**/.claude/**`) because chokidar matches against
108
- // absolute paths a bare pattern would match *everything* when the
109
- // dev server itself runs from inside a worktree whose path contains
110
- // a `.claude` segment. `*.log` covers runtime log sinks (e.g.
111
- // vite-plugin-log's `app.log` in the app root), which are appended
112
- // continuously at runtime and must never feed back into the
113
- // watcher.
114
- ignored: ['**/dist/**', '**/out/**', '**/*.log', `${resolve(import.meta.dirname, ROOT, '.claude')}/**`],
115
- },
84
+ watch: isVitest
85
+ ? null
86
+ : {
87
+ // Stop build outputs from driving HMR — they are the root of the
88
+ // `main.css` HMR storm.
89
+ //
90
+ // Tailwind's `@source` scanning (see `src/main.css`) registers its
91
+ // scanned source files as Vite watch dependencies of the compiled
92
+ // theme CSS. Tailwind's own scanner respects `.gitignore` (and the
93
+ // `@source not` directives), so it never *scans* `dist/`. BUT the
94
+ // scanner hands Vite a coarse `dir-dependency` glob e.g.
95
+ // `{**/*.html,**/*.ts,**/*.tsx}` and Vite re-expands that glob
96
+ // itself, ignoring only `node_modules` (not `.gitignore`, not the
97
+ // `@source not` negations). The re-expansion therefore sweeps in
98
+ // every `packages/*/dist/**/*.d.ts` (`.d.ts` matches `**/*.ts`),
99
+ // making each emitted declaration file a watch-dependency of
100
+ // `main.css`. A single package rebuild emits dozens of `.d.ts` in a
101
+ // tight burst, and each write re-invalidates the theme — 40+ HMR
102
+ // pings for `main.css` in one second, repeating on every rebuild.
103
+ //
104
+ // Ignoring build outputs in the watcher is also semantically
105
+ // correct: in dev the workspace resolves `@dxos/*` via the `source`
106
+ // export condition (see `vite-plugin-import-source`), so `dist/`
107
+ // is never consumed at runtime and its churn should never trigger
108
+ // HMR. Vite concatenates these patterns with its built-in ignores
109
+ // (`**/node_modules/**`, `**/.git/**`, …), so this is purely
110
+ // additive.
111
+ //
112
+ // `<root>/.claude/**` covers agent worktrees checked out under the
113
+ // repo root (`.claude/worktrees/<name>/packages/**`): they are full
114
+ // source copies, so the glob re-expansion above sweeps them in and
115
+ // every agent-side edit burst or checkout invalidates the theme in
116
+ // the user's dev server. The pattern is anchored at the resolved
117
+ // repo root (not `**/.claude/**`) because chokidar matches against
118
+ // absolute paths — a bare pattern would match *everything* when the
119
+ // dev server itself runs from inside a worktree whose path contains
120
+ // a `.claude` segment. `*.log` covers runtime log sinks (e.g.
121
+ // vite-plugin-log's `app.log` in the app root), which are appended
122
+ // continuously at runtime and must never feed back into the
123
+ // watcher.
124
+ ignored: ['**/dist/**', '**/out/**', '**/*.log', `${resolve(import.meta.dirname, ROOT, '.claude')}/**`],
125
+ },
116
126
  },
117
127
  css: {
118
128
  postcss: {
@@ -24,7 +24,7 @@ body {
24
24
  }
25
25
 
26
26
  /* Pre-CSS color fallbacks so text is readable before and during Vite HMR. */
27
- /* Values are intentionally kept in sync with @theme tokens in semantic.css:
27
+ /* Values are intentionally kept in sync with @theme tokens in theme/surfaces.css:
28
28
  * color ← --color-base-fg = light-dark(neutral-950, neutral-150)
29
29
  * background-color ← --color-base-surface = elevation-3 = light-dark(neutral-125, neutral-850)
30
30
  * The literals carry the ramp tint (chroma 0.001, hue 190) so first paint matches the loaded theme. */
package/src/tokens.css ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Design tokens, for stylesheets compiled outside this repo.
3
+ *
4
+ * A plugin loaded from the registry is not scanned by the host's Tailwind build, so it compiles its
5
+ * own stylesheet and must be able to generate every utility it uses — including token-backed ones
6
+ * like `bg-base-surface`. Importing this registers the token names with Tailwind so those utilities
7
+ * resolve; the values themselves still come from the host at runtime, which is what keeps a plugin
8
+ * following the active theme.
9
+ *
10
+ * Tokens only — no base, component, or layout rules, which the host already applies.
11
+ *
12
+ * `main.css` imports this too, so the host and external plugins compile against one token set.
13
+ */
14
+
15
+ @import './css/theme/animation.css';
16
+ @import './css/theme/border.css';
17
+ @import './css/theme/palette.css';
18
+ @import './css/theme/roles.css';
19
+ @import './css/theme/spacing.css';
20
+ @import './css/theme/styles.css';
21
+ @import './css/theme/surfaces.css';
22
+ @import './css/theme/text.css';
@@ -1,4 +0,0 @@
1
- import { type Density } from '@dxos/ui-types';
2
- export declare const densityDimensions: (density?: Density) => "min-h-[1.5rem] px-1" | "min-h-[1.75rem] px-1.5" | "min-h-[2.5rem] pointer-fine:min-h-[2rem] px-2" | "min-h-[2.5rem] px-3";
3
- export declare const densityBlockSize: (density?: Density) => "min-h-[1.5rem]" | "min-h-[1.75rem]" | "min-h-[2.5rem]" | "min-h-[2.5rem] pointer-fine:min-h-[2rem]";
4
- //# sourceMappingURL=density.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"density.d.ts","sourceRoot":"","sources":["../../../../src/fragments/density.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAY9C,eAAO,MAAM,iBAAiB,aAAa,OAAO,+HAYjD,CAAC;AAEF,eAAO,MAAM,gBAAgB,aAAa,OAAO,yGAYhD,CAAC"}
@@ -1,117 +0,0 @@
1
- /**
2
- * Panel — applies bg-{hue}-surface / text-{hue}-fg / border-{hue}-border tokens via data-hue attribute or BEM modifier.
3
- * Example: <div className='dx-panel' data-hue='blue'>...</div>
4
- * Example: <div className='dx-panel dx-panel--blue'>...</div>
5
- */
6
-
7
- @layer dx-components {
8
- [data-hue='neutral'] .dx-panel,
9
- .dx-panel--neutral {
10
- @apply bg-neutral-surface text-neutral-fg border-neutral-border;
11
- }
12
-
13
- [data-hue='red'] .dx-panel,
14
- .dx-panel--red {
15
- @apply bg-red-surface text-red-fg border-red-border;
16
- }
17
-
18
- [data-hue='orange'] .dx-panel,
19
- .dx-panel--orange {
20
- @apply bg-orange-surface text-orange-fg border-orange-border;
21
- }
22
-
23
- [data-hue='amber'] .dx-panel,
24
- .dx-panel--amber {
25
- @apply bg-amber-surface text-amber-fg border-amber-border;
26
- }
27
-
28
- [data-hue='yellow'] .dx-panel,
29
- .dx-panel--yellow {
30
- @apply bg-yellow-surface text-yellow-fg border-yellow-border;
31
- }
32
-
33
- [data-hue='lime'] .dx-panel,
34
- .dx-panel--lime {
35
- @apply bg-lime-surface text-lime-fg border-lime-border;
36
- }
37
-
38
- [data-hue='green'] .dx-panel,
39
- .dx-panel--green {
40
- @apply bg-green-surface text-green-fg border-green-border;
41
- }
42
-
43
- [data-hue='emerald'] .dx-panel,
44
- .dx-panel--emerald {
45
- @apply bg-emerald-surface text-emerald-fg border-emerald-border;
46
- }
47
-
48
- [data-hue='teal'] .dx-panel,
49
- .dx-panel--teal {
50
- @apply bg-teal-surface text-teal-fg border-teal-border;
51
- }
52
-
53
- [data-hue='cyan'] .dx-panel,
54
- .dx-panel--cyan {
55
- @apply bg-cyan-surface text-cyan-fg border-cyan-border;
56
- }
57
-
58
- [data-hue='sky'] .dx-panel,
59
- .dx-panel--sky {
60
- @apply bg-sky-surface text-sky-fg border-sky-border;
61
- }
62
-
63
- [data-hue='blue'] .dx-panel,
64
- .dx-panel--blue {
65
- @apply bg-blue-surface text-blue-fg border-blue-border;
66
- }
67
-
68
- [data-hue='indigo'] .dx-panel,
69
- .dx-panel--indigo {
70
- @apply bg-indigo-surface text-indigo-fg border-indigo-border;
71
- }
72
-
73
- [data-hue='violet'] .dx-panel,
74
- .dx-panel--violet {
75
- @apply bg-violet-surface text-violet-fg border-violet-border;
76
- }
77
-
78
- [data-hue='purple'] .dx-panel,
79
- .dx-panel--purple {
80
- @apply bg-purple-surface text-purple-fg border-purple-border;
81
- }
82
-
83
- [data-hue='fuchsia'] .dx-panel,
84
- .dx-panel--fuchsia {
85
- @apply bg-fuchsia-surface text-fuchsia-fg border-fuchsia-border;
86
- }
87
-
88
- [data-hue='pink'] .dx-panel,
89
- .dx-panel--pink {
90
- @apply bg-pink-surface text-pink-fg border-pink-border;
91
- }
92
-
93
- [data-hue='rose'] .dx-panel,
94
- .dx-panel--rose {
95
- @apply bg-rose-surface text-rose-fg border-rose-border;
96
- }
97
-
98
- [data-hue='info'] .dx-panel,
99
- .dx-panel--info {
100
- @apply bg-info-surface text-info-fg border-info-border;
101
- }
102
-
103
- [data-hue='success'] .dx-panel,
104
- .dx-panel--success {
105
- @apply bg-success-surface text-success-fg border-success-border;
106
- }
107
-
108
- [data-hue='warning'] .dx-panel,
109
- .dx-panel--warning {
110
- @apply bg-warning-surface text-warning-fg border-warning-border;
111
- }
112
-
113
- [data-hue='error'] .dx-panel,
114
- .dx-panel--error {
115
- @apply bg-error-surface text-error-fg border-error-border;
116
- }
117
- }