@usetheo/ui 0.4.0-next.0 → 0.5.1-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,124 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.1-next.0] - 2026-05-22
11
+
12
+ Patch — RFC 0008 follow-up. The 0.5.0-next.0 release declared `tailwindcss@^4`
13
+ as a peer dependency and shipped the `./vite-plugin` + `./preset` subpaths,
14
+ but the actual CSS / token / preset artifacts inside the tarball were still
15
+ Tailwind v3 internally. Result: every TheoKit consumer of 0.5.0-next.0 booted
16
+ with unstyled UI in dev and production — `bg-primary`, `text-muted-foreground`,
17
+ `border-border`, `text-body-sm`, etc. emitted as className strings with no
18
+ matching CSS rule.
19
+
20
+ This release rewrites the three v3-shaped artifacts to v4-native syntax and
21
+ ships a fixture-backed real-build dogfood so the regression cannot recur.
22
+
23
+ ### Changed
24
+
25
+ - **`dist/styles.css` is now Tailwind v4 native.** Uses `@import "tailwindcss"`
26
+ (replaces the v3 `@tailwind base; @tailwind components; @tailwind utilities;`
27
+ trio that Tailwind v4 emits as literal strings, with zero utility generation).
28
+ Imports `tokens.css` (runtime cascade) AND `tokens-v4.css` (`@theme` namespace)
29
+ so consumers' Tailwind v4 build resolves both layers correctly. Same
30
+ `@layer base` content (border-color, body font, focus ring, scrollbar
31
+ styling) as before. (#TBD)
32
+ - **`./preset` subpath is now a CSS file.** Tailwind v4 dropped the v3 JS
33
+ preset format — `theme.extend.colors.{name}` declarations are a no-op for
34
+ v4. The new `dist/preset.css` simply chains `@import "./tokens.css"` and
35
+ `@import "./tokens-v4.css"` so consumers can `@import "@usetheo/ui/preset.css"`
36
+ from their own Tailwind v4 entry CSS. (#TBD)
37
+
38
+ ### Added
39
+
40
+ - **`@usetheo/ui/tokens-v4.css` (NEW)** — `@theme {}` block declaring 28
41
+ `--color-*` aliases (full color set), 14 `--text-*` typescale tiers
42
+ (Violet Forge — `--text-display-2xl` through `--text-code-sm` with companion
43
+ `--*--line-height`, `--*--letter-spacing`, `--*--font-weight`), 3 `--font-*`
44
+ family tokens, 7 `--radius-*` tiers, 5 `--shadow-*` levels, 3 `--ease-*`
45
+ timings, and 2 `--animate-*` keyframe-bound utilities. Every color alias
46
+ uses `hsl(var(--*))` indirection so `<ThemeProvider>`'s runtime
47
+ `[data-theme]` cascade keeps working — switching themes still recolors
48
+ every utility. (#TBD)
49
+ - **`@usetheo/ui/styles-v3-legacy.css` (NEW)** — the previous v3-shaped
50
+ `@tailwind base/components/utilities` entry. Pinned consumers on
51
+ `tailwindcss@^3` who still want a prebuilt stylesheet can import this
52
+ subpath. New code SHOULD use `@usetheo/ui/styles.css` (v4) instead.
53
+ - **`@usetheo/ui/preset-v3-legacy` (NEW)** — the v3 JS `Partial<Config>`
54
+ preset that previously lived at `./preset`. Renamed so the canonical
55
+ `./preset` subpath can host the v4 CSS preset. v3 consumers update
56
+ imports from `@usetheo/ui/preset` to `@usetheo/ui/preset-v3-legacy`.
57
+ - **Dogfood scripts** — `pnpm dogfood:v4-zero-config` (shape check, runs in
58
+ `quality:gates`) and `pnpm dogfood:v4-real-build` (end-to-end: packs the
59
+ tarball, installs in a tmp project alongside `@tailwindcss/cli@^4`, runs
60
+ Tailwind v4 against `tests/fixtures/v4-zero-config/`, and grep-asserts the
61
+ expected utility classes appear in the emitted CSS — 12 assertions). The
62
+ real-build dogfood is opt-in (slow, requires network) but catches any
63
+ future regression where v3-shaped artifacts get shipped under a v4 peer
64
+ declaration. (#TBD)
65
+
66
+ ### Notes
67
+
68
+ - **Breaking for any 0.5.0-next.0 consumer.** The `./preset` subpath changed
69
+ from JS (`Partial<Config>` default-export) to CSS file. Code importing
70
+ `import preset from "@usetheo/ui/preset"` will break and must migrate to
71
+ `@import "@usetheo/ui/preset.css"` in an entry CSS. Blast radius: TheoKit
72
+ (already reverted away from 0.5.x by the time this fix shipped) plus any
73
+ community consumer that adopted 0.5.0-next.0 in the same day — likely zero.
74
+ - The runtime indirection via `hsl(var(--*))` aliases keeps `<ThemeProvider>`
75
+ and every built-in theme (`violet-forge`, `dracula`, `vercel-mono`, etc.)
76
+ working with zero changes — the v4 utilities transparently follow the v3
77
+ cascade.
78
+
79
+ ## [0.5.0-next.0] - 2026-05-22
80
+
81
+ Minor bump — public API gains two subpath exports (`./vite-plugin` and
82
+ `./preset`) so the TheoKit framework's `integrateUseTheoUI()` can
83
+ auto-wire Tailwind v4 for consumers with zero further configuration.
84
+ Zero visual break and no runtime behavior change for existing consumers.
85
+
86
+ ### Added
87
+
88
+ - **`@usetheo/ui/vite-plugin` (NEW, RFC 0008)** — Default-export factory
89
+ returning one Vite `Plugin`. The plugin's `config()` hook
90
+ dynamic-imports `@tailwindcss/vite` v4 and chains it into the
91
+ consumer's plugin array when resolvable, and degrades to `console.warn`
92
+ + CSS-only mode (via the pre-built `@usetheo/ui/styles.css` subpath)
93
+ when the peer is not installed. A virtual module
94
+ `virtual:@usetheo/ui/library-sources.css` provides the `@source`
95
+ directive covering `node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}`
96
+ so Tailwind scans the library's published JS for utilities. Plugin
97
+ name slug: `@usetheo/ui/vite-plugin`. Options: `tailwind?: boolean`
98
+ (default `true`), `contentExtra?: string[]` (extra `@source` globs).
99
+ (#TBD)
100
+ - **`@usetheo/ui/preset` (NEW, RFC 0008)** — Default-export Tailwind v4
101
+ `Partial<Config>` mirroring the design tokens in `tokens.css`
102
+ (colors via `hsl(var(--x) / <alpha-value>)`, font families, the
103
+ Violet Forge typescale, radii, shadows, animations, motion timing)
104
+ with `content` paths covering `./node_modules/@usetheo/ui/dist/**` and
105
+ the `tailwindcss-animate` plugin. Consumer usage:
106
+ `import preset from "@usetheo/ui/preset"; export default { presets: [preset] }`.
107
+ Internally delegates to the existing `src/styles/tailwind-preset.ts` —
108
+ the v3 shadcn-registry preset and the v4 import preset stay
109
+ byte-for-byte aligned and impossible to drift. (#TBD)
110
+ - **`@tailwindcss/vite ^4`, `tailwindcss ^4`, `vite ^6 || ^7` peer-deps
111
+ (all optional)** — added to `peerDependenciesMeta` so consumers
112
+ importing `@usetheo/ui` standalone (no framework) are not forced into
113
+ Tailwind v4. Required only when consuming via TheoKit's auto-wire path
114
+ or the new `./vite-plugin` subpath. (#TBD)
115
+
116
+ ### Notes
117
+
118
+ - Existing `tailwindcss@^3` consumers continue to work via the shadcn
119
+ registry preset (`registry/r/tailwind-preset.json`) and the prebuilt
120
+ `@usetheo/ui/styles.css`. The new subpaths are additive — they do not
121
+ break v3-based setups.
122
+ - The `vite-plugin` returns ONE `Plugin` object (not `Plugin[]`) per the
123
+ cross-repo contract with TheoKit's `integrateUseTheoUI()`. The chain
124
+ to `@tailwindcss/vite` happens via the `config()` hook's `plugins`
125
+ field — Vite 5+ tightened the TypeScript signature, the runtime still
126
+ merges plugins as expected.
127
+
10
128
  ## [0.4.0-next.0] - 2026-05-22
11
129
 
12
130
  Minor bump — public API gains 7 new theme exports. Zero visual break for
@@ -0,0 +1,35 @@
1
+ import { Config } from 'tailwindcss';
2
+
3
+ /**
4
+ * `@usetheo/ui/preset-v3-legacy` — Tailwind v3 JS preset (legacy path).
5
+ *
6
+ * Default-export a `Partial<Config>` mirroring the design tokens shipped
7
+ * in `@usetheo/ui/tokens.css`. Adds a `content` field covering the
8
+ * library's published artifact tree so v3-based Tailwind builds emit
9
+ * the utilities used by `@usetheo/ui` components.
10
+ *
11
+ * **Tailwind v4 consumers MUST use `@usetheo/ui/preset.css`**, not this
12
+ * file — Tailwind v4 dropped the JS preset format entirely. This file
13
+ * exists for any remaining `tailwindcss@^3` consumer (notably the
14
+ * shadcn-style copy-paste registry path); new code should not import
15
+ * from this subpath.
16
+ *
17
+ * The token surface is delegated to `./styles/tailwind-preset.ts` (the
18
+ * existing source of truth used by the local Ladle dev surface and the
19
+ * shadcn registry).
20
+ *
21
+ * v3 consumer usage:
22
+ *
23
+ * // tailwind.config.ts (Tailwind v3 only)
24
+ * import preset from "@usetheo/ui/preset-v3-legacy";
25
+ * export default {
26
+ * presets: [preset],
27
+ * content: ["./app/**\/*.{ts,tsx}"],
28
+ * };
29
+ *
30
+ * See RFC 0008 follow-up.
31
+ */
32
+
33
+ declare const preset: Partial<Config>;
34
+
35
+ export { preset as default };
@@ -0,0 +1,159 @@
1
+ import animate from 'tailwindcss-animate';
2
+
3
+ // src/styles/tailwind-preset.ts
4
+ var hsl = (token) => `hsl(var(${token}) / <alpha-value>)`;
5
+ var theoUIPreset = {
6
+ theme: {
7
+ container: {
8
+ center: true,
9
+ padding: "1rem",
10
+ screens: {
11
+ "2xl": "1280px"
12
+ }
13
+ },
14
+ extend: {
15
+ colors: {
16
+ background: hsl("--background"),
17
+ foreground: hsl("--foreground"),
18
+ card: {
19
+ DEFAULT: hsl("--card"),
20
+ foreground: hsl("--card-foreground")
21
+ },
22
+ popover: {
23
+ DEFAULT: hsl("--popover"),
24
+ foreground: hsl("--popover-foreground")
25
+ },
26
+ primary: {
27
+ DEFAULT: hsl("--primary"),
28
+ deep: hsl("--primary-deep"),
29
+ glow: hsl("--primary-glow"),
30
+ foreground: hsl("--primary-foreground")
31
+ },
32
+ secondary: {
33
+ DEFAULT: hsl("--secondary"),
34
+ foreground: hsl("--secondary-foreground")
35
+ },
36
+ accent: {
37
+ DEFAULT: hsl("--accent"),
38
+ deep: hsl("--accent-deep"),
39
+ foreground: hsl("--accent-foreground")
40
+ },
41
+ muted: {
42
+ DEFAULT: hsl("--muted"),
43
+ foreground: hsl("--muted-foreground")
44
+ },
45
+ success: {
46
+ DEFAULT: hsl("--success"),
47
+ foreground: hsl("--success-foreground")
48
+ },
49
+ warning: {
50
+ DEFAULT: hsl("--warning"),
51
+ foreground: hsl("--warning-foreground")
52
+ },
53
+ destructive: {
54
+ DEFAULT: hsl("--destructive"),
55
+ foreground: hsl("--destructive-foreground")
56
+ },
57
+ info: {
58
+ DEFAULT: hsl("--info"),
59
+ foreground: hsl("--info-foreground")
60
+ },
61
+ border: hsl("--border"),
62
+ input: hsl("--input"),
63
+ ring: hsl("--ring")
64
+ },
65
+ fontFamily: {
66
+ display: "var(--font-display)",
67
+ sans: "var(--font-body)",
68
+ mono: "var(--font-mono)"
69
+ },
70
+ /* Geist-inspired Violet Forge typescale.
71
+ *
72
+ * Three strict weights: 400 (body), 500 (UI), 600 (display/headings).
73
+ * Letter-spacing scales with size — aggressive negative on display.
74
+ * Mirrors the Vercel/Geist vocabulary while keeping Theo's identity.
75
+ */
76
+ fontSize: {
77
+ // Display tier — aggressive compression, content-led headlines
78
+ "display-2xl": ["64px", { lineHeight: "1", letterSpacing: "-0.0464em", fontWeight: "600" }],
79
+ "display-xl": ["48px", { lineHeight: "1.05", letterSpacing: "-0.05em", fontWeight: "600" }],
80
+ "display-lg": ["40px", { lineHeight: "1.1", letterSpacing: "-0.05em", fontWeight: "600" }],
81
+ "display-md": ["32px", { lineHeight: "1.2", letterSpacing: "-0.04em", fontWeight: "600" }],
82
+ headline: ["28px", { lineHeight: "1.25", letterSpacing: "-0.035em", fontWeight: "600" }],
83
+ // Title tier — section / card heads
84
+ "title-lg": ["24px", { lineHeight: "1.33", letterSpacing: "-0.04em", fontWeight: "600" }],
85
+ "title-md": ["20px", { lineHeight: "1.4", letterSpacing: "-0.03em", fontWeight: "600" }],
86
+ // Body tier — FAANG-density realignment 2026-05-22: body-md is the
87
+ // industry-standard 14px (shadcn / Vercel Geist / Linear / Stripe /
88
+ // Mantine). The previous 15px was idiosyncratic. body-sm (14px label
89
+ // weight) remains separate via its line-height / weight signature.
90
+ "body-lg": ["18px", { lineHeight: "1.56", letterSpacing: "-0.01em", fontWeight: "400" }],
91
+ "body-md": ["14px", { lineHeight: "1.43", letterSpacing: "0", fontWeight: "400" }],
92
+ "body-sm": ["13px", { lineHeight: "1.46", fontWeight: "400" }],
93
+ // Label tier — used on buttons, nav, secondary actions
94
+ label: ["14px", { lineHeight: "1.43", fontWeight: "500" }],
95
+ "label-caps": ["12px", { lineHeight: "1.33", letterSpacing: "0.04em", fontWeight: "500" }],
96
+ // Mono — code surfaces, technical labels
97
+ "code-md": ["14px", { lineHeight: "1.5", fontWeight: "400" }],
98
+ "code-sm": ["13px", { lineHeight: "1.54", fontWeight: "500" }]
99
+ },
100
+ borderRadius: {
101
+ none: "var(--radius-none)",
102
+ sm: "var(--radius-sm)",
103
+ md: "var(--radius-md)",
104
+ lg: "var(--radius-lg)",
105
+ xl: "var(--radius-xl)",
106
+ "2xl": "var(--radius-2xl)",
107
+ full: "var(--radius-full)"
108
+ },
109
+ boxShadow: {
110
+ sm: "var(--shadow-sm)",
111
+ md: "var(--shadow-md)",
112
+ lg: "var(--shadow-lg)",
113
+ glow: "var(--shadow-glow)",
114
+ "glow-strong": "var(--shadow-glow-strong)"
115
+ },
116
+ transitionTimingFunction: {
117
+ "out-soft": "var(--ease-out-soft)",
118
+ snap: "var(--ease-snap)"
119
+ },
120
+ transitionDuration: {
121
+ fast: "var(--duration-fast)",
122
+ base: "var(--duration-base)",
123
+ slow: "var(--duration-slow)"
124
+ },
125
+ keyframes: {
126
+ "fade-in-up": {
127
+ "0%": { opacity: "0", transform: "translateY(8px)" },
128
+ "100%": { opacity: "1", transform: "translateY(0)" }
129
+ },
130
+ "pulse-glow": {
131
+ "0%, 100%": { boxShadow: "0 0 0 0 hsl(var(--primary) / 0.5)" },
132
+ "50%": { boxShadow: "0 0 0 8px hsl(var(--primary) / 0)" }
133
+ }
134
+ },
135
+ animation: {
136
+ "fade-in-up": "fade-in-up var(--duration-base) var(--ease-out-soft) both",
137
+ "pulse-glow": "pulse-glow 1.5s var(--ease-in-out) infinite"
138
+ }
139
+ }
140
+ },
141
+ plugins: [animate]
142
+ };
143
+
144
+ // src/preset-v3-legacy.ts
145
+ var LIBRARY_CONTENT_GLOBS = [
146
+ // Resolved relative to the consumer's `tailwind.config.{ts,js}`.
147
+ "./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}",
148
+ // Yarn PnP / pnpm hoist fallback — Tailwind's globbing tolerates both.
149
+ "./node_modules/@usetheo/ui/dist/**/*.{ts,tsx}"
150
+ ];
151
+ var preset = {
152
+ ...theoUIPreset,
153
+ content: LIBRARY_CONTENT_GLOBS
154
+ };
155
+ var preset_v3_legacy_default = preset;
156
+
157
+ export { preset_v3_legacy_default as default };
158
+ //# sourceMappingURL=preset-v3-legacy.js.map
159
+ //# sourceMappingURL=preset-v3-legacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/styles/tailwind-preset.ts","../src/preset-v3-legacy.ts"],"names":[],"mappings":"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","file":"preset-v3-legacy.js","sourcesContent":["/**\n * Theo UI Tailwind preset — Violet Forge identity.\n *\n * Single source of truth for the design system's utility-level tokens:\n * - colors mapped to CSS variables (HSL split via `hsl(var(--x) / <alpha>)`)\n * - Geist-inspired typescale (display / title / body / label / code tiers)\n * - radii, shadows, motion timing & duration, keyframes\n * - tailwindcss-animate plugin\n *\n * Consumed by:\n * - `tailwind.config.ts` (this repo) via `presets: [theoUIPreset]`\n * - registry/r/tailwind-preset.json (shipped to consumers via shadcn CLI)\n *\n * Consumers integrate as:\n *\n * import type { Config } from \"tailwindcss\";\n * import { theoUIPreset } from \"./styles/tailwind-preset\";\n *\n * export default {\n * darkMode: \"class\",\n * content: [\"./src/**\\/*.{ts,tsx}\"],\n * presets: [theoUIPreset],\n * } satisfies Config;\n *\n * Note: `darkMode` and `content` are NOT in the preset — they are consumer\n * decisions. The preset only contains `theme.extend.*` and `plugins`.\n */\n\nimport type { Config } from \"tailwindcss\";\nimport animate from \"tailwindcss-animate\";\n\nconst hsl = (token: string) => `hsl(var(${token}) / <alpha-value>)`;\n\nexport const theoUIPreset: Partial<Config> = {\n theme: {\n container: {\n center: true,\n padding: \"1rem\",\n screens: {\n \"2xl\": \"1280px\",\n },\n },\n extend: {\n colors: {\n background: hsl(\"--background\"),\n foreground: hsl(\"--foreground\"),\n card: {\n DEFAULT: hsl(\"--card\"),\n foreground: hsl(\"--card-foreground\"),\n },\n popover: {\n DEFAULT: hsl(\"--popover\"),\n foreground: hsl(\"--popover-foreground\"),\n },\n primary: {\n DEFAULT: hsl(\"--primary\"),\n deep: hsl(\"--primary-deep\"),\n glow: hsl(\"--primary-glow\"),\n foreground: hsl(\"--primary-foreground\"),\n },\n secondary: {\n DEFAULT: hsl(\"--secondary\"),\n foreground: hsl(\"--secondary-foreground\"),\n },\n accent: {\n DEFAULT: hsl(\"--accent\"),\n deep: hsl(\"--accent-deep\"),\n foreground: hsl(\"--accent-foreground\"),\n },\n muted: {\n DEFAULT: hsl(\"--muted\"),\n foreground: hsl(\"--muted-foreground\"),\n },\n success: {\n DEFAULT: hsl(\"--success\"),\n foreground: hsl(\"--success-foreground\"),\n },\n warning: {\n DEFAULT: hsl(\"--warning\"),\n foreground: hsl(\"--warning-foreground\"),\n },\n destructive: {\n DEFAULT: hsl(\"--destructive\"),\n foreground: hsl(\"--destructive-foreground\"),\n },\n info: {\n DEFAULT: hsl(\"--info\"),\n foreground: hsl(\"--info-foreground\"),\n },\n border: hsl(\"--border\"),\n input: hsl(\"--input\"),\n ring: hsl(\"--ring\"),\n },\n fontFamily: {\n display: \"var(--font-display)\",\n sans: \"var(--font-body)\",\n mono: \"var(--font-mono)\",\n },\n /* Geist-inspired Violet Forge typescale.\n *\n * Three strict weights: 400 (body), 500 (UI), 600 (display/headings).\n * Letter-spacing scales with size — aggressive negative on display.\n * Mirrors the Vercel/Geist vocabulary while keeping Theo's identity.\n */\n fontSize: {\n // Display tier — aggressive compression, content-led headlines\n \"display-2xl\": [\"64px\", { lineHeight: \"1\", letterSpacing: \"-0.0464em\", fontWeight: \"600\" }],\n \"display-xl\": [\"48px\", { lineHeight: \"1.05\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-lg\": [\"40px\", { lineHeight: \"1.1\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-md\": [\"32px\", { lineHeight: \"1.2\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n headline: [\"28px\", { lineHeight: \"1.25\", letterSpacing: \"-0.035em\", fontWeight: \"600\" }],\n // Title tier — section / card heads\n \"title-lg\": [\"24px\", { lineHeight: \"1.33\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n \"title-md\": [\"20px\", { lineHeight: \"1.4\", letterSpacing: \"-0.03em\", fontWeight: \"600\" }],\n // Body tier — FAANG-density realignment 2026-05-22: body-md is the\n // industry-standard 14px (shadcn / Vercel Geist / Linear / Stripe /\n // Mantine). The previous 15px was idiosyncratic. body-sm (14px label\n // weight) remains separate via its line-height / weight signature.\n \"body-lg\": [\"18px\", { lineHeight: \"1.56\", letterSpacing: \"-0.01em\", fontWeight: \"400\" }],\n \"body-md\": [\"14px\", { lineHeight: \"1.43\", letterSpacing: \"0\", fontWeight: \"400\" }],\n \"body-sm\": [\"13px\", { lineHeight: \"1.46\", fontWeight: \"400\" }],\n // Label tier — used on buttons, nav, secondary actions\n label: [\"14px\", { lineHeight: \"1.43\", fontWeight: \"500\" }],\n \"label-caps\": [\"12px\", { lineHeight: \"1.33\", letterSpacing: \"0.04em\", fontWeight: \"500\" }],\n // Mono — code surfaces, technical labels\n \"code-md\": [\"14px\", { lineHeight: \"1.5\", fontWeight: \"400\" }],\n \"code-sm\": [\"13px\", { lineHeight: \"1.54\", fontWeight: \"500\" }],\n },\n borderRadius: {\n none: \"var(--radius-none)\",\n sm: \"var(--radius-sm)\",\n md: \"var(--radius-md)\",\n lg: \"var(--radius-lg)\",\n xl: \"var(--radius-xl)\",\n \"2xl\": \"var(--radius-2xl)\",\n full: \"var(--radius-full)\",\n },\n boxShadow: {\n sm: \"var(--shadow-sm)\",\n md: \"var(--shadow-md)\",\n lg: \"var(--shadow-lg)\",\n glow: \"var(--shadow-glow)\",\n \"glow-strong\": \"var(--shadow-glow-strong)\",\n },\n transitionTimingFunction: {\n \"out-soft\": \"var(--ease-out-soft)\",\n snap: \"var(--ease-snap)\",\n },\n transitionDuration: {\n fast: \"var(--duration-fast)\",\n base: \"var(--duration-base)\",\n slow: \"var(--duration-slow)\",\n },\n keyframes: {\n \"fade-in-up\": {\n \"0%\": { opacity: \"0\", transform: \"translateY(8px)\" },\n \"100%\": { opacity: \"1\", transform: \"translateY(0)\" },\n },\n \"pulse-glow\": {\n \"0%, 100%\": { boxShadow: \"0 0 0 0 hsl(var(--primary) / 0.5)\" },\n \"50%\": { boxShadow: \"0 0 0 8px hsl(var(--primary) / 0)\" },\n },\n },\n animation: {\n \"fade-in-up\": \"fade-in-up var(--duration-base) var(--ease-out-soft) both\",\n \"pulse-glow\": \"pulse-glow 1.5s var(--ease-in-out) infinite\",\n },\n },\n },\n plugins: [animate],\n};\n","/**\n * `@usetheo/ui/preset-v3-legacy` — Tailwind v3 JS preset (legacy path).\n *\n * Default-export a `Partial<Config>` mirroring the design tokens shipped\n * in `@usetheo/ui/tokens.css`. Adds a `content` field covering the\n * library's published artifact tree so v3-based Tailwind builds emit\n * the utilities used by `@usetheo/ui` components.\n *\n * **Tailwind v4 consumers MUST use `@usetheo/ui/preset.css`**, not this\n * file — Tailwind v4 dropped the JS preset format entirely. This file\n * exists for any remaining `tailwindcss@^3` consumer (notably the\n * shadcn-style copy-paste registry path); new code should not import\n * from this subpath.\n *\n * The token surface is delegated to `./styles/tailwind-preset.ts` (the\n * existing source of truth used by the local Ladle dev surface and the\n * shadcn registry).\n *\n * v3 consumer usage:\n *\n * // tailwind.config.ts (Tailwind v3 only)\n * import preset from \"@usetheo/ui/preset-v3-legacy\";\n * export default {\n * presets: [preset],\n * content: [\"./app/**\\/*.{ts,tsx}\"],\n * };\n *\n * See RFC 0008 follow-up.\n */\nimport type { Config } from \"tailwindcss\";\nimport { theoUIPreset } from \"./styles/tailwind-preset.js\";\n\nconst LIBRARY_CONTENT_GLOBS: string[] = [\n // Resolved relative to the consumer's `tailwind.config.{ts,js}`.\n \"./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}\",\n // Yarn PnP / pnpm hoist fallback — Tailwind's globbing tolerates both.\n \"./node_modules/@usetheo/ui/dist/**/*.{ts,tsx}\",\n];\n\nconst preset: Partial<Config> = {\n ...theoUIPreset,\n content: LIBRARY_CONTENT_GLOBS,\n};\n\nexport default preset;\n"]}
@@ -0,0 +1,27 @@
1
+ /* Theo UI — Tailwind v4 CSS preset.
2
+ *
3
+ * For consumers running their own Tailwind v4 build (TheoKit framework
4
+ * path, custom Vite/Astro/Next apps with @tailwindcss/vite, etc.). The
5
+ * preset pulls in:
6
+ * 1. `tokens.css` — runtime `--*` vars `<ThemeProvider>` mutates per
7
+ * theme cascade.
8
+ * 2. `tokens-v4.css` — the `@theme { --color-*, --font-*, --text-*,
9
+ * --radius-*, --shadow-*, --ease-*, --animate-* }` namespace
10
+ * Tailwind v4 actually reads to generate utilities.
11
+ *
12
+ * Consumer usage (their entry CSS):
13
+ *
14
+ * @import "tailwindcss";
15
+ * @import "@usetheo/ui/preset.css";
16
+ *
17
+ * /* the consumer's own @theme overrides come AFTER, so they win. * /
18
+ *
19
+ * Tailwind v4 dropped the v3 JS preset format. The previous JS preset
20
+ * remains shipped under `@usetheo/ui/preset-v3-legacy` (which works only
21
+ * with `tailwindcss@^3`). New v4 consumers MUST use this CSS preset.
22
+ *
23
+ * See RFC 0008 follow-up.
24
+ */
25
+
26
+ @import "./tokens.css";
27
+ @import "./tokens-v4.css";
@@ -0,0 +1,88 @@
1
+ @import "./fonts.css";
2
+ @import "./tokens.css";
3
+
4
+ @tailwind base;
5
+ @tailwind components;
6
+ @tailwind utilities;
7
+
8
+ @layer base {
9
+ * {
10
+ border-color: hsl(var(--border));
11
+ }
12
+
13
+ html {
14
+ font-family: var(--font-body);
15
+ -webkit-font-smoothing: antialiased;
16
+ -moz-osx-font-smoothing: grayscale;
17
+ text-rendering: optimizeLegibility;
18
+ }
19
+
20
+ body {
21
+ background-color: hsl(var(--background));
22
+ color: hsl(var(--foreground));
23
+ /* OpenType ligatures globally — Geist's "liga" feature is structural
24
+ * and gives the Vercel-tight glyph combinations. */
25
+ font-feature-settings: "liga" 1, "ss01" 1;
26
+ }
27
+
28
+ /* Display headings inherit theme display font (Geist by default).
29
+ * Per-size tracking is set in tailwind.config.ts; this is the safety net. */
30
+ h1,
31
+ h2,
32
+ h3,
33
+ h4 {
34
+ font-family: var(--font-display);
35
+ font-weight: 600;
36
+ }
37
+
38
+ /* Code surfaces: use the mono token + tabular numerals so columns of
39
+ * numbers line up in tables/logs/metrics. */
40
+ code,
41
+ pre,
42
+ kbd,
43
+ samp {
44
+ font-family: var(--font-mono);
45
+ font-feature-settings: "tnum" 1, "liga" 1;
46
+ }
47
+
48
+ /* Focus ring — violet (signature) */
49
+ :focus-visible {
50
+ outline: 2px solid hsl(var(--ring));
51
+ outline-offset: 2px;
52
+ }
53
+
54
+ /* Selection — soft violet */
55
+ ::selection {
56
+ background-color: hsl(var(--primary) / 0.25);
57
+ color: hsl(var(--foreground));
58
+ }
59
+
60
+ /* Native scrollbar fallback (Violet Forge).
61
+ * Used for native browser scroll on elements that don't wrap in <ScrollArea>.
62
+ * Components that DO use <ScrollArea> hide the native scrollbar via Radix.
63
+ */
64
+ *::-webkit-scrollbar {
65
+ width: 10px;
66
+ height: 10px;
67
+ }
68
+ *::-webkit-scrollbar-track {
69
+ background: transparent;
70
+ }
71
+ *::-webkit-scrollbar-thumb {
72
+ background-color: hsl(var(--primary) / 0.25);
73
+ border: 2px solid transparent;
74
+ background-clip: content-box;
75
+ border-radius: 9999px;
76
+ transition: background-color var(--duration-fast) var(--ease-out-soft);
77
+ }
78
+ *::-webkit-scrollbar-thumb:hover {
79
+ background-color: hsl(var(--primary) / 0.5);
80
+ }
81
+ *::-webkit-scrollbar-thumb:active {
82
+ background-color: hsl(var(--primary) / 0.75);
83
+ }
84
+ * {
85
+ scrollbar-width: thin;
86
+ scrollbar-color: hsl(var(--primary) / 0.3) transparent;
87
+ }
88
+ }
package/dist/styles.css CHANGED
@@ -1,9 +1,26 @@
1
+ /* Theo UI — Tailwind v4 prebuilt entry stylesheet.
2
+ *
3
+ * What ships at `@usetheo/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 `@usetheo/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";
1
21
  @import "./fonts.css";
2
22
  @import "./tokens.css";
3
-
4
- @tailwind base;
5
- @tailwind components;
6
- @tailwind utilities;
23
+ @import "./tokens-v4.css";
7
24
 
8
25
  @layer base {
9
26
  * {
@@ -26,7 +43,7 @@
26
43
  }
27
44
 
28
45
  /* Display headings inherit theme display font (Geist by default).
29
- * Per-size tracking is set in tailwind.config.ts; this is the safety net. */
46
+ * Per-size tracking is set in tokens-v4.css; this is the safety net. */
30
47
  h1,
31
48
  h2,
32
49
  h3,
@@ -0,0 +1,187 @@
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: hsl(var(--primary))`) preserves the
18
+ * runtime theme cascade — browsers resolve `var(--color-primary)` →
19
+ * `hsl(var(--primary))` → current `[data-theme]` value at paint time.
20
+ *
21
+ * See RFC 0008 follow-up.
22
+ */
23
+
24
+ @theme {
25
+ /* Color tokens ------------------------------------------------------ */
26
+ --color-background: hsl(var(--background));
27
+ --color-foreground: hsl(var(--foreground));
28
+
29
+ --color-card: hsl(var(--card));
30
+ --color-card-foreground: hsl(var(--card-foreground));
31
+
32
+ --color-popover: hsl(var(--popover));
33
+ --color-popover-foreground: hsl(var(--popover-foreground));
34
+
35
+ --color-primary: hsl(var(--primary));
36
+ --color-primary-deep: hsl(var(--primary-deep));
37
+ --color-primary-glow: hsl(var(--primary-glow));
38
+ --color-primary-foreground: hsl(var(--primary-foreground));
39
+
40
+ --color-secondary: hsl(var(--secondary));
41
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
42
+
43
+ --color-accent: hsl(var(--accent));
44
+ --color-accent-deep: hsl(var(--accent-deep));
45
+ --color-accent-foreground: hsl(var(--accent-foreground));
46
+
47
+ --color-muted: hsl(var(--muted));
48
+ --color-muted-foreground: hsl(var(--muted-foreground));
49
+
50
+ --color-border: hsl(var(--border));
51
+ --color-input: hsl(var(--input));
52
+ --color-ring: hsl(var(--ring));
53
+
54
+ --color-success: hsl(var(--success));
55
+ --color-success-foreground: hsl(var(--success-foreground));
56
+ --color-warning: hsl(var(--warning));
57
+ --color-warning-foreground: hsl(var(--warning-foreground));
58
+ --color-destructive: hsl(var(--destructive));
59
+ --color-destructive-foreground: hsl(var(--destructive-foreground));
60
+ --color-info: hsl(var(--info));
61
+ --color-info-foreground: hsl(var(--info-foreground));
62
+
63
+ /* Fonts ------------------------------------------------------------- */
64
+ --font-display: var(--font-display);
65
+ --font-sans: var(--font-body);
66
+ --font-mono: var(--font-mono);
67
+
68
+ /* Violet Forge typescale -------------------------------------------- *
69
+ * Compound text utilities: each `--text-{name}` declares the size,
70
+ * with companion `--*--line-height`, `--*--letter-spacing`, and
71
+ * `--*--font-weight` properties Tailwind v4 picks up automatically.
72
+ */
73
+ --text-display-2xl: 64px;
74
+ --text-display-2xl--line-height: 1;
75
+ --text-display-2xl--letter-spacing: -0.0464em;
76
+ --text-display-2xl--font-weight: 600;
77
+
78
+ --text-display-xl: 48px;
79
+ --text-display-xl--line-height: 1.05;
80
+ --text-display-xl--letter-spacing: -0.05em;
81
+ --text-display-xl--font-weight: 600;
82
+
83
+ --text-display-lg: 40px;
84
+ --text-display-lg--line-height: 1.1;
85
+ --text-display-lg--letter-spacing: -0.05em;
86
+ --text-display-lg--font-weight: 600;
87
+
88
+ --text-display-md: 32px;
89
+ --text-display-md--line-height: 1.2;
90
+ --text-display-md--letter-spacing: -0.04em;
91
+ --text-display-md--font-weight: 600;
92
+
93
+ --text-headline: 28px;
94
+ --text-headline--line-height: 1.25;
95
+ --text-headline--letter-spacing: -0.035em;
96
+ --text-headline--font-weight: 600;
97
+
98
+ --text-title-lg: 24px;
99
+ --text-title-lg--line-height: 1.33;
100
+ --text-title-lg--letter-spacing: -0.04em;
101
+ --text-title-lg--font-weight: 600;
102
+
103
+ --text-title-md: 20px;
104
+ --text-title-md--line-height: 1.4;
105
+ --text-title-md--letter-spacing: -0.03em;
106
+ --text-title-md--font-weight: 600;
107
+
108
+ --text-body-lg: 18px;
109
+ --text-body-lg--line-height: 1.56;
110
+ --text-body-lg--letter-spacing: -0.01em;
111
+ --text-body-lg--font-weight: 400;
112
+
113
+ --text-body-md: 14px;
114
+ --text-body-md--line-height: 1.43;
115
+ --text-body-md--letter-spacing: 0;
116
+ --text-body-md--font-weight: 400;
117
+
118
+ --text-body-sm: 13px;
119
+ --text-body-sm--line-height: 1.46;
120
+ --text-body-sm--font-weight: 400;
121
+
122
+ --text-label: 14px;
123
+ --text-label--line-height: 1.43;
124
+ --text-label--font-weight: 500;
125
+
126
+ --text-label-caps: 12px;
127
+ --text-label-caps--line-height: 1.33;
128
+ --text-label-caps--letter-spacing: 0.04em;
129
+ --text-label-caps--font-weight: 500;
130
+
131
+ --text-code-md: 14px;
132
+ --text-code-md--line-height: 1.5;
133
+ --text-code-md--font-weight: 400;
134
+
135
+ --text-code-sm: 13px;
136
+ --text-code-sm--line-height: 1.54;
137
+ --text-code-sm--font-weight: 500;
138
+
139
+ /* Radii — Tailwind v4 reads the value directly (no var indirection). */
140
+ --radius-none: 0px;
141
+ --radius-sm: 4px;
142
+ --radius-md: 6px;
143
+ --radius-lg: 10px;
144
+ --radius-xl: 14px;
145
+ --radius-2xl: 20px;
146
+ --radius-full: 9999px;
147
+
148
+ /* Shadows — alias to tokens.css computed values (which already use
149
+ * `hsl(var(--foreground) / …)` so theme-switch keeps them in step). */
150
+ --shadow-sm: var(--shadow-sm);
151
+ --shadow-md: var(--shadow-md);
152
+ --shadow-lg: var(--shadow-lg);
153
+ --shadow-glow: var(--shadow-glow);
154
+ --shadow-glow-strong: var(--shadow-glow-strong);
155
+
156
+ /* Motion ------------------------------------------------------------ */
157
+ --ease-out-soft: var(--ease-out-soft);
158
+ --ease-snap: var(--ease-snap);
159
+
160
+ /* Animations — declare so `animate-fade-in-up` and `animate-pulse-glow`
161
+ * resolve. Keyframes are declared below (outside @theme — Tailwind v4
162
+ * picks them up via the @keyframes namespace).
163
+ */
164
+ --animate-fade-in-up: fade-in-up var(--duration-base) var(--ease-out-soft) both;
165
+ --animate-pulse-glow: pulse-glow 1.5s var(--ease-in-out) infinite;
166
+ }
167
+
168
+ @keyframes fade-in-up {
169
+ 0% {
170
+ opacity: 0;
171
+ transform: translateY(8px);
172
+ }
173
+ 100% {
174
+ opacity: 1;
175
+ transform: translateY(0);
176
+ }
177
+ }
178
+
179
+ @keyframes pulse-glow {
180
+ 0%,
181
+ 100% {
182
+ box-shadow: 0 0 0 0 hsl(var(--primary) / 0.5);
183
+ }
184
+ 50% {
185
+ box-shadow: 0 0 0 8px hsl(var(--primary) / 0);
186
+ }
187
+ }
@@ -0,0 +1,29 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * `@usetheo/ui/vite-plugin` — auto-wire Tailwind v4 for consumers.
5
+ *
6
+ * Default export is a factory returning ONE Vite Plugin. Internally:
7
+ * 1. chains `@tailwindcss/vite` v4 via the `config()` hook when resolvable
8
+ * and `opts.tailwind !== false`,
9
+ * 2. exposes a virtual module `virtual:@usetheo/ui/library-sources.css`
10
+ * whose contents register `@source` directives covering the library's
11
+ * published files, so consumer-side Tailwind scans `@usetheo/ui` JSX
12
+ * and emits its utility classes,
13
+ * 3. degrades gracefully via `console.warn` (never throws) when
14
+ * `@tailwindcss/vite` is not installed — the surface stays usable in
15
+ * CSS-only mode via the pre-built `@usetheo/ui/styles.css` subpath.
16
+ *
17
+ * This is the cross-repo contract TheoKit's `integrateUseTheoUI()` probes
18
+ * for. See `docs/rfcs/0008-vite-plugin-and-preset.md`.
19
+ */
20
+
21
+ interface UseTheoUIPluginOptions {
22
+ /** Disable Tailwind v4 auto-chain. Default: `true`. */
23
+ tailwind?: boolean;
24
+ /** Extra `@source` globs merged into the library defaults. */
25
+ contentExtra?: string[];
26
+ }
27
+ declare function useTheoUIVite(opts?: UseTheoUIPluginOptions): Plugin;
28
+
29
+ export { type UseTheoUIPluginOptions, useTheoUIVite as default };
@@ -0,0 +1,72 @@
1
+ // src/vite-plugin.ts
2
+ var PLUGIN_NAME = "@usetheo/ui/vite-plugin";
3
+ var VIRTUAL_ID = "virtual:@usetheo/ui/library-sources.css";
4
+ var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
5
+ function buildLibrarySourcesCss(extra = []) {
6
+ const defaults = [
7
+ '"../node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}"',
8
+ '"./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}"'
9
+ ];
10
+ const all = [...defaults, ...extra.map((g) => JSON.stringify(g))];
11
+ return `${all.map((src) => `@source ${src};`).join("\n")}
12
+ `;
13
+ }
14
+ function useTheoUIVite(opts = {}) {
15
+ const wantsTailwind = opts.tailwind !== false;
16
+ const extra = opts.contentExtra ?? [];
17
+ let warned = false;
18
+ const warnMissingPeer = () => {
19
+ if (warned) return;
20
+ warned = true;
21
+ console.warn(
22
+ `[${PLUGIN_NAME}] @tailwindcss/vite was not resolvable; falling back to CSS-only mode. Install \`@tailwindcss/vite@^4\` to get the utility-driven design tokens, or import \`@usetheo/ui/styles.css\` directly for the pre-built surface.`
23
+ );
24
+ };
25
+ return {
26
+ name: PLUGIN_NAME,
27
+ // Vite's typing for `config()` since 5.x explicitly omits `plugins` from
28
+ // the allowed return shape to nudge authors toward `Plugin[]` factories.
29
+ // The runtime still honors `{ plugins }` merge, and the TheoKit contract
30
+ // requires ONE Plugin object (so we can't return `Plugin[]` here).
31
+ // The cast keeps the public surface honest and the type-checker quiet.
32
+ async config(_userConfig, _env) {
33
+ if (!wantsTailwind) {
34
+ return void 0;
35
+ }
36
+ try {
37
+ const specifier = "@tailwindcss/vite";
38
+ const mod = await import(
39
+ /* @vite-ignore */
40
+ specifier
41
+ );
42
+ const factory = mod.default;
43
+ if (typeof factory !== "function") {
44
+ warnMissingPeer();
45
+ return void 0;
46
+ }
47
+ const tw = factory();
48
+ const tailwindPlugins = Array.isArray(tw) ? tw : [tw];
49
+ return { plugins: tailwindPlugins };
50
+ } catch {
51
+ warnMissingPeer();
52
+ return void 0;
53
+ }
54
+ },
55
+ resolveId(id) {
56
+ if (id === VIRTUAL_ID) {
57
+ return RESOLVED_VIRTUAL_ID;
58
+ }
59
+ return void 0;
60
+ },
61
+ load(id) {
62
+ if (id === RESOLVED_VIRTUAL_ID) {
63
+ return buildLibrarySourcesCss(extra);
64
+ }
65
+ return void 0;
66
+ }
67
+ };
68
+ }
69
+
70
+ export { useTheoUIVite as default };
71
+ //# sourceMappingURL=vite-plugin.js.map
72
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite-plugin.ts"],"names":[],"mappings":";AA0BA,IAAM,WAAA,GAAc,yBAAA;AACpB,IAAM,UAAA,GAAa,yCAAA;AACnB,IAAM,mBAAA,GAAsB,KAAK,UAAU,CAAA,CAAA;AAE3C,SAAS,sBAAA,CAAuB,KAAA,GAAkB,EAAC,EAAW;AAK5D,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,sDAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,GAAA,GAAM,CAAC,GAAG,QAAA,EAAU,GAAG,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAC,CAAA;AAChE,EAAA,OAAO,CAAA,EAAG,GAAA,CAAI,GAAA,CAAI,CAAC,GAAA,KAAQ,CAAA,QAAA,EAAW,GAAG,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;AAAA,CAAA;AAC1D;AAEe,SAAR,aAAA,CAA+B,IAAA,GAA+B,EAAC,EAAW;AAC/E,EAAA,MAAM,aAAA,GAAgB,KAAK,QAAA,KAAa,KAAA;AACxC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,YAAA,IAAgB,EAAC;AAEpC,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,MAAM,kBAAkB,MAAY;AAClC,IAAA,IAAI,MAAA,EAAQ;AACZ,IAAA,MAAA,GAAS,IAAA;AAET,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,IAAI,WAAW,CAAA,yNAAA;AAAA,KACjB;AAAA,EACF,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAON,MAAM,MAAA,CAAO,WAAA,EAAa,IAAA,EAAwD;AAChF,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,IAAI;AAKF,QAAA,MAAM,SAAA,GAAY,mBAAA;AAClB,QAAA,MAAM,MAAM,MAAM;AAAA;AAAA,UAA0B;AAAA,SAAA;AAC5C,QAAA,MAAM,UAAW,GAAA,CAA8B,OAAA;AAC/C,QAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,UAAA,eAAA,EAAgB;AAChB,UAAA,OAAO,KAAA,CAAA;AAAA,QACT;AACA,QAAA,MAAM,KAAM,OAAA,EAAoC;AAChD,QAAA,MAAM,kBAAkB,KAAA,CAAM,OAAA,CAAQ,EAAE,CAAA,GAAI,EAAA,GAAK,CAAC,EAAE,CAAA;AACpD,QAAA,OAAO,EAAE,SAAS,eAAA,EAAgB;AAAA,MACpC,CAAA,CAAA,MAAQ;AACN,QAAA,eAAA,EAAgB;AAChB,QAAA,OAAO,MAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IAEA,UAAU,EAAA,EAAI;AACZ,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,OAAO,mBAAA;AAAA,MACT;AACA,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAK,EAAA,EAAI;AACP,MAAA,IAAI,OAAO,mBAAA,EAAqB;AAC9B,QAAA,OAAO,uBAAuB,KAAK,CAAA;AAAA,MACrC;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF","file":"vite-plugin.js","sourcesContent":["/**\n * `@usetheo/ui/vite-plugin` — auto-wire Tailwind v4 for consumers.\n *\n * Default export is a factory returning ONE Vite Plugin. Internally:\n * 1. chains `@tailwindcss/vite` v4 via the `config()` hook when resolvable\n * and `opts.tailwind !== false`,\n * 2. exposes a virtual module `virtual:@usetheo/ui/library-sources.css`\n * whose contents register `@source` directives covering the library's\n * published files, so consumer-side Tailwind scans `@usetheo/ui` JSX\n * and emits its utility classes,\n * 3. degrades gracefully via `console.warn` (never throws) when\n * `@tailwindcss/vite` is not installed — the surface stays usable in\n * CSS-only mode via the pre-built `@usetheo/ui/styles.css` subpath.\n *\n * This is the cross-repo contract TheoKit's `integrateUseTheoUI()` probes\n * for. See `docs/rfcs/0008-vite-plugin-and-preset.md`.\n */\nimport type { Plugin, UserConfig } from \"vite\";\n\nexport interface UseTheoUIPluginOptions {\n /** Disable Tailwind v4 auto-chain. Default: `true`. */\n tailwind?: boolean;\n /** Extra `@source` globs merged into the library defaults. */\n contentExtra?: string[];\n}\n\nconst PLUGIN_NAME = \"@usetheo/ui/vite-plugin\";\nconst VIRTUAL_ID = \"virtual:@usetheo/ui/library-sources.css\";\nconst RESOLVED_VIRTUAL_ID = `\\0${VIRTUAL_ID}`;\n\nfunction buildLibrarySourcesCss(extra: string[] = []): string {\n // `@source` paths are resolved relative to the importing CSS file in\n // Tailwind v4. The library defaults below cover the published artifact\n // tree under the consumer's `node_modules/@usetheo/ui/dist/`. Extra globs\n // are appended verbatim — consumers can register additional roots.\n const defaults = [\n '\"../node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}\"',\n '\"./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}\"',\n ];\n const all = [...defaults, ...extra.map((g) => JSON.stringify(g))];\n return `${all.map((src) => `@source ${src};`).join(\"\\n\")}\\n`;\n}\n\nexport default function useTheoUIVite(opts: UseTheoUIPluginOptions = {}): Plugin {\n const wantsTailwind = opts.tailwind !== false;\n const extra = opts.contentExtra ?? [];\n\n let warned = false;\n const warnMissingPeer = (): void => {\n if (warned) return;\n warned = true;\n // biome-ignore lint/suspicious/noConsole: TheoKit cross-repo contract requires `console.warn` (not throw) when the optional `@tailwindcss/vite` peer is missing. See RFC 0008 §3 D1.\n console.warn(\n `[${PLUGIN_NAME}] @tailwindcss/vite was not resolvable; falling back to CSS-only mode. Install \\`@tailwindcss/vite@^4\\` to get the utility-driven design tokens, or import \\`@usetheo/ui/styles.css\\` directly for the pre-built surface.`,\n );\n };\n\n return {\n name: PLUGIN_NAME,\n\n // Vite's typing for `config()` since 5.x explicitly omits `plugins` from\n // the allowed return shape to nudge authors toward `Plugin[]` factories.\n // The runtime still honors `{ plugins }` merge, and the TheoKit contract\n // requires ONE Plugin object (so we can't return `Plugin[]` here).\n // The cast keeps the public surface honest and the type-checker quiet.\n async config(_userConfig, _env): Promise<Omit<UserConfig, \"plugins\"> | undefined> {\n if (!wantsTailwind) {\n return undefined;\n }\n\n try {\n // Dynamic import keeps `@tailwindcss/vite` as an OPTIONAL peer. The\n // specifier is held in a variable so Vite's import-analysis pass\n // leaves it alone — at runtime the consumer's resolver does the\n // actual resolution. Resolution failure must not crash the build.\n const specifier = \"@tailwindcss/vite\";\n const mod = await import(/* @vite-ignore */ specifier);\n const factory = (mod as { default?: unknown }).default;\n if (typeof factory !== \"function\") {\n warnMissingPeer();\n return undefined;\n }\n const tw = (factory as () => Plugin | Plugin[])();\n const tailwindPlugins = Array.isArray(tw) ? tw : [tw];\n return { plugins: tailwindPlugins } as unknown as Omit<UserConfig, \"plugins\">;\n } catch {\n warnMissingPeer();\n return undefined;\n }\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) {\n return RESOLVED_VIRTUAL_ID;\n }\n return undefined;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return buildLibrarySourcesCss(extra);\n }\n return undefined;\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usetheo/ui",
3
- "version": "0.4.0-next.0",
3
+ "version": "0.5.1-next.0",
4
4
  "description": "Theo UI — framework-agnostic React component library with the Violet Forge design system. Focused on AI-agent interfaces, cloud dashboards, and developer-tooling surfaces.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -11,7 +11,11 @@
11
11
  "import": "./dist/index.js"
12
12
  },
13
13
  "./styles.css": "./dist/styles.css",
14
+ "./styles-v3-legacy.css": "./dist/styles-v3-legacy.css",
14
15
  "./tokens.css": "./dist/tokens.css",
16
+ "./tokens-v4.css": "./dist/tokens-v4.css",
17
+ "./preset.css": "./dist/preset.css",
18
+ "./preset": "./dist/preset.css",
15
19
  "./fonts.css": "./dist/fonts.css",
16
20
  "./fonts-cdn.css": "./dist/fonts-cdn.css",
17
21
  "./slide/themes/default.css": "./dist/slide/themes/default.css",
@@ -439,6 +443,14 @@
439
443
  "./slide-deck": {
440
444
  "types": "./dist/slide-deck/index.d.ts",
441
445
  "import": "./dist/slide-deck/index.js"
446
+ },
447
+ "./vite-plugin": {
448
+ "types": "./dist/vite-plugin.d.ts",
449
+ "import": "./dist/vite-plugin.js"
450
+ },
451
+ "./preset-v3-legacy": {
452
+ "types": "./dist/preset-v3-legacy.d.ts",
453
+ "import": "./dist/preset-v3-legacy.js"
442
454
  }
443
455
  },
444
456
  "files": ["dist", "registry/r", "registry/index.json", "LICENSE", "CHANGELOG.md"],
@@ -473,10 +485,13 @@
473
485
  "dogfood:slide": "tsx scripts/dogfood-slide.ts",
474
486
  "dogfood:slide-deck": "tsx scripts/dogfood-slide-deck.ts",
475
487
  "dogfood:slide-rich": "tsx scripts/dogfood-slide-rich.ts",
476
- "quality:gates": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm test && pnpm build && pnpm registry:build && pnpm registry:validate && pnpm quality:structure && pnpm quality:bundle && pnpm quality:a11y && pnpm ladle:build && pnpm dogfood:whiteboard && pnpm dogfood:slide && pnpm dogfood:slide-deck && pnpm dogfood:slide-rich",
488
+ "dogfood:v4-zero-config": "tsx scripts/dogfood-v4-zero-config.ts",
489
+ "dogfood:v4-real-build": "bash scripts/dogfood-v4-real-build.sh",
490
+ "quality:gates": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm test && pnpm build && pnpm registry:build && pnpm registry:validate && pnpm quality:structure && pnpm quality:bundle && pnpm quality:a11y && pnpm ladle:build && pnpm dogfood:whiteboard && pnpm dogfood:slide && pnpm dogfood:slide-deck && pnpm dogfood:slide-rich && pnpm dogfood:v4-zero-config",
477
491
  "quality:gates:fast": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm registry:build && pnpm registry:validate && pnpm quality:structure"
478
492
  },
479
493
  "peerDependencies": {
494
+ "@tailwindcss/vite": "^4.0.0",
480
495
  "hast-util-from-html": "^2.0.0",
481
496
  "hast-util-sanitize": "^5.0.0",
482
497
  "hast-util-to-jsx-runtime": "^2.0.0",
@@ -493,8 +508,10 @@
493
508
  "react-dom": ">=18.2.0 <20",
494
509
  "roughjs": "^4.6.0",
495
510
  "shiki": "^1.0.0",
511
+ "tailwindcss": "^4.0.0",
496
512
  "unist-util-visit": "^5.0.0",
497
513
  "unist-util-visit-parents": "^6.0.0",
514
+ "vite": "^6.0.0 || ^7.0.0",
498
515
  "yaml": "^2.0.0"
499
516
  },
500
517
  "peerDependenciesMeta": {
@@ -548,6 +565,15 @@
548
565
  },
549
566
  "mermaid": {
550
567
  "optional": true
568
+ },
569
+ "@tailwindcss/vite": {
570
+ "optional": true
571
+ },
572
+ "tailwindcss": {
573
+ "optional": true
574
+ },
575
+ "vite": {
576
+ "optional": true
551
577
  }
552
578
  },
553
579
  "dependencies": {