@dxos/ui-theme 0.8.4-staging.ac66bdf99f → 0.9.1-main.c7dcc2e112
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/browser/index.mjs +89 -867
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +89 -867
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/plugin/node-cjs/main.css +279 -120
- package/dist/plugin/node-cjs/main.css.map +3 -3
- package/dist/plugin/node-cjs/meta.json +1 -1
- package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs +82 -10
- package/dist/plugin/node-cjs/plugins/ThemePlugin.cjs.map +3 -3
- package/dist/plugin/node-esm/main.css +279 -120
- package/dist/plugin/node-esm/main.css.map +3 -3
- package/dist/plugin/node-esm/meta.json +1 -1
- package/dist/plugin/node-esm/plugins/ThemePlugin.mjs +84 -12
- package/dist/plugin/node-esm/plugins/ThemePlugin.mjs.map +3 -3
- package/dist/types/src/Theme.stories.d.ts.map +1 -1
- package/dist/types/src/defs.d.ts +1 -1
- package/dist/types/src/defs.d.ts.map +1 -1
- package/dist/types/src/fragments/density.d.ts +2 -2
- package/dist/types/src/fragments/density.d.ts.map +1 -1
- package/dist/types/src/fragments/hover.d.ts +0 -1
- package/dist/types/src/fragments/hover.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/plugins/ThemePlugin.d.ts +3 -2
- package/dist/types/src/plugins/ThemePlugin.d.ts.map +1 -1
- package/dist/types/src/util/hash-styles.d.ts +12 -2
- package/dist/types/src/util/hash-styles.d.ts.map +1 -1
- package/dist/types/src/util/mx.d.ts +0 -36
- package/dist/types/src/util/mx.d.ts.map +1 -1
- package/dist/types/src/util/size.d.ts.map +1 -1
- package/dist/types/src/util/valence.d.ts +8 -2
- package/dist/types/src/util/valence.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -21
- package/src/Theme.stories.tsx +94 -27
- package/src/css/DESIGN_SYSTEM.md +187 -0
- package/src/css/base/base.css +2 -2
- package/src/css/components/button.css +57 -12
- package/src/css/components/card.css +14 -0
- package/src/css/components/checkbox.css +3 -3
- package/src/css/components/focus.css +12 -12
- package/src/css/components/link.css +4 -1
- package/src/css/components/panel.css +45 -45
- package/src/css/components/state.css +76 -0
- package/src/css/components/state.md +101 -0
- package/src/css/components/surface.css +86 -11
- package/src/css/components/tag.css +25 -24
- package/src/css/integrations/codemirror.css +4 -3
- package/src/css/integrations/tldraw.css +1 -1
- package/src/css/layout/main.css +6 -0
- package/src/css/layout/size.css +17 -4
- package/src/css/theme/animation.css +31 -0
- package/src/css/theme/palette.css +34 -0
- package/src/css/theme/semantic.css +190 -68
- package/src/css/theme/spacing.css +30 -13
- package/src/css/theme/styles.css +172 -119
- package/src/css/utilities.css +42 -0
- package/src/defs.ts +3 -1
- package/src/fragments/AUDIT.md +0 -2
- package/src/fragments/density.ts +34 -7
- package/src/fragments/hover.ts +0 -2
- package/src/index.ts +1 -1
- package/src/main.css +68 -9
- package/src/plugins/ThemePlugin.ts +102 -14
- package/src/plugins/main.css +10 -7
- package/src/util/hash-styles.ts +54 -42
- package/src/util/mx.ts +1 -126
- package/src/util/valence.ts +15 -5
- package/dist/types/src/theme/components/avatar.d.ts +0 -21
- package/dist/types/src/theme/components/avatar.d.ts.map +0 -1
- package/dist/types/src/theme/components/breadcrumb.d.ts +0 -9
- package/dist/types/src/theme/components/breadcrumb.d.ts.map +0 -1
- package/dist/types/src/theme/components/button.d.ts +0 -15
- package/dist/types/src/theme/components/button.d.ts.map +0 -1
- package/dist/types/src/theme/components/card.d.ts +0 -12
- package/dist/types/src/theme/components/card.d.ts.map +0 -1
- package/dist/types/src/theme/components/dialog.d.ts +0 -17
- package/dist/types/src/theme/components/dialog.d.ts.map +0 -1
- package/dist/types/src/theme/components/focus.d.ts +0 -6
- package/dist/types/src/theme/components/focus.d.ts.map +0 -1
- package/dist/types/src/theme/components/icon-button.d.ts +0 -8
- package/dist/types/src/theme/components/icon-button.d.ts.map +0 -1
- package/dist/types/src/theme/components/icon.d.ts +0 -10
- package/dist/types/src/theme/components/icon.d.ts.map +0 -1
- package/dist/types/src/theme/components/index.d.ts +0 -27
- package/dist/types/src/theme/components/index.d.ts.map +0 -1
- package/dist/types/src/theme/components/input.d.ts +0 -115
- package/dist/types/src/theme/components/input.d.ts.map +0 -1
- package/dist/types/src/theme/components/link.d.ts +0 -7
- package/dist/types/src/theme/components/link.d.ts.map +0 -1
- package/dist/types/src/theme/components/list.d.ts +0 -14
- package/dist/types/src/theme/components/list.d.ts.map +0 -1
- package/dist/types/src/theme/components/main.d.ts +0 -28
- package/dist/types/src/theme/components/main.d.ts.map +0 -1
- package/dist/types/src/theme/components/menu.d.ts +0 -13
- package/dist/types/src/theme/components/menu.d.ts.map +0 -1
- package/dist/types/src/theme/components/message.d.ts +0 -12
- package/dist/types/src/theme/components/message.d.ts.map +0 -1
- package/dist/types/src/theme/components/popover.d.ts +0 -11
- package/dist/types/src/theme/components/popover.d.ts.map +0 -1
- package/dist/types/src/theme/components/scroll-area.d.ts +0 -32
- package/dist/types/src/theme/components/scroll-area.d.ts.map +0 -1
- package/dist/types/src/theme/components/select.d.ts +0 -13
- package/dist/types/src/theme/components/select.d.ts.map +0 -1
- package/dist/types/src/theme/components/separator.d.ts +0 -8
- package/dist/types/src/theme/components/separator.d.ts.map +0 -1
- package/dist/types/src/theme/components/skeleton.d.ts +0 -7
- package/dist/types/src/theme/components/skeleton.d.ts.map +0 -1
- package/dist/types/src/theme/components/splitter.d.ts +0 -4
- package/dist/types/src/theme/components/splitter.d.ts.map +0 -1
- package/dist/types/src/theme/components/status.d.ts +0 -9
- package/dist/types/src/theme/components/status.d.ts.map +0 -1
- package/dist/types/src/theme/components/tag.d.ts +0 -7
- package/dist/types/src/theme/components/tag.d.ts.map +0 -1
- package/dist/types/src/theme/components/toast.d.ts +0 -12
- package/dist/types/src/theme/components/toast.d.ts.map +0 -1
- package/dist/types/src/theme/components/toolbar.d.ts +0 -11
- package/dist/types/src/theme/components/toolbar.d.ts.map +0 -1
- package/dist/types/src/theme/components/tooltip.d.ts +0 -8
- package/dist/types/src/theme/components/tooltip.d.ts.map +0 -1
- package/dist/types/src/theme/components/treegrid.d.ts +0 -10
- package/dist/types/src/theme/components/treegrid.d.ts.map +0 -1
- package/dist/types/src/theme/index.d.ts +0 -4
- package/dist/types/src/theme/index.d.ts.map +0 -1
- package/dist/types/src/theme/primitives/column.d.ts +0 -29
- package/dist/types/src/theme/primitives/column.d.ts.map +0 -1
- package/dist/types/src/theme/primitives/index.d.ts +0 -3
- package/dist/types/src/theme/primitives/index.d.ts.map +0 -1
- package/dist/types/src/theme/primitives/panel.d.ts +0 -13
- package/dist/types/src/theme/primitives/panel.d.ts.map +0 -1
- package/dist/types/src/theme/theme.d.ts +0 -5
- package/dist/types/src/theme/theme.d.ts.map +0 -1
- package/src/css/components/selected.css +0 -30
- package/src/theme/components/avatar.ts +0 -95
- package/src/theme/components/breadcrumb.ts +0 -29
- package/src/theme/components/button.ts +0 -48
- package/src/theme/components/card.ts +0 -102
- package/src/theme/components/dialog.ts +0 -61
- package/src/theme/components/focus.ts +0 -33
- package/src/theme/components/icon-button.ts +0 -18
- package/src/theme/components/icon.ts +0 -28
- package/src/theme/components/index.ts +0 -30
- package/src/theme/components/input.ts +0 -171
- package/src/theme/components/link.ts +0 -25
- package/src/theme/components/list.ts +0 -46
- package/src/theme/components/main.ts +0 -34
- package/src/theme/components/menu.ts +0 -50
- package/src/theme/components/message.ts +0 -40
- package/src/theme/components/popover.ts +0 -41
- package/src/theme/components/scroll-area.ts +0 -115
- package/src/theme/components/select.ts +0 -52
- package/src/theme/components/separator.ts +0 -24
- package/src/theme/components/skeleton.ts +0 -23
- package/src/theme/components/splitter.ts +0 -20
- package/src/theme/components/status.ts +0 -32
- package/src/theme/components/tag.ts +0 -23
- package/src/theme/components/toast.ts +0 -53
- package/src/theme/components/toolbar.ts +0 -35
- package/src/theme/components/tooltip.ts +0 -27
- package/src/theme/components/treegrid.ts +0 -37
- package/src/theme/index.ts +0 -7
- package/src/theme/primitives/column.ts +0 -71
- package/src/theme/primitives/index.ts +0 -6
- package/src/theme/primitives/panel.ts +0 -43
- package/src/theme/theme.ts +0 -87
package/src/main.css
CHANGED
|
@@ -11,7 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
@layer properties, theme, dx-tokens, user-tokens, base, tw-base, dx-base, components, tw-components, dx-components, utilities;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* `source(none)` disables Tailwind 4's automatic content discovery (which
|
|
16
|
+
* scans the project root, including `publicDir`). With auto-detection on,
|
|
17
|
+
* every write to `public/icons.svg` from `vite-plugin-icons` triggers a
|
|
18
|
+
* Tailwind CSS regeneration, which invalidates `main.css` through its
|
|
19
|
+
* import graph and produces a 3–5-ping HMR storm per icon-sprite write
|
|
20
|
+
* during cold-start (icons land in tight bursts as plugin chunks stream
|
|
21
|
+
* in). Explicit `@source` directives below replace the auto-scan with a
|
|
22
|
+
* stable, source-only set.
|
|
23
|
+
*/
|
|
24
|
+
@import 'tailwindcss' source(none);
|
|
15
25
|
|
|
16
26
|
/* @theme extensions */
|
|
17
27
|
@import './css/theme/animation.css';
|
|
@@ -28,6 +38,7 @@
|
|
|
28
38
|
|
|
29
39
|
/* @layer dx-components (components) */
|
|
30
40
|
@import './css/components/button.css';
|
|
41
|
+
@import './css/components/card.css';
|
|
31
42
|
@import './css/components/checkbox.css';
|
|
32
43
|
@import './css/components/dialog.css';
|
|
33
44
|
@import './css/components/focus.css';
|
|
@@ -35,7 +46,7 @@
|
|
|
35
46
|
@import './css/components/panel.css';
|
|
36
47
|
@import './css/components/link.css';
|
|
37
48
|
@import './css/components/scrollbar.css';
|
|
38
|
-
@import './css/components/
|
|
49
|
+
@import './css/components/state.css';
|
|
39
50
|
@import './css/components/surface.css';
|
|
40
51
|
@import './css/components/tag.css';
|
|
41
52
|
@import './css/components/text.css';
|
|
@@ -54,15 +65,63 @@
|
|
|
54
65
|
@import './css/integrations/tldraw.css';
|
|
55
66
|
|
|
56
67
|
/**
|
|
57
|
-
*
|
|
68
|
+
* With `source(none)` on the Tailwind import above, these are the *only*
|
|
69
|
+
* paths Tailwind scans for utility classes. Cover every workspace source
|
|
70
|
+
* directory so lazy-loaded plugin / UI / SDK chunks don't surface new
|
|
71
|
+
* utilities mid-session, and root-level HTML entrypoints (index.html,
|
|
72
|
+
* internal.html, …) are visible too.
|
|
73
|
+
*
|
|
74
|
+
* Paths are relative to this file (`packages/ui/ui-theme/src/main.css`):
|
|
75
|
+
* ../../../ → `packages/`
|
|
76
|
+
* ../../../../tools/ → `tools/`
|
|
77
|
+
*
|
|
78
|
+
* Tailwind ignores `node_modules` and common build/output dirs by default,
|
|
79
|
+
* so a broad workspace glob is safe *for scanning*.
|
|
80
|
+
*
|
|
81
|
+
* NOTE: Tailwind's scanner respects `.gitignore` (so `dist/` is never
|
|
82
|
+
* scanned), but it hands Vite a coarse `dir-dependency` glob and Vite
|
|
83
|
+
* re-expands that itself, ignoring only `node_modules`. That re-expansion
|
|
84
|
+
* sweeps `dist/**` `.d.ts` files into the watch set and causes a `main.css`
|
|
85
|
+
* HMR storm on every package rebuild. Build outputs are excluded from the
|
|
86
|
+
* dev watcher in `plugins/ThemePlugin.ts` (`server.watch.ignored`) rather
|
|
87
|
+
* than here, because `@source not` does NOT propagate to the glob Vite
|
|
88
|
+
* re-expands.
|
|
89
|
+
*/
|
|
90
|
+
@source '../../../**/*.{ts,tsx,html}';
|
|
91
|
+
@source '../../../../tools/**/*.{ts,tsx,html}';
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Exclude generated/static data files that have no Tailwind classes but are
|
|
95
|
+
* large enough to dominate scan time. The oxide scanner tokenizes every byte;
|
|
96
|
+
* a single 5MB geo-data file can pin Vite at 100% CPU for tens of seconds on
|
|
97
|
+
* a cold start.
|
|
98
|
+
*/
|
|
99
|
+
@source not '../../../ui/react-ui-geo/data/**';
|
|
100
|
+
@source not '../../../ui/solid-ui-geo/data/**';
|
|
101
|
+
@source not '../../../apps/composer-app/src/plugins/welcome/components/Welcome/hero-image.ts';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Force-emit CSS variables for the full color palette for all:
|
|
58
105
|
* --color-{hue}-{shade}
|
|
59
106
|
* --color-{hue}-{style}
|
|
60
107
|
*/
|
|
61
|
-
@source inline(
|
|
62
|
-
@source inline(
|
|
63
|
-
@source inline(
|
|
64
|
-
@source inline(
|
|
65
|
-
@source inline(
|
|
108
|
+
@source inline('bg-{neutral,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}-{50,100,200,300,400,500,600,700,800,900,950}');
|
|
109
|
+
@source inline('bg-{neutral,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}-{bg,bg-hover,fg,surface,text,border}');
|
|
110
|
+
@source inline('bg-{primary,secondary}-{50,100,200,300,400,500,600,700,800,900,950}');
|
|
111
|
+
@source inline('border-{neutral,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}-border');
|
|
112
|
+
@source inline('bg-{info,success,warning,error}-{bg,surface,fg,text,border}');
|
|
113
|
+
@source inline('border-{info,success,warning,error}-border');
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Force-emit tokens consumed by web components / .pcss files outside the
|
|
117
|
+
* Tailwind source-scan graph (e.g. lit-grid's dx-grid.pcss uses var(--color-grid-line)).
|
|
118
|
+
*/
|
|
119
|
+
@source inline('bg-grid-{surface,fg,line,highlight,comment,comment-active,selection-surface,selection-fg}');
|
|
120
|
+
/*
|
|
121
|
+
* Sidebar surfaces alias each other (e.g. header-surface -> l0-surface); force-emit so the vars
|
|
122
|
+
* exist in apps that use the alias but not the underlying utility (e.g. devtools).
|
|
123
|
+
*/
|
|
124
|
+
@source inline('bg-{l0,r0,l1,r1}-surface');
|
|
66
125
|
|
|
67
126
|
/**
|
|
68
127
|
* Plugins must come after all imports.
|
|
@@ -84,4 +143,4 @@
|
|
|
84
143
|
/** Supports mouse/trackpad (Web) */
|
|
85
144
|
@custom-variant hover-hover (@media (hover: hover));
|
|
86
145
|
/** Active navigation */
|
|
87
|
-
@custom-variant is-current (&[aria-current]:not([aria-current=
|
|
146
|
+
@custom-variant is-current (&[aria-current]:not([aria-current='false']));
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable no-console */
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import tailwindcssPostcss from '@tailwindcss/postcss';
|
|
8
|
+
import tailwindcssVite from '@tailwindcss/vite';
|
|
8
9
|
import autoprefixer from 'autoprefixer';
|
|
9
10
|
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
-
import {
|
|
11
|
+
import { resolve } from 'node:path';
|
|
11
12
|
import postcssImport from 'postcss-import';
|
|
12
13
|
import postcssNesting from 'postcss-nesting';
|
|
13
14
|
import { type HtmlTagDescriptor, type Plugin, type UserConfig } from 'vite';
|
|
@@ -40,8 +41,9 @@ export type ThemePluginOptions = {
|
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Vite plugin to configure theme.
|
|
44
|
+
* Returns the official Tailwind Vite plugin (persistent incremental scanner) alongside the theme plugin.
|
|
43
45
|
*/
|
|
44
|
-
export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
|
|
46
|
+
export const ThemePlugin = (options: ThemePluginOptions): Plugin[] => {
|
|
45
47
|
// Prefer source CSS if available (monorepo dev), fall back to dist for installed package.
|
|
46
48
|
const srcThemePath = resolve(import.meta.dirname, ROOT, 'src/main.css');
|
|
47
49
|
const distThemePath = resolve(import.meta.dirname, '../main.css');
|
|
@@ -53,23 +55,65 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
|
|
|
53
55
|
const darkModeScriptPath = resolve(pluginsDir, 'dark-mode.ts');
|
|
54
56
|
const mainCssPath = resolve(pluginsDir, 'main.css');
|
|
55
57
|
|
|
56
|
-
const config
|
|
57
|
-
srcCssPath: isMonorepo ? srcThemePath : distThemePath,
|
|
58
|
-
virtualFileId: '@dxos-theme',
|
|
59
|
-
|
|
58
|
+
const config = {
|
|
59
|
+
srcCssPath: options.srcCssPath ?? (isMonorepo ? srcThemePath : distThemePath),
|
|
60
|
+
virtualFileId: options.virtualFileId ?? '@dxos-theme',
|
|
61
|
+
verbose: options.verbose,
|
|
60
62
|
};
|
|
61
63
|
|
|
62
|
-
// Derive project root from the source location so Tailwind scans all packages.
|
|
63
|
-
const base = isMonorepo ? resolve(dirname(srcThemePath), ROOT) : undefined;
|
|
64
|
-
|
|
65
64
|
if (process.env.DEBUG || options.verbose) {
|
|
66
65
|
console.log('ThemePlugin:\n', JSON.stringify(config, null, 2));
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
// Trailing-edge debounce handle for theme CSS reloads (see `handleHotUpdate`).
|
|
69
|
+
let themeReloadTimer: ReturnType<typeof setTimeout> | undefined;
|
|
70
|
+
|
|
71
|
+
const themePlugin: Plugin = {
|
|
70
72
|
name: 'vite-plugin-dxos-ui-theme',
|
|
71
73
|
config: (): UserConfig => {
|
|
72
74
|
return {
|
|
75
|
+
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
|
+
},
|
|
116
|
+
},
|
|
73
117
|
css: {
|
|
74
118
|
postcss: {
|
|
75
119
|
plugins: [
|
|
@@ -77,9 +121,11 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
|
|
|
77
121
|
postcssImport(),
|
|
78
122
|
// Processes CSS nesting syntax.
|
|
79
123
|
postcssNesting(),
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
124
|
+
// Resolves @reference/@apply in `.pcss` files (e.g. lit-grid, lit-ui), which the
|
|
125
|
+
// @tailwindcss/vite plugin skips — its transform filter only matches `.css`.
|
|
126
|
+
// Theme `.css` files are compiled by @tailwindcss/vite first (enforce: 'pre'),
|
|
127
|
+
// so this plugin's quick-bail check passes them through untouched.
|
|
128
|
+
tailwindcssPostcss(),
|
|
83
129
|
// Adds vendor prefixes.
|
|
84
130
|
autoprefixer,
|
|
85
131
|
],
|
|
@@ -92,6 +138,42 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
|
|
|
92
138
|
return config.srcCssPath;
|
|
93
139
|
}
|
|
94
140
|
},
|
|
141
|
+
hotUpdate({ type, file, modules }) {
|
|
142
|
+
// Direct edits to CSS (the theme source or its imports) keep Vite's
|
|
143
|
+
// default immediate update for instant feedback while authoring styles.
|
|
144
|
+
if (this.environment.name !== 'client' || type !== 'update' || file.endsWith('.css')) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Every content file Tailwind scans is registered as a dependency of the
|
|
149
|
+
// theme CSS — Vite models it as a file-only entry node whose importer is
|
|
150
|
+
// `main.css` — so each source-file save invalidates `main.css` and
|
|
151
|
+
// re-runs the monorepo-wide Tailwind scan. During an edit wave that
|
|
152
|
+
// serializes one full scan per save. Drop the theme-dep entries from the
|
|
153
|
+
// update (the changed module itself still hot-updates immediately) and
|
|
154
|
+
// reload the theme CSS once on the trailing edge of a quiet window, so a
|
|
155
|
+
// wave costs at most one scan.
|
|
156
|
+
const isThemeDep = (mod: (typeof modules)[number]): boolean =>
|
|
157
|
+
mod.file === config.srcCssPath ||
|
|
158
|
+
(mod.id === null &&
|
|
159
|
+
mod.importers.size > 0 &&
|
|
160
|
+
[...mod.importers].every((importer) => importer.file === config.srcCssPath));
|
|
161
|
+
if (!modules.some(isThemeDep)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const environment = this.environment;
|
|
166
|
+
clearTimeout(themeReloadTimer);
|
|
167
|
+
themeReloadTimer = setTimeout(() => {
|
|
168
|
+
for (const mod of environment.moduleGraph.getModulesByFile(config.srcCssPath) ?? []) {
|
|
169
|
+
environment.reloadModule(mod).catch(() => {
|
|
170
|
+
// Server may be mid-restart; the next edit reschedules the reload.
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}, 300);
|
|
174
|
+
|
|
175
|
+
return modules.filter((mod) => !isThemeDep(mod));
|
|
176
|
+
},
|
|
95
177
|
transformIndexHtml: () => {
|
|
96
178
|
// Apply .dark class to <html> synchronously before any scripts run, so that
|
|
97
179
|
// the critical CSS html.dark rules apply on the very first paint.
|
|
@@ -122,4 +204,10 @@ export const ThemePlugin = (options: ThemePluginOptions): Plugin => {
|
|
|
122
204
|
return [darkModeTag, layersTag, criticalTag];
|
|
123
205
|
},
|
|
124
206
|
};
|
|
207
|
+
|
|
208
|
+
// The Tailwind Vite plugins are `enforce: 'pre'`, so they compile theme CSS (resolving
|
|
209
|
+
// `@import 'tailwindcss'`, @source, @plugin, @theme) before the postcss chain runs —
|
|
210
|
+
// postcss-import never sees the raw Tailwind directives. Scan roots come from the @source
|
|
211
|
+
// directives in main.css (relative to that file); no project-root base is needed.
|
|
212
|
+
return [...tailwindcssVite(), themePlugin];
|
|
125
213
|
};
|
package/src/plugins/main.css
CHANGED
|
@@ -24,22 +24,25 @@ 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 semantic.css:
|
|
28
|
+
* color ← --color-base-fg = light-dark(neutral-950, neutral-150)
|
|
29
|
+
* background-color ← --color-base-surface = elevation-3 = light-dark(neutral-125, neutral-850)
|
|
30
|
+
* The literals carry the ramp tint (chroma 0.001, hue 190) so first paint matches the loaded theme. */
|
|
28
31
|
:root {
|
|
29
32
|
color-scheme: light;
|
|
30
33
|
}
|
|
31
34
|
html {
|
|
32
|
-
color: oklch(0.145 0
|
|
33
|
-
background-color: oklch(0.
|
|
35
|
+
color: oklch(0.145 0.001 190);
|
|
36
|
+
background-color: oklch(0.92 0.001 190);
|
|
34
37
|
}
|
|
35
38
|
html.dark {
|
|
36
39
|
color-scheme: dark;
|
|
37
|
-
color: oklch(0.
|
|
38
|
-
background-color: oklch(0.
|
|
40
|
+
color: oklch(0.905 0.001 190);
|
|
41
|
+
background-color: oklch(0.237 0.001 190);
|
|
39
42
|
}
|
|
40
43
|
@media (prefers-color-scheme: dark) {
|
|
41
44
|
html:not(.dark) {
|
|
42
|
-
color: oklch(0.
|
|
43
|
-
background-color: oklch(0.
|
|
45
|
+
color: oklch(0.905 0.001 190);
|
|
46
|
+
background-color: oklch(0.237 0.001 190);
|
|
44
47
|
}
|
|
45
48
|
}
|
package/src/util/hash-styles.ts
CHANGED
|
@@ -6,21 +6,23 @@ import { type ChromaticPalette } from '@dxos/ui-types';
|
|
|
6
6
|
|
|
7
7
|
export type Hue = ChromaticPalette | 'neutral';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* See theme.css
|
|
11
|
+
*/
|
|
10
12
|
export type ColorStyles = {
|
|
11
13
|
hue: Hue;
|
|
12
|
-
|
|
13
|
-
surface: string;
|
|
14
|
-
|
|
15
|
-
text: string;
|
|
16
|
-
border: string;
|
|
14
|
+
bg: string;
|
|
15
|
+
surface: string;
|
|
16
|
+
fg: string;
|
|
17
|
+
text: string;
|
|
18
|
+
border: string;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
const neutral: ColorStyles = {
|
|
20
22
|
hue: 'neutral',
|
|
21
|
-
|
|
23
|
+
bg: 'bg-neutral-bg',
|
|
22
24
|
surface: 'bg-neutral-surface',
|
|
23
|
-
|
|
25
|
+
fg: 'text-neutral-fg',
|
|
24
26
|
text: 'text-neutral-text',
|
|
25
27
|
border: 'border-neutral-border',
|
|
26
28
|
};
|
|
@@ -30,137 +32,137 @@ const neutral: ColorStyles = {
|
|
|
30
32
|
const styles: ColorStyles[] = [
|
|
31
33
|
{
|
|
32
34
|
hue: 'red',
|
|
33
|
-
|
|
35
|
+
bg: 'bg-red-bg',
|
|
34
36
|
surface: 'bg-red-surface',
|
|
35
|
-
|
|
37
|
+
fg: 'text-red-fg',
|
|
36
38
|
text: 'text-red-text',
|
|
37
39
|
border: 'border-red-border',
|
|
38
40
|
},
|
|
39
41
|
{
|
|
40
42
|
hue: 'orange',
|
|
41
|
-
|
|
43
|
+
bg: 'bg-orange-bg',
|
|
42
44
|
surface: 'bg-orange-surface',
|
|
43
|
-
|
|
45
|
+
fg: 'text-orange-fg',
|
|
44
46
|
text: 'text-orange-text',
|
|
45
47
|
border: 'border-orange-border',
|
|
46
48
|
},
|
|
47
49
|
{
|
|
48
50
|
hue: 'amber',
|
|
49
|
-
|
|
51
|
+
bg: 'bg-amber-bg',
|
|
50
52
|
surface: 'bg-amber-surface',
|
|
51
|
-
|
|
53
|
+
fg: 'text-amber-fg',
|
|
52
54
|
text: 'text-amber-text',
|
|
53
55
|
border: 'border-amber-border',
|
|
54
56
|
},
|
|
55
57
|
{
|
|
56
58
|
hue: 'yellow',
|
|
57
|
-
|
|
59
|
+
bg: 'bg-yellow-bg',
|
|
58
60
|
surface: 'bg-yellow-surface',
|
|
59
|
-
|
|
61
|
+
fg: 'text-yellow-fg',
|
|
60
62
|
text: 'text-yellow-text',
|
|
61
63
|
border: 'border-yellow-border',
|
|
62
64
|
},
|
|
63
65
|
{
|
|
64
66
|
hue: 'lime',
|
|
65
|
-
|
|
67
|
+
bg: 'bg-lime-bg',
|
|
66
68
|
surface: 'bg-lime-surface',
|
|
67
|
-
|
|
69
|
+
fg: 'text-lime-fg',
|
|
68
70
|
text: 'text-lime-text',
|
|
69
71
|
border: 'border-lime-border',
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
74
|
hue: 'green',
|
|
73
|
-
|
|
75
|
+
bg: 'bg-green-bg',
|
|
74
76
|
surface: 'bg-green-surface',
|
|
75
|
-
|
|
77
|
+
fg: 'text-green-fg',
|
|
76
78
|
text: 'text-green-text',
|
|
77
79
|
border: 'border-green-border',
|
|
78
80
|
},
|
|
79
81
|
{
|
|
80
82
|
hue: 'emerald',
|
|
81
|
-
|
|
83
|
+
bg: 'bg-emerald-bg',
|
|
82
84
|
surface: 'bg-emerald-surface',
|
|
83
|
-
|
|
85
|
+
fg: 'text-emerald-fg',
|
|
84
86
|
text: 'text-emerald-text',
|
|
85
87
|
border: 'border-emerald-border',
|
|
86
88
|
},
|
|
87
89
|
{
|
|
88
90
|
hue: 'teal',
|
|
89
|
-
|
|
91
|
+
bg: 'bg-teal-bg',
|
|
90
92
|
surface: 'bg-teal-surface',
|
|
91
|
-
|
|
93
|
+
fg: 'text-teal-fg',
|
|
92
94
|
text: 'text-teal-text',
|
|
93
95
|
border: 'border-teal-border',
|
|
94
96
|
},
|
|
95
97
|
{
|
|
96
98
|
hue: 'cyan',
|
|
97
|
-
|
|
99
|
+
bg: 'bg-cyan-bg',
|
|
98
100
|
surface: 'bg-cyan-surface',
|
|
99
|
-
|
|
101
|
+
fg: 'text-cyan-fg',
|
|
100
102
|
text: 'text-cyan-text',
|
|
101
103
|
border: 'border-cyan-border',
|
|
102
104
|
},
|
|
103
105
|
{
|
|
104
106
|
hue: 'sky',
|
|
105
|
-
|
|
107
|
+
bg: 'bg-sky-bg',
|
|
106
108
|
surface: 'bg-sky-surface',
|
|
107
|
-
|
|
109
|
+
fg: 'text-sky-fg',
|
|
108
110
|
text: 'text-sky-text',
|
|
109
111
|
border: 'border-sky-border',
|
|
110
112
|
},
|
|
111
113
|
{
|
|
112
114
|
hue: 'blue',
|
|
113
|
-
|
|
115
|
+
bg: 'bg-blue-bg',
|
|
114
116
|
surface: 'bg-blue-surface',
|
|
115
|
-
|
|
117
|
+
fg: 'text-blue-fg',
|
|
116
118
|
text: 'text-blue-text',
|
|
117
119
|
border: 'border-blue-border',
|
|
118
120
|
},
|
|
119
121
|
{
|
|
120
122
|
hue: 'indigo',
|
|
121
|
-
|
|
123
|
+
bg: 'bg-indigo-bg',
|
|
122
124
|
surface: 'bg-indigo-surface',
|
|
123
|
-
|
|
125
|
+
fg: 'text-indigo-fg',
|
|
124
126
|
text: 'text-indigo-text',
|
|
125
127
|
border: 'border-indigo-border',
|
|
126
128
|
},
|
|
127
129
|
{
|
|
128
130
|
hue: 'violet',
|
|
129
|
-
|
|
131
|
+
bg: 'bg-violet-bg',
|
|
130
132
|
surface: 'bg-violet-surface',
|
|
131
|
-
|
|
133
|
+
fg: 'text-violet-fg',
|
|
132
134
|
text: 'text-violet-text',
|
|
133
135
|
border: 'border-violet-border',
|
|
134
136
|
},
|
|
135
137
|
{
|
|
136
138
|
hue: 'purple',
|
|
137
|
-
|
|
139
|
+
bg: 'bg-purple-bg',
|
|
138
140
|
surface: 'bg-purple-surface',
|
|
139
|
-
|
|
141
|
+
fg: 'text-purple-fg',
|
|
140
142
|
text: 'text-purple-text',
|
|
141
143
|
border: 'border-purple-border',
|
|
142
144
|
},
|
|
143
145
|
{
|
|
144
146
|
hue: 'fuchsia',
|
|
145
|
-
|
|
147
|
+
bg: 'bg-fuchsia-bg',
|
|
146
148
|
surface: 'bg-fuchsia-surface',
|
|
147
|
-
|
|
149
|
+
fg: 'text-fuchsia-fg',
|
|
148
150
|
text: 'text-fuchsia-text',
|
|
149
151
|
border: 'border-fuchsia-border',
|
|
150
152
|
},
|
|
151
153
|
{
|
|
152
154
|
hue: 'pink',
|
|
153
|
-
|
|
155
|
+
bg: 'bg-pink-bg',
|
|
154
156
|
surface: 'bg-pink-surface',
|
|
155
|
-
|
|
157
|
+
fg: 'text-pink-fg',
|
|
156
158
|
text: 'text-pink-text',
|
|
157
159
|
border: 'border-pink-border',
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
162
|
hue: 'rose',
|
|
161
|
-
|
|
163
|
+
bg: 'bg-rose-bg',
|
|
162
164
|
surface: 'bg-rose-surface',
|
|
163
|
-
|
|
165
|
+
fg: 'text-rose-fg',
|
|
164
166
|
text: 'text-rose-text',
|
|
165
167
|
border: 'border-rose-border',
|
|
166
168
|
},
|
|
@@ -171,6 +173,16 @@ export const palette = {
|
|
|
171
173
|
hues: styles,
|
|
172
174
|
};
|
|
173
175
|
|
|
176
|
+
const validHues: ReadonlySet<Hue> = new Set<Hue>([neutral.hue, ...styles.map((s) => s.hue)]);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Normalise an arbitrary string into a known `Hue`, falling back to `'neutral'` when the
|
|
180
|
+
* input doesn't match one of the catalogued palette entries. Useful when accepting hue
|
|
181
|
+
* values from user-authored data (e.g. ECHO objects, plugin settings) that need to be
|
|
182
|
+
* forwarded to a hue-keyed prop like `Tag`'s `palette`.
|
|
183
|
+
*/
|
|
184
|
+
export const toHue = (hue: string | undefined): Hue => (hue && validHues.has(hue as Hue) ? (hue as Hue) : 'neutral');
|
|
185
|
+
|
|
174
186
|
// TODO(burdon): Rename getClassNames.
|
|
175
187
|
export const getStyles = (hue: string): ColorStyles => {
|
|
176
188
|
return styles.find((color) => color.hue === hue) || neutral;
|