@usetheo/ui 0.35.4 → 0.36.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/dist/components/composites/stepper/stepper.d.ts +6 -0
- package/dist/components/primitives/card/card.d.ts +19 -0
- package/dist/components/primitives/dialog/dialog.d.ts +2 -0
- package/dist/components/primitives/sidebar/sidebar.d.ts +28 -1
- package/dist/components.css +2 -0
- package/dist/fonts/geist-400.woff2 +0 -0
- package/dist/fonts/geist-500.woff2 +0 -0
- package/dist/fonts/geist-600.woff2 +0 -0
- package/dist/fonts/geist-mono-400.woff2 +0 -0
- package/dist/fonts/geist-mono-500.woff2 +0 -0
- package/dist/fonts/geist-mono-600.woff2 +0 -0
- package/dist/fonts-cdn.css +28 -0
- package/dist/fonts.css +75 -0
- package/dist/index.js +63 -23
- package/dist/preset.css +27 -0
- package/dist/styles-v3-legacy.css +88 -0
- package/dist/styles.css +137 -0
- package/dist/tokens-v4.css +223 -0
- package/dist/tokens.css +315 -0
- package/package.json +15 -4
package/dist/styles.css
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* Theo UI — Tailwind v4 prebuilt entry stylesheet.
|
|
2
|
+
*
|
|
3
|
+
* What ships at `@theokit/ui/styles.css` since RFC 0008 follow-up
|
|
4
|
+
* (`0.5.1-next.0`). Consumer imports this once and the framework's
|
|
5
|
+
* Tailwind v4 build picks up:
|
|
6
|
+
* - `@import "tailwindcss"` — Tailwind v4 core (preflight + utilities)
|
|
7
|
+
* - `@import "./fonts.css"` — self-hosted Geist + Geist Mono @font-face
|
|
8
|
+
* - `@import "./tokens.css"` — runtime `--*` vars (theme cascade target)
|
|
9
|
+
* - `@import "./tokens-v4.css"` — `@theme { --color-*, --text-*, … }`
|
|
10
|
+
* namespace Tailwind v4 reads to emit utilities
|
|
11
|
+
* - `@layer base` — `border-color`, body font, focus ring, scrollbar
|
|
12
|
+
* styling that depend on the runtime vars
|
|
13
|
+
*
|
|
14
|
+
* v3-only consumers (Tailwind 3.x + the shadcn registry preset) should
|
|
15
|
+
* load `@theokit/ui/styles-v3-legacy.css` instead — that file is the
|
|
16
|
+
* unchanged `@tailwind base; @tailwind components; @tailwind utilities`
|
|
17
|
+
* variant. See RFC 0008 follow-up.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
@import "tailwindcss";
|
|
21
|
+
@import "./fonts.css";
|
|
22
|
+
@import "./tokens.css";
|
|
23
|
+
@import "./tokens-v4.css";
|
|
24
|
+
|
|
25
|
+
@layer base {
|
|
26
|
+
* {
|
|
27
|
+
border-color: hsl(var(--border));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Tailwind v4 preflight regression compensation —
|
|
31
|
+
* v4 intentionally dropped the `button { cursor: pointer }` rule
|
|
32
|
+
* (https://tailwindcss.com/docs/upgrade-guide#default-button-cursor)
|
|
33
|
+
* so every native <button> renders with the default arrow cursor.
|
|
34
|
+
*
|
|
35
|
+
* Library authors are expected to opt in explicitly per button. The
|
|
36
|
+
* Violet Forge `<Button>` primitive does so (see its CVA). For the
|
|
37
|
+
* dozens of native <button> elements composites render directly,
|
|
38
|
+
* we restore the v3 behavior here — scoped to *interactive* buttons:
|
|
39
|
+
*
|
|
40
|
+
* - `:not(:disabled)` skips the HTML `disabled` attribute path
|
|
41
|
+
* (browser already shows `cursor: default` for disabled, but the
|
|
42
|
+
* rule below stays explicit for clarity).
|
|
43
|
+
* - `:not([aria-disabled="true"])` skips the ARIA-disabled path
|
|
44
|
+
* (where the button stays focusable but acts as no-op).
|
|
45
|
+
*
|
|
46
|
+
* This is an EXPLICIT opt-in by the library author for consumers who
|
|
47
|
+
* imported `@theokit/ui/styles.css` — it does NOT alter Tailwind's
|
|
48
|
+
* global preflight, only this stylesheet's @layer base.
|
|
49
|
+
*/
|
|
50
|
+
button:not(:disabled):not([aria-disabled="true"]),
|
|
51
|
+
[role="button"]:not([aria-disabled="true"]) {
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
html {
|
|
56
|
+
font-family: var(--font-body);
|
|
57
|
+
-webkit-font-smoothing: antialiased;
|
|
58
|
+
-moz-osx-font-smoothing: grayscale;
|
|
59
|
+
text-rendering: optimizeLegibility;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
body {
|
|
63
|
+
background-color: hsl(var(--background));
|
|
64
|
+
color: hsl(var(--foreground));
|
|
65
|
+
/* OpenType ligatures globally — Geist's "liga" feature is structural
|
|
66
|
+
* and gives the Vercel-tight glyph combinations. */
|
|
67
|
+
font-feature-settings: "liga" 1, "ss01" 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Display headings inherit theme display font (Geist by default).
|
|
71
|
+
* Per-size tracking is set in tokens-v4.css; this is the safety net. */
|
|
72
|
+
h1,
|
|
73
|
+
h2,
|
|
74
|
+
h3,
|
|
75
|
+
h4 {
|
|
76
|
+
font-family: var(--font-display);
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Code surfaces: use the mono token + tabular numerals so columns of
|
|
81
|
+
* numbers line up in tables/logs/metrics. */
|
|
82
|
+
code,
|
|
83
|
+
pre,
|
|
84
|
+
kbd,
|
|
85
|
+
samp {
|
|
86
|
+
font-family: var(--font-mono);
|
|
87
|
+
font-feature-settings: "tnum" 1, "liga" 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Focus ring — violet (signature) */
|
|
91
|
+
:focus-visible {
|
|
92
|
+
outline: 2px solid hsl(var(--ring));
|
|
93
|
+
outline-offset: 2px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Selection — soft violet */
|
|
97
|
+
::selection {
|
|
98
|
+
background-color: hsl(var(--primary) / 0.25);
|
|
99
|
+
color: hsl(var(--foreground));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Native scrollbar fallback (Violet Forge).
|
|
103
|
+
* Used for native browser scroll on elements that don't wrap in <ScrollArea>.
|
|
104
|
+
* Components that DO use <ScrollArea> hide the native scrollbar via Radix.
|
|
105
|
+
*/
|
|
106
|
+
*::-webkit-scrollbar {
|
|
107
|
+
width: 10px;
|
|
108
|
+
height: 10px;
|
|
109
|
+
}
|
|
110
|
+
*::-webkit-scrollbar-track {
|
|
111
|
+
background: transparent;
|
|
112
|
+
}
|
|
113
|
+
*::-webkit-scrollbar-thumb {
|
|
114
|
+
background-color: hsl(var(--primary) / 0.25);
|
|
115
|
+
border: 2px solid transparent;
|
|
116
|
+
background-clip: content-box;
|
|
117
|
+
border-radius: 9999px;
|
|
118
|
+
transition: background-color var(--duration-fast) var(--ease-out-soft);
|
|
119
|
+
}
|
|
120
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
121
|
+
background-color: hsl(var(--primary) / 0.5);
|
|
122
|
+
}
|
|
123
|
+
*::-webkit-scrollbar-thumb:active {
|
|
124
|
+
background-color: hsl(var(--primary) / 0.75);
|
|
125
|
+
}
|
|
126
|
+
* {
|
|
127
|
+
scrollbar-width: thin;
|
|
128
|
+
scrollbar-color: hsl(var(--primary) / 0.3) transparent;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* RFC 0008 follow-up #2 — pre-compiled utility rules. The bytes here
|
|
133
|
+
* are emitted at build time by `scripts/build-precompiled-css.ts` so
|
|
134
|
+
* consumers do not depend on Tailwind v4 `@source` scanning the
|
|
135
|
+
* library's node_modules tree (which breaks under pnpm symlinks).
|
|
136
|
+
*/
|
|
137
|
+
@import "./components.css";
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/* Theo UI — Tailwind v4 theme aliases.
|
|
2
|
+
*
|
|
3
|
+
* Companion to `tokens.css`. The base file declares the v3-style runtime
|
|
4
|
+
* variables (`--primary`, `--background`, …) that `<ThemeProvider>` and
|
|
5
|
+
* `[data-theme]` cascades mutate at runtime. This file declares the
|
|
6
|
+
* Tailwind v4 `@theme` namespace (`--color-primary`, `--color-background`,
|
|
7
|
+
* …) by aliasing back to those runtime vars via `hsl(var(--*))`.
|
|
8
|
+
*
|
|
9
|
+
* Why two layers:
|
|
10
|
+
* - `<ThemeProvider>` (and the 10 built-in themes) sets `--primary`
|
|
11
|
+
* etc. at runtime — that's the theme switch surface.
|
|
12
|
+
* - Tailwind v4 resolves color utilities exclusively through
|
|
13
|
+
* `--color-*` tokens declared in `@theme`. Without these aliases
|
|
14
|
+
* `bg-primary`, `text-muted-foreground`, `border-border`, etc. emit
|
|
15
|
+
* a className with no matching CSS rule.
|
|
16
|
+
*
|
|
17
|
+
* The indirection (`--color-primary: var(--primary)`) preserves the
|
|
18
|
+
* runtime theme cascade — browsers resolve `var(--color-primary)` →
|
|
19
|
+
* `var(--primary)` → current `[data-theme]` value at paint time.
|
|
20
|
+
*
|
|
21
|
+
* See RFC 0008 follow-up.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* `@theme inline` (Tailwind v4 keyword) preserves the var() reference at
|
|
25
|
+
* runtime instead of inlining the value at build time. Without `inline`,
|
|
26
|
+
* `--color-primary: var(--primary)` would resolve to whatever `--primary`
|
|
27
|
+
* was when Tailwind compiled the CSS — breaking the theme cascade.
|
|
28
|
+
* See https://tailwindcss.com/docs/theme#using-your-theme-variables-in-arbitrary-values
|
|
29
|
+
*/
|
|
30
|
+
@theme inline {
|
|
31
|
+
/* Color tokens ------------------------------------------------------ */
|
|
32
|
+
--color-background: var(--background);
|
|
33
|
+
--color-foreground: var(--foreground);
|
|
34
|
+
|
|
35
|
+
--color-card: var(--card);
|
|
36
|
+
--color-card-foreground: var(--card-foreground);
|
|
37
|
+
|
|
38
|
+
--color-popover: var(--popover);
|
|
39
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
40
|
+
|
|
41
|
+
--color-primary: var(--primary);
|
|
42
|
+
--color-primary-deep: var(--primary-deep);
|
|
43
|
+
--color-primary-glow: var(--primary-glow);
|
|
44
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
45
|
+
|
|
46
|
+
--color-secondary: var(--secondary);
|
|
47
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
48
|
+
|
|
49
|
+
--color-accent: var(--accent);
|
|
50
|
+
--color-accent-deep: var(--accent-deep);
|
|
51
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
52
|
+
|
|
53
|
+
--color-muted: var(--muted);
|
|
54
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
55
|
+
|
|
56
|
+
--color-border: var(--border);
|
|
57
|
+
--color-input: var(--input);
|
|
58
|
+
--color-ring: var(--ring);
|
|
59
|
+
|
|
60
|
+
--color-success: var(--success);
|
|
61
|
+
--color-success-foreground: var(--success-foreground);
|
|
62
|
+
--color-warning: var(--warning);
|
|
63
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
64
|
+
--color-destructive: var(--destructive);
|
|
65
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
66
|
+
--color-info: var(--info);
|
|
67
|
+
--color-info-foreground: var(--info-foreground);
|
|
68
|
+
|
|
69
|
+
/* Status group (D4) ------------------------------------------------- */
|
|
70
|
+
--color-status-online: var(--status-online);
|
|
71
|
+
--color-status-online-foreground: var(--status-online-foreground);
|
|
72
|
+
--color-status-offline: var(--status-offline);
|
|
73
|
+
--color-status-offline-foreground: var(--status-offline-foreground);
|
|
74
|
+
--color-status-degraded: var(--status-degraded);
|
|
75
|
+
--color-status-degraded-foreground: var(--status-degraded-foreground);
|
|
76
|
+
--color-status-info: var(--status-info);
|
|
77
|
+
--color-status-info-foreground: var(--status-info-foreground);
|
|
78
|
+
|
|
79
|
+
/* Fonts ------------------------------------------------------------- */
|
|
80
|
+
--font-display: var(--font-display);
|
|
81
|
+
--font-sans: var(--font-body);
|
|
82
|
+
--font-mono: var(--font-mono);
|
|
83
|
+
|
|
84
|
+
/* Violet Forge typescale -------------------------------------------- *
|
|
85
|
+
* Compound text utilities: each `--text-{name}` declares the size,
|
|
86
|
+
* with companion `--*--line-height`, `--*--letter-spacing`, and
|
|
87
|
+
* `--*--font-weight` properties Tailwind v4 picks up automatically.
|
|
88
|
+
*/
|
|
89
|
+
--text-display-2xl: 64px;
|
|
90
|
+
--text-display-2xl--line-height: 1;
|
|
91
|
+
--text-display-2xl--letter-spacing: -0.0464em;
|
|
92
|
+
--text-display-2xl--font-weight: 600;
|
|
93
|
+
|
|
94
|
+
--text-display-xl: 48px;
|
|
95
|
+
--text-display-xl--line-height: 1.05;
|
|
96
|
+
--text-display-xl--letter-spacing: -0.05em;
|
|
97
|
+
--text-display-xl--font-weight: 600;
|
|
98
|
+
|
|
99
|
+
--text-display-lg: 40px;
|
|
100
|
+
--text-display-lg--line-height: 1.1;
|
|
101
|
+
--text-display-lg--letter-spacing: -0.05em;
|
|
102
|
+
--text-display-lg--font-weight: 600;
|
|
103
|
+
|
|
104
|
+
--text-display-md: 32px;
|
|
105
|
+
--text-display-md--line-height: 1.2;
|
|
106
|
+
--text-display-md--letter-spacing: -0.04em;
|
|
107
|
+
--text-display-md--font-weight: 600;
|
|
108
|
+
|
|
109
|
+
--text-headline: 28px;
|
|
110
|
+
--text-headline--line-height: 1.25;
|
|
111
|
+
--text-headline--letter-spacing: -0.035em;
|
|
112
|
+
--text-headline--font-weight: 600;
|
|
113
|
+
|
|
114
|
+
--text-title-lg: 24px;
|
|
115
|
+
--text-title-lg--line-height: 1.33;
|
|
116
|
+
--text-title-lg--letter-spacing: -0.04em;
|
|
117
|
+
--text-title-lg--font-weight: 600;
|
|
118
|
+
|
|
119
|
+
--text-title-md: 20px;
|
|
120
|
+
--text-title-md--line-height: 1.4;
|
|
121
|
+
--text-title-md--letter-spacing: -0.03em;
|
|
122
|
+
--text-title-md--font-weight: 600;
|
|
123
|
+
|
|
124
|
+
--text-body-lg: 18px;
|
|
125
|
+
--text-body-lg--line-height: 1.56;
|
|
126
|
+
--text-body-lg--letter-spacing: -0.01em;
|
|
127
|
+
--text-body-lg--font-weight: 400;
|
|
128
|
+
|
|
129
|
+
--text-body-md: 14px;
|
|
130
|
+
--text-body-md--line-height: 1.43;
|
|
131
|
+
--text-body-md--letter-spacing: 0;
|
|
132
|
+
--text-body-md--font-weight: 400;
|
|
133
|
+
|
|
134
|
+
--text-body-sm: 13px;
|
|
135
|
+
--text-body-sm--line-height: 1.46;
|
|
136
|
+
--text-body-sm--font-weight: 400;
|
|
137
|
+
|
|
138
|
+
--text-label: 14px;
|
|
139
|
+
--text-label--line-height: 1.43;
|
|
140
|
+
--text-label--font-weight: 500;
|
|
141
|
+
|
|
142
|
+
--text-label-caps: 12px;
|
|
143
|
+
--text-label-caps--line-height: 1.33;
|
|
144
|
+
--text-label-caps--letter-spacing: 0.04em;
|
|
145
|
+
--text-label-caps--font-weight: 500;
|
|
146
|
+
|
|
147
|
+
--text-code-md: 14px;
|
|
148
|
+
--text-code-md--line-height: 1.5;
|
|
149
|
+
--text-code-md--font-weight: 400;
|
|
150
|
+
|
|
151
|
+
--text-code-sm: 13px;
|
|
152
|
+
--text-code-sm--line-height: 1.54;
|
|
153
|
+
--text-code-sm--font-weight: 500;
|
|
154
|
+
|
|
155
|
+
/* Radii — Tailwind v4 reads the value directly (no var indirection). */
|
|
156
|
+
--radius-none: 0px;
|
|
157
|
+
--radius-sm: 4px;
|
|
158
|
+
--radius-md: 6px;
|
|
159
|
+
--radius-lg: 10px;
|
|
160
|
+
--radius-xl: 14px;
|
|
161
|
+
--radius-2xl: 20px;
|
|
162
|
+
--radius-full: 9999px;
|
|
163
|
+
|
|
164
|
+
/* Shadows — alias to tokens.css computed values (which already use
|
|
165
|
+
* `hsl(var(--foreground) / …)` so theme-switch keeps them in step). */
|
|
166
|
+
--shadow-sm: var(--shadow-sm);
|
|
167
|
+
--shadow-md: var(--shadow-md);
|
|
168
|
+
--shadow-lg: var(--shadow-lg);
|
|
169
|
+
--shadow-glow: var(--shadow-glow);
|
|
170
|
+
--shadow-glow-strong: var(--shadow-glow-strong);
|
|
171
|
+
|
|
172
|
+
/* Motion ------------------------------------------------------------ */
|
|
173
|
+
--ease-out-soft: var(--ease-out-soft);
|
|
174
|
+
--ease-snap: var(--ease-snap);
|
|
175
|
+
|
|
176
|
+
/* Container queries (T5.5) — Tailwind v4 native @container support.
|
|
177
|
+
* Sizes drive `@<size>:<utility>` variants for container-relative responsive
|
|
178
|
+
* design (components that adapt to their parent's width, not the viewport).
|
|
179
|
+
* Default sizes mirror Tailwind v4 defaults; declared explicitly so the
|
|
180
|
+
* preset ships consistent values.
|
|
181
|
+
*/
|
|
182
|
+
--container-3xs: 16rem;
|
|
183
|
+
--container-2xs: 18rem;
|
|
184
|
+
--container-xs: 20rem;
|
|
185
|
+
--container-sm: 24rem;
|
|
186
|
+
--container-md: 28rem;
|
|
187
|
+
--container-lg: 32rem;
|
|
188
|
+
--container-xl: 36rem;
|
|
189
|
+
--container-2xl: 42rem;
|
|
190
|
+
--container-3xl: 48rem;
|
|
191
|
+
--container-4xl: 56rem;
|
|
192
|
+
--container-5xl: 64rem;
|
|
193
|
+
--container-6xl: 72rem;
|
|
194
|
+
--container-7xl: 80rem;
|
|
195
|
+
|
|
196
|
+
/* Animations — declare so `animate-fade-in-up` and `animate-pulse-glow`
|
|
197
|
+
* resolve. Keyframes are declared below (outside @theme — Tailwind v4
|
|
198
|
+
* picks them up via the @keyframes namespace).
|
|
199
|
+
*/
|
|
200
|
+
--animate-fade-in-up: fade-in-up var(--duration-base) var(--ease-out-soft) both;
|
|
201
|
+
--animate-pulse-glow: pulse-glow 1.5s var(--ease-in-out) infinite;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@keyframes fade-in-up {
|
|
205
|
+
0% {
|
|
206
|
+
opacity: 0;
|
|
207
|
+
transform: translateY(8px);
|
|
208
|
+
}
|
|
209
|
+
100% {
|
|
210
|
+
opacity: 1;
|
|
211
|
+
transform: translateY(0);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@keyframes pulse-glow {
|
|
216
|
+
0%,
|
|
217
|
+
100% {
|
|
218
|
+
box-shadow: 0 0 0 0 color-mix(in oklch, var(--primary) 50%, transparent);
|
|
219
|
+
}
|
|
220
|
+
50% {
|
|
221
|
+
box-shadow: 0 0 0 8px transparent;
|
|
222
|
+
}
|
|
223
|
+
}
|
package/dist/tokens.css
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/* Theo UI — Design System "Violet Forge" tokens.
|
|
2
|
+
*
|
|
3
|
+
* Tokens normativos. Não edite valores aqui sem revisão do design system.
|
|
4
|
+
* Referência: docs/design-system.md
|
|
5
|
+
*
|
|
6
|
+
* Estratégia:
|
|
7
|
+
* - CSS custom properties para todas as cores, scales e durations.
|
|
8
|
+
* - Default é light. Dark é ativado via [data-theme="dark"] ou .dark.
|
|
9
|
+
* - HSL split (h s l) em vez de hex permite alpha via color-mix() ou hsl(var(--x) / .5).
|
|
10
|
+
*
|
|
11
|
+
* Roxo Theo: #A855F7 → oklch(0.628 0.225 296) (electric violet — unified 2026-06-09)
|
|
12
|
+
* Burnt sienna: #C96442 → hsl(15 54% 53%)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* `:root` and `[data-theme="dark"]` declarations are intentionally NOT wrapped
|
|
16
|
+
* in `@layer base` so they apply at the document level regardless of whether
|
|
17
|
+
* the consumer's CSS pipeline runs Tailwind's preflight. The texture utilities
|
|
18
|
+
* below remain in `@layer utilities` because that layer affects ordering only,
|
|
19
|
+
* not the cascade root.
|
|
20
|
+
*/
|
|
21
|
+
:root {
|
|
22
|
+
/* Base palette ------------------------------------------------------- *
|
|
23
|
+
* Neutrals are 100% desaturated (Vercel-style). Color comes from the
|
|
24
|
+
* primary (violet) + accent (burnt sienna) tokens below — never from
|
|
25
|
+
* surface tints.
|
|
26
|
+
*/
|
|
27
|
+
--background: oklch(1 0 0); /* #FFFFFF */
|
|
28
|
+
--foreground: oklch(0.146 0 0); /* #0A0A0A */
|
|
29
|
+
|
|
30
|
+
--card: oklch(1 0 0); /* #FFFFFF */
|
|
31
|
+
--card-foreground: oklch(0.146 0 0);
|
|
32
|
+
|
|
33
|
+
--popover: oklch(1 0 0);
|
|
34
|
+
--popover-foreground: oklch(0.146 0 0);
|
|
35
|
+
|
|
36
|
+
/* Primary — Theo violet (equity) ------------------------------------- *
|
|
37
|
+
* Post-T3.1 (ADR-0006): -deep and -glow are derived algorithmically via
|
|
38
|
+
* OKLCH relative-color syntax. Themes that need a per-token override may
|
|
39
|
+
* still declare these explicitly — the cascade resolves the override.
|
|
40
|
+
* `max()`/`min()` clamps protect dark/light themes from L overflow (EC-7).
|
|
41
|
+
*/
|
|
42
|
+
--primary: oklch(0.628 0.225 296); /* #A855F7 — Theo violet electric (unified 2026-06-09) */
|
|
43
|
+
--primary-deep: oklch(from var(--primary) max(0.05, calc(l - 0.16)) c h);
|
|
44
|
+
--primary-glow: oklch(from var(--primary) min(0.95, calc(l + 0.18)) c h);
|
|
45
|
+
--primary-foreground: oklch(1 0 0);
|
|
46
|
+
|
|
47
|
+
/* Secondary — neutral muted ----------------------------------------- */
|
|
48
|
+
--secondary: oklch(0.97 0 0); /* #F5F5F5 */
|
|
49
|
+
--secondary-foreground: oklch(0.146 0 0);
|
|
50
|
+
|
|
51
|
+
/* Accent — burnt sienna --------------------------------------------- *
|
|
52
|
+
* T3.1: accent-deep derived. Override path identical to primary.
|
|
53
|
+
*/
|
|
54
|
+
--accent: oklch(0.621 0.132 39); /* #C96442 */
|
|
55
|
+
--accent-deep: oklch(from var(--accent) max(0.05, calc(l - 0.13)) c h);
|
|
56
|
+
--accent-foreground: oklch(1 0 0);
|
|
57
|
+
|
|
58
|
+
/* Muted ------------------------------------------------------------- */
|
|
59
|
+
--muted: oklch(0.97 0 0);
|
|
60
|
+
--muted-foreground: oklch(0.555 0 0); /* #737373 */
|
|
61
|
+
|
|
62
|
+
/* Surfaces, borders, inputs ----------------------------------------- */
|
|
63
|
+
--border: oklch(0.931 0 0); /* #E8E8E8 — Vercel-style hairline */
|
|
64
|
+
--input: oklch(0.931 0 0);
|
|
65
|
+
--ring: oklch(0.628 0.225 296); /* matches primary */
|
|
66
|
+
|
|
67
|
+
/* Semantics --------------------------------------------------------- */
|
|
68
|
+
--success: oklch(0.611 0.161 149.7); /* #16A34A */
|
|
69
|
+
--success-foreground: oklch(1 0 0);
|
|
70
|
+
--warning: oklch(0.67 0.154 60.6); /* #D97706 */
|
|
71
|
+
--warning-foreground: oklch(1 0 0);
|
|
72
|
+
--destructive: oklch(0.579 0.214 27.2); /* #DC2626 */
|
|
73
|
+
--destructive-foreground: oklch(1 0 0);
|
|
74
|
+
--info: oklch(0.626 0.186 259.6); /* #3B82F6 */
|
|
75
|
+
--info-foreground: oklch(1 0 0);
|
|
76
|
+
|
|
77
|
+
/* Status (D4 — operational state group) ----------------------------- *
|
|
78
|
+
* Separate from semantic group (success/destructive/warning/info, which
|
|
79
|
+
* are action-result colors). Status describes component liveness:
|
|
80
|
+
* online (alive), offline (dead), degraded (alive-but-slow), info (flag).
|
|
81
|
+
* Defaults mirror semantic counterparts; themes override for distinction.
|
|
82
|
+
*/
|
|
83
|
+
--status-online: oklch(0.611 0.161 149.7);
|
|
84
|
+
--status-online-foreground: oklch(1 0 0);
|
|
85
|
+
--status-offline: oklch(0.579 0.214 27.2);
|
|
86
|
+
--status-offline-foreground: oklch(1 0 0);
|
|
87
|
+
--status-degraded: oklch(0.67 0.154 60.6);
|
|
88
|
+
--status-degraded-foreground: oklch(1 0 0);
|
|
89
|
+
--status-info: oklch(0.626 0.186 259.6);
|
|
90
|
+
--status-info-foreground: oklch(1 0 0);
|
|
91
|
+
|
|
92
|
+
/* Radii ------------------------------------------------------------- */
|
|
93
|
+
--radius-none: 0px;
|
|
94
|
+
--radius-sm: 4px;
|
|
95
|
+
--radius-md: 6px;
|
|
96
|
+
--radius-lg: 10px;
|
|
97
|
+
--radius-xl: 14px;
|
|
98
|
+
--radius-2xl: 20px;
|
|
99
|
+
--radius-full: 9999px;
|
|
100
|
+
/* shadcn compat */
|
|
101
|
+
--radius: 14px;
|
|
102
|
+
|
|
103
|
+
/* Spacing scale (4px base) ------------------------------------------ */
|
|
104
|
+
--space-1: 4px;
|
|
105
|
+
--space-2: 8px;
|
|
106
|
+
--space-3: 12px;
|
|
107
|
+
--space-4: 16px;
|
|
108
|
+
--space-5: 20px;
|
|
109
|
+
--space-6: 24px;
|
|
110
|
+
--space-8: 32px;
|
|
111
|
+
--space-10: 40px;
|
|
112
|
+
--space-12: 48px;
|
|
113
|
+
--space-16: 64px;
|
|
114
|
+
--space-20: 80px;
|
|
115
|
+
--space-24: 96px;
|
|
116
|
+
--space-32: 128px;
|
|
117
|
+
|
|
118
|
+
/* Elevation — theme-aware (uses --foreground for shadow ink,
|
|
119
|
+
* --primary for the signature glow, so swapping themes recolors them).
|
|
120
|
+
* Post-T2.5: tokens are OKLCH; we compose alpha via color-mix(in oklch, ...)
|
|
121
|
+
* which is perceptually correct AND works with any color-space variable.
|
|
122
|
+
*/
|
|
123
|
+
--shadow-sm: 0 1px 2px 0 color-mix(in oklch, var(--foreground) 6%, transparent);
|
|
124
|
+
--shadow-md: 0 2px 8px -2px color-mix(in oklch, var(--foreground) 8%, transparent), 0 1px 3px
|
|
125
|
+
color-mix(in oklch, var(--foreground) 6%, transparent);
|
|
126
|
+
--shadow-lg: 0 12px 32px -8px color-mix(in oklch, var(--foreground) 12%, transparent), 0 4px 12px
|
|
127
|
+
color-mix(in oklch, var(--foreground) 8%, transparent);
|
|
128
|
+
--shadow-glow: 0 0 24px color-mix(in oklch, var(--primary) 25%, transparent);
|
|
129
|
+
--shadow-glow-strong: 0 0 32px color-mix(in oklch, var(--primary) 40%, transparent);
|
|
130
|
+
|
|
131
|
+
/* Motion ------------------------------------------------------------ */
|
|
132
|
+
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
|
|
133
|
+
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
134
|
+
--ease-snap: cubic-bezier(0.85, 0, 0.15, 1);
|
|
135
|
+
--duration-fast: 120ms;
|
|
136
|
+
--duration-base: 200ms;
|
|
137
|
+
--duration-slow: 360ms;
|
|
138
|
+
--stagger: 60ms;
|
|
139
|
+
|
|
140
|
+
/* Typography (Geist family — Vercel-inspired Violet Forge) ---------- */
|
|
141
|
+
--font-display: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
142
|
+
--font-body: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
143
|
+
--font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Dark mode — dominante --------------------------------------------- *
|
|
147
|
+
* Vercel-aligned grayscale: pure neutrals (0% saturation) for every
|
|
148
|
+
* surface and text layer. Color only in primary (violet), accent (burnt
|
|
149
|
+
* sienna), and semantic tokens (success/warning/destructive/info).
|
|
150
|
+
*
|
|
151
|
+
* Activated exclusively via the `.dark` class on `<html>` (set by
|
|
152
|
+
* `ThemeProvider`, `ThemeScript`, or consumers manually). The previous
|
|
153
|
+
* `[data-theme="dark"]` companion selector was dead — `data-theme` holds
|
|
154
|
+
* the theme NAME, never the literal `"dark"`.
|
|
155
|
+
*/
|
|
156
|
+
.dark {
|
|
157
|
+
--background: oklch(0.146 0 0); /* #0A0A0A */
|
|
158
|
+
--foreground: oklch(0.97 0 0); /* #F5F5F5 */
|
|
159
|
+
|
|
160
|
+
--card: oklch(0.182 0 0); /* #121212 */
|
|
161
|
+
--card-foreground: oklch(0.97 0 0);
|
|
162
|
+
|
|
163
|
+
--popover: oklch(0.204 0 0); /* #171717 */
|
|
164
|
+
--popover-foreground: oklch(0.97 0 0);
|
|
165
|
+
|
|
166
|
+
--primary: oklch(0.628 0.225 296);
|
|
167
|
+
--primary-deep: oklch(from var(--primary) max(0.05, calc(l - 0.16)) c h);
|
|
168
|
+
--primary-glow: oklch(from var(--primary) min(0.95, calc(l + 0.18)) c h);
|
|
169
|
+
--primary-foreground: oklch(1 0 0);
|
|
170
|
+
|
|
171
|
+
--secondary: oklch(0.227 0 0); /* #1C1C1C */
|
|
172
|
+
--secondary-foreground: oklch(0.97 0 0);
|
|
173
|
+
|
|
174
|
+
--accent: oklch(0.621 0.132 39);
|
|
175
|
+
--accent-deep: oklch(from var(--accent) max(0.05, calc(l - 0.13)) c h);
|
|
176
|
+
--accent-foreground: oklch(1 0 0);
|
|
177
|
+
|
|
178
|
+
--muted: oklch(0.227 0 0);
|
|
179
|
+
--muted-foreground: oklch(0.683 0 0); /* #999 — Vercel gray-500 */
|
|
180
|
+
|
|
181
|
+
--border: oklch(0.28 0 0); /* #292929 */
|
|
182
|
+
--input: oklch(0.227 0 0);
|
|
183
|
+
--ring: oklch(0.628 0.225 296);
|
|
184
|
+
|
|
185
|
+
--success: oklch(0.814 0.192 155.7); /* #22E58C */
|
|
186
|
+
--success-foreground: oklch(0.146 0 0);
|
|
187
|
+
--warning: oklch(0.77 0.165 70.6); /* #F59E0B */
|
|
188
|
+
--warning-foreground: oklch(0.146 0 0);
|
|
189
|
+
--destructive: oklch(0.677 0.213 15.6); /* #FF4F6D */
|
|
190
|
+
--destructive-foreground: oklch(0.146 0 0);
|
|
191
|
+
--info: oklch(0.732 0.142 254.4); /* #5FB3FF */
|
|
192
|
+
--info-foreground: oklch(0.146 0 0);
|
|
193
|
+
|
|
194
|
+
/* Status (D4 — dark mode mirror of light defaults) ------------------ */
|
|
195
|
+
--status-online: oklch(0.814 0.192 155.7);
|
|
196
|
+
--status-online-foreground: oklch(0.146 0 0);
|
|
197
|
+
--status-offline: oklch(0.677 0.213 15.6);
|
|
198
|
+
--status-offline-foreground: oklch(0.146 0 0);
|
|
199
|
+
--status-degraded: oklch(0.77 0.165 70.6);
|
|
200
|
+
--status-degraded-foreground: oklch(0.146 0 0);
|
|
201
|
+
--status-info: oklch(0.732 0.142 254.4);
|
|
202
|
+
--status-info-foreground: oklch(0.146 0 0);
|
|
203
|
+
|
|
204
|
+
/* In dark mode, shadows are heavier ink (against the dark surface) and
|
|
205
|
+
* the glow brightens. Both still derive from theme tokens.
|
|
206
|
+
* Post-T2.5: OKLCH-aware composition via color-mix() — black ink via
|
|
207
|
+
* the literal oklch(0 0 0) (no theme indirection needed).
|
|
208
|
+
*/
|
|
209
|
+
--shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.4);
|
|
210
|
+
--shadow-md: 0 2px 8px -2px oklch(0 0 0 / 0.5), 0 1px 3px oklch(0 0 0 / 0.4);
|
|
211
|
+
--shadow-lg: 0 12px 32px -8px oklch(0 0 0 / 0.6), 0 4px 12px oklch(0 0 0 / 0.4);
|
|
212
|
+
--shadow-glow: 0 0 24px color-mix(in oklch, var(--primary-glow) 40%, transparent);
|
|
213
|
+
--shadow-glow-strong: 0 0 36px color-mix(in oklch, var(--primary-glow) 60%, transparent);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Forced colors (Windows High Contrast Mode) — D7 / ADR-0008 ----------- *
|
|
217
|
+
* Maps Theo's semantic tokens to system colors so the UI remains usable
|
|
218
|
+
* (and required by WCAG 1.4.1) when the OS forces a high-contrast palette.
|
|
219
|
+
* Decorative textures opt out via `forced-color-adjust: none` (the dotted
|
|
220
|
+
* patterns and hero glow would invert into solid blocks otherwise).
|
|
221
|
+
*
|
|
222
|
+
* Reference: MDN forced-colors media query, WCAG 2.2 SC 1.4.1.
|
|
223
|
+
*/
|
|
224
|
+
@media (forced-colors: active) {
|
|
225
|
+
:root,
|
|
226
|
+
.dark {
|
|
227
|
+
--background: Canvas;
|
|
228
|
+
--foreground: CanvasText;
|
|
229
|
+
--card: Canvas;
|
|
230
|
+
--card-foreground: CanvasText;
|
|
231
|
+
--popover: Canvas;
|
|
232
|
+
--popover-foreground: CanvasText;
|
|
233
|
+
--primary: Highlight;
|
|
234
|
+
--primary-foreground: HighlightText;
|
|
235
|
+
--secondary: ButtonFace;
|
|
236
|
+
--secondary-foreground: ButtonText;
|
|
237
|
+
--accent: Highlight;
|
|
238
|
+
--accent-foreground: HighlightText;
|
|
239
|
+
--muted: ButtonFace;
|
|
240
|
+
--muted-foreground: GrayText;
|
|
241
|
+
--border: ButtonBorder;
|
|
242
|
+
--input: ButtonBorder;
|
|
243
|
+
--ring: Highlight;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Texture utilities are decorative — keep them out of the system-color map. */
|
|
247
|
+
.bg-dotted-violet,
|
|
248
|
+
.bg-dotted-violet-strong,
|
|
249
|
+
.bg-hero-glow,
|
|
250
|
+
.bg-paper-grain {
|
|
251
|
+
forced-color-adjust: none;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Reduced motion — neutralizes durations and transitions for users who
|
|
256
|
+
* request prefers-reduced-motion: reduce. Components that use animation as
|
|
257
|
+
* semantic state (e.g. a spinner on a running step) can opt back in with
|
|
258
|
+
* Tailwind's `motion-safe:` prefix.
|
|
259
|
+
*
|
|
260
|
+
* Reference: WCAG 2.3.3 Animation from Interactions (Level AAA).
|
|
261
|
+
*/
|
|
262
|
+
@media (prefers-reduced-motion: reduce) {
|
|
263
|
+
:root {
|
|
264
|
+
--duration-fast: 0ms;
|
|
265
|
+
--duration-base: 0ms;
|
|
266
|
+
--duration-slow: 0ms;
|
|
267
|
+
--stagger: 0ms;
|
|
268
|
+
}
|
|
269
|
+
*,
|
|
270
|
+
*::before,
|
|
271
|
+
*::after {
|
|
272
|
+
animation-duration: 0.001ms !important;
|
|
273
|
+
animation-iteration-count: 1 !important;
|
|
274
|
+
transition-duration: 0.001ms !important;
|
|
275
|
+
scroll-behavior: auto !important;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Texture utilities (signature) ---------------------------------------- *
|
|
280
|
+
* Post-T2.5: tokens are OKLCH; we compose alpha via color-mix(in oklch, …),
|
|
281
|
+
* which is perceptually correct and works with any color-space variable.
|
|
282
|
+
*/
|
|
283
|
+
@layer utilities {
|
|
284
|
+
.bg-dotted-violet {
|
|
285
|
+
background-image: radial-gradient(
|
|
286
|
+
color-mix(in oklch, var(--primary) 8%, transparent) 1px,
|
|
287
|
+
transparent 1px
|
|
288
|
+
);
|
|
289
|
+
background-size: 20px 20px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.bg-dotted-violet-strong {
|
|
293
|
+
background-image: radial-gradient(
|
|
294
|
+
color-mix(in oklch, var(--primary) 16%, transparent) 1px,
|
|
295
|
+
transparent 1px
|
|
296
|
+
);
|
|
297
|
+
background-size: 20px 20px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.bg-hero-glow {
|
|
301
|
+
background-image: radial-gradient(
|
|
302
|
+
ellipse 60% 50% at 70% 0%,
|
|
303
|
+
color-mix(in oklch, var(--primary) 18%, transparent) 0%,
|
|
304
|
+
transparent 60%
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.bg-paper-grain {
|
|
309
|
+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.06 0 0 0 0 0.04 0 0 0 0 0.08 0 0 0 0.18 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.text-balance {
|
|
313
|
+
text-wrap: balance;
|
|
314
|
+
}
|
|
315
|
+
}
|