@trineui/cli 0.3.0 → 0.4.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/README.md CHANGED
@@ -1,60 +1,204 @@
1
1
  # @trineui/cli
2
2
 
3
- Canonical public package:
3
+ `@trineui/cli` is the public Trine CLI for the current Button-first baseline.
4
+
5
+ Current public baseline:
6
+
7
+ - guided `init`
8
+ - `add button`
9
+ - Button is the only public component baseline in scope
10
+ - Angular has the strongest proof coverage right now
11
+
12
+ Canonical public flow:
4
13
 
5
14
  ```bash
15
+ npx @trineui/cli@latest init
6
16
  npx @trineui/cli@latest add button
7
17
  ```
8
18
 
9
- Registry status note:
19
+ `init` is guided by default. Use `--yes` for non-interactive mode.
20
+
21
+ ## What Works Right Now
22
+
23
+ The current public Trine baseline is intentionally narrow.
24
+
25
+ It supports:
26
+
27
+ - preparing a supported Angular app with `init`
28
+ - delivering a consumer-owned Button with `add button`
29
+ - wiring a local `@trine/ui` alias into the target app
30
+ - injecting the Trine styling baseline into the target app's real global stylesheet entry
31
+ - local customization by editing `button.skin.ts` or adding semantic variable overrides below the Trine-managed block
32
+
33
+ It does not currently support:
34
+
35
+ - more components than Button
36
+ - automatic prerequisite installation
37
+ - automatic usage-surface scaffolding
38
+ - broad framework coverage claims beyond the current Angular-first baseline
39
+
40
+ ## Before You Start
41
+
42
+ ### Supported target shape
43
+
44
+ The current proven target shape is an Angular app with:
45
+
46
+ - `src/app`
47
+ - `tsconfig.app.json`
48
+ - a valid global stylesheet entry such as:
49
+ - `src/styles.scss`
50
+ - `src/styles.css`
51
+ - `src/global.scss`
52
+ - or a build styles entry resolved from `angular.json`
53
+
54
+ `init` can distinguish `angular` and `ionic-angular`, but Angular has the strongest live proof coverage today.
55
+
56
+ ### Manual prerequisites
57
+
58
+ The live CLI still expects these prerequisites to be installed manually in the target repo.
59
+
60
+ For the current Button baseline, these are hard requirements rather than optional enhancements:
61
+
62
+ ```bash
63
+ npm install class-variance-authority
64
+ npm install -D tailwindcss@^4 @tailwindcss/postcss postcss
65
+ ```
66
+
67
+ The current baseline also expects PostCSS configuration in the target repo:
68
+
69
+ `.postcssrc.json`
70
+
71
+ ```json
72
+ {
73
+ "plugins": {
74
+ "@tailwindcss/postcss": {}
75
+ }
76
+ }
77
+ ```
78
+
79
+ Use a Node LTS line supported by Angular 21 in the target repo. Odd-numbered Node releases can build with warnings.
80
+
81
+ For the styling-layer contract behind these requirements, see:
82
+
83
+ - `docs/01-overview/05-styling-contract.md`
84
+
85
+ ## Quickstart
86
+
87
+ ### 1. Run guided init
88
+
89
+ From the app root:
90
+
91
+ ```bash
92
+ npx @trineui/cli@latest init
93
+ ```
94
+
95
+ If you prefer non-interactive mode:
10
96
 
11
- - live npm `@latest` is currently `0.1.2`
12
- - repo-local next publish candidate is `0.2.0`
13
- - the live registry package currently exposes `add button`
14
- - `init` is implemented in this repo and included in the next publish candidate, but it is not available from the live npm package until publish succeeds
97
+ ```bash
98
+ npx @trineui/cli@latest init --yes
99
+ ```
15
100
 
16
- Repository-local current command surface:
101
+ If you are inside a workspace root or want to target a specific app:
17
102
 
18
103
  ```bash
19
- trine init [--target <app-root>] [--yes]
20
- trine add button --target <app-root>
104
+ npx @trineui/cli@latest init --target /absolute/path/to/app
21
105
  ```
22
106
 
23
- Notes:
107
+ `init` currently:
108
+
109
+ - detects the target app
110
+ - detects `angular` vs `ionic-angular`
111
+ - resolves the target stylesheet entry
112
+ - previews file changes before mutation
113
+ - injects a named Trine-managed styling block into the resolved global stylesheet entry
114
+ - ensures `src/app/components/ui/index.ts`
115
+ - ensures local `@trine/ui` in `tsconfig.app.json`
116
+ - ensures the stylesheet block includes semantic tokens, `.dark` overrides, and Tailwind v4 `@theme inline` mapping
117
+
118
+ `init` does not currently install Tailwind/PostCSS/CVA or generate `.postcssrc.json` for you.
119
+
120
+ Rerunning `init` on an already prepared target should no-op cleanly.
121
+
122
+ ### 2. Deliver Button
123
+
124
+ ```bash
125
+ npx @trineui/cli@latest add button
126
+ ```
127
+
128
+ Or explicitly:
129
+
130
+ ```bash
131
+ npx @trineui/cli@latest add button --target /absolute/path/to/app
132
+ ```
133
+
134
+ `add button` currently delivers:
135
+
136
+ - `src/app/components/ui/button/button.ts`
137
+ - `src/app/components/ui/button/button.html`
138
+ - `src/app/components/ui/button/button.skin.ts`
139
+ - `src/app/components/ui/button/index.ts`
140
+ - `src/app/components/ui/index.ts`
141
+
142
+ It fails clearly if consumer-owned Button files already exist. It does not silently overwrite them.
24
143
 
25
- - `init` is now the preferred first step for project readiness; `add button` still works without it for backward compatibility
26
- - `init` is guided by default: it detects the target, surfaces warnings, previews file changes, and asks for confirmation before mutation
27
- - `init --yes` keeps a non-interactive fast path for automation and scripting
28
- - `button` is the only supported component in this public-style baseline
29
- - omitting `--target` uses the current directory when it already matches the supported Angular app shape
30
- - when the current directory is not a supported Angular app target, the CLI auto-detects a single Angular app target under the current directory and proceeds automatically
31
- - when multiple Angular app targets are found, guided `init` lets the user choose one interactively
32
- - when multiple Angular app targets are found, `init --yes` and `add button` fail clearly and ask for `--target <app-root>`
33
- - when multiple plausible stylesheet entries are found, guided `init` lets the user choose one interactively
34
- - when multiple plausible stylesheet entries are found, `init --yes` fails clearly and asks the user to rerun without `--yes`
35
- - external targets can run `trine add button` from the app root or pass an explicit app root such as `--target /absolute/path/to/angular-app`
36
- - `init` detects `angular` and `ionic-angular` targets conservatively; unsupported targets fail clearly
37
- - the canonical public package name is `@trineui/cli`
38
- - the CLI command exposed through the package bin is still `trine`
39
- - `apps/consumer-fixture` is the first separate-target proof and does not use the demo-only `@trine/ui/*` bridge
40
- - `/tmp/trine-button-publish-proof` is the latest truly external packaged-proof repo outside the monorepo
41
- - packaged/public-style proof uses a packed local tarball to simulate `npx @trineui/cli@latest add button`
42
- - the packaged CLI ships compiled runtime files plus Button templates so it can execute from `node_modules` in a real `npx`-style flow
43
- - `init` ensures `src/styles/tokens.css`, `src/styles/trine-consumer.css`, `src/app/components/ui/index.ts`, the local `@trine/ui` alias, and local stylesheet wiring
44
- - rerunning `init` on an already prepared target exits cleanly with a no-op summary instead of prompting again
45
- - consumer-owned component destination files cause a clear failure
46
- - existing shared baseline files (`tokens.css` and `trine-consumer.css`) are preserved so a second component can be added into the same target repo
47
- - the command copies consumer-owned source instead of wiring runtime back to `packages/ui`
48
- - for `apps/demo`, `@trine/ui` resolves locally for delivered components while `@trine/ui/*` temporarily bridges non-localized components back to the authoring source
49
- - the delivered shared styling baseline is `tokens.css` + `trine-consumer.css`
50
- - the current proven target dependency baseline is Angular 21, Tailwind CSS v4, and `class-variance-authority`
51
- - the current proven target shape accepts a global stylesheet entry such as `src/styles.scss`, `src/styles.css`, or `src/global.scss` when it is resolved from `angular.json`
52
- - use a Node LTS line supported by Angular 21 in the target repo; odd-numbered Node releases can build with warnings
53
- - when `package.json` is present in the target root, the CLI warns if Tailwind CSS v4 or `class-variance-authority` are missing
144
+ ### 3. Render a small proof
54
145
 
55
- Local package proof equivalent:
146
+ Import `TrineButton` from local `@trine/ui` and render a small proof surface:
147
+
148
+ ```ts
149
+ import { Component } from '@angular/core';
150
+ import { TrineButton } from '@trine/ui';
151
+
152
+ @Component({
153
+ selector: 'app-root',
154
+ imports: [TrineButton],
155
+ template: `
156
+ <trine-button>Primary action</trine-button>
157
+ <trine-button variant="secondary">Secondary action</trine-button>
158
+ <trine-button [loading]="true">Saving</trine-button>
159
+ `,
160
+ })
161
+ export class App {}
162
+ ```
163
+
164
+ ### 4. Build the target app
56
165
 
57
166
  ```bash
58
- npx --yes --package /absolute/path/to/trineui-cli-<version>.tgz trine init
59
- npx --yes --package /absolute/path/to/trineui-cli-<version>.tgz trine add button
167
+ npm run build
60
168
  ```
169
+
170
+ Successful verification usually means:
171
+
172
+ - the app builds
173
+ - `@trine/ui` resolves to local consumer source
174
+ - the resolved stylesheet entry contains a Trine-managed baseline block
175
+ - Button renders from local delivered files
176
+
177
+ ## Customize Locally
178
+
179
+ Trine uses a copy-paste ownership model.
180
+
181
+ After delivery, the consumer repo owns the visual layer.
182
+
183
+ The two main local customization paths are:
184
+
185
+ - edit `src/app/components/ui/button/button.skin.ts`
186
+ - add semantic variable overrides below the Trine-managed block in the resolved global stylesheet entry
187
+
188
+ This is the intended model. Trine does not keep runtime ownership of the Button visuals after delivery.
189
+
190
+ ## Current Limitations
191
+
192
+ - Button-only public baseline
193
+ - manual prerequisites still required
194
+ - usage surface is not scaffolded automatically
195
+ - Angular has the strongest live proof coverage; Ionic Angular coverage is narrower
196
+ - multiple target apps still require `--target` or guided selection
197
+ - `src/styles.scss` and `src/global.scss` currently work, but Angular 21 surfaces a Sass deprecation warning for `@import 'tailwindcss';`
198
+ - the baseline is intentionally narrow and not yet a broader component platform
199
+
200
+ ## Full Walkthrough
201
+
202
+ For a fuller repo-level walkthrough of the current baseline, see:
203
+
204
+ - `docs/01-overview/04-cli-getting-started.md`
@@ -1,6 +1,6 @@
1
1
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { TEMPLATE_ROOT, ensureLinesFile, ensureSharedStyleBaseline, ensureStylesImport, ensureTsconfigAlias, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
3
+ import { TEMPLATE_ROOT, ensureLinesFile, ensureTrineStylesheetBaseline, ensureTsconfigAlias, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
4
4
  const DEMO_BRIDGE_COMMENT = '// Temporary demo verification bridge: delivered local components resolve locally; other components still re-export from the authoring source.';
5
5
  const DEMO_LOCAL_COMPONENTS = [
6
6
  {
@@ -76,10 +76,9 @@ export function addComponent(manifest, options) {
76
76
  copyFileSync(source, destination);
77
77
  }
78
78
  const componentCopiedFiles = componentCopyTargets.map(({ destination }) => toTargetRelativePath(targetRoot, destination));
79
- const sharedStylesResult = ensureSharedStyleBaseline(targetRoot, manifest.componentLabel);
80
- const copiedFiles = [...componentCopiedFiles, ...sharedStylesResult.createdFiles];
79
+ const copiedFiles = [...componentCopiedFiles];
81
80
  const updatedFiles = [];
82
- const warnings = [...sharedStylesResult.warnings];
81
+ const warnings = [];
83
82
  const componentBarrelPath = path.join(componentDestDir, 'index.ts');
84
83
  if (ensureLinesFile(componentBarrelPath, [manifest.barrelLine])) {
85
84
  updatedFiles.push(toTargetRelativePath(targetRoot, componentBarrelPath));
@@ -94,12 +93,13 @@ export function addComponent(manifest, options) {
94
93
  if (ensureTsconfigAlias(targetTsconfig, targetRoot, options.cwd)) {
95
94
  updatedFiles.push(toTargetRelativePath(targetRoot, targetTsconfig));
96
95
  }
97
- const stylesResult = ensureStylesImport(targetStylesEntry);
96
+ const stylesResult = ensureTrineStylesheetBaseline(targetRoot, targetStylesEntry);
98
97
  if (stylesResult.updated) {
99
98
  updatedFiles.push(toTargetRelativePath(targetRoot, targetStylesEntry));
100
99
  }
100
+ warnings.push(...stylesResult.warnings);
101
101
  if (stylesResult.authoringImportStillPresent) {
102
- warnings.push(`${toTargetRelativePath(targetRoot, targetStylesEntry)} still imports @trine/ui/styles/trine.css for the broader demo authoring baseline.`);
102
+ warnings.push(`${toTargetRelativePath(targetRoot, targetStylesEntry)} keeps the repo-local @trine/ui/styles/trine.css authoring entry so apps/demo can still validate non-localized components during local development.`);
103
103
  }
104
104
  if (isDemoTarget(targetRoot)) {
105
105
  warnings.push('apps/demo keeps non-localized components on a temporary @trine/ui/* bridge back to packages/ui so the full demo app can still build while delivered components resolve locally.');
package/dist/index.js CHANGED
@@ -19,13 +19,13 @@ Defaults:
19
19
 
20
20
  Notes:
21
21
  - v0 supports init plus add button only
22
- - init owns target detection, framework detection, stylesheet resolution, baseline files, local @trine/ui alias setup, and local stylesheet wiring
22
+ - init owns target detection, framework detection, stylesheet resolution, Trine-managed global stylesheet injection, and local @trine/ui alias setup
23
23
  - add button still works without init for backward compatibility, but init is the preferred first step
24
24
  - the current proven target model is Angular 21 + src/app + tsconfig.app.json + a global stylesheet entry such as src/styles.scss, src/styles.css, or src/global.scss resolved directly or from angular.json
25
25
  - v0 distinguishes angular and ionic-angular targets; unsupported frameworks fail clearly
26
- - the current proven styling/runtime baseline requires Tailwind CSS v4 and class-variance-authority in the target repo
26
+ - the current proven styling/runtime baseline requires Tailwind CSS v4, PostCSS with @tailwindcss/postcss, and class-variance-authority in the target repo
27
27
  - consumer-owned component files fail clearly if they already exist
28
- - shared styling baseline files are copied when missing and preserved when they already exist
28
+ - init injects a Trine-managed styling block into the resolved global stylesheet entry instead of copying separate shared CSS files
29
29
  - @trine/ui is configured as a consumer-local alias inside the target app
30
30
  - apps/demo keeps a temporary @trine/ui/* bridge for non-localized components during local repo verification`;
31
31
  const SUPPORTED_COMPONENTS = ['button'];
@@ -245,7 +245,7 @@ function printInitSuccess(result, selection) {
245
245
  if (result.warnings.length > 0) {
246
246
  lines.push('', 'Warnings:', ...result.warnings.map((warning) => `- ${warning}`));
247
247
  }
248
- lines.push('', 'Manual next steps:', '- Review prerequisite warnings and install anything still missing before building the target app.', '- Run trine add button when you want to deliver the first Trine component into this project.', '- Build the target app and confirm @trine/ui resolves through the local consumer-owned source tree.');
248
+ lines.push('', 'Manual next steps:', '- Review prerequisite warnings and install anything still missing before building the target app.', '- Run trine add button when you want to deliver the first Trine component into this project.', '- Build the target app and confirm @trine/ui resolves through the local consumer-owned source tree.', '- If you want token-level customization, add semantic variable overrides below the Trine-managed block in the global stylesheet instead of editing inside the managed block.');
249
249
  console.log(lines.join('\n'));
250
250
  }
251
251
  function printAddSuccess(component, result, selection) {
@@ -260,7 +260,7 @@ function printAddSuccess(component, result, selection) {
260
260
  if (result.warnings.length > 0) {
261
261
  lines.push('', 'Warnings:', ...result.warnings.map((warning) => `- ${warning}`));
262
262
  }
263
- lines.push('', 'Manual next steps:', `- Ensure the target repo has Tailwind CSS v4 and class-variance-authority installed before building the delivered ${componentLabel}.`, `- Build the target app and confirm ${componentLabel} imports resolve through the local @trine/ui alias.`, `- Review the copied ${component}.skin.ts and tokens.css if you want local consumer customization.`, '- For future setup in this repo, prefer running trine init first so project-level Trine baseline files are already in place.');
263
+ lines.push('', 'Manual next steps:', `- Ensure the target repo has Tailwind CSS v4, PostCSS with @tailwindcss/postcss, and class-variance-authority installed before building the delivered ${componentLabel}.`, `- Build the target app and confirm ${componentLabel} imports resolve through the local @trine/ui alias.`, `- Review the copied ${component}.skin.ts if you want component-level local customization.`, '- Add semantic token overrides below the Trine-managed global stylesheet block if you want app-level visual customization that survives rerunning init.', '- For future setup in this repo, prefer running trine init first so the project-level Trine baseline is already in place.');
264
264
  if (isRepoDemoVerification) {
265
265
  lines.push('- Open /validation-shell and review the CLI delivery proof section for the temporary demo verification path.');
266
266
  }
package/dist/init.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { ensureSharedStyleBaseline, ensureStylesImport, ensureTsconfigAlias, ensureUiRootBarrel, inspectSharedStyleBaseline, inspectStylesImport, inspectTsconfigAlias, inspectUiRootBarrel, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
2
+ import { ensureTrineStylesheetBaseline, ensureTsconfigAlias, ensureUiRootBarrel, inspectTrineStylesheetBaseline, inspectTsconfigAlias, inspectUiRootBarrel, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
3
3
  export function planInitProject(options) {
4
4
  const targetRoot = path.resolve(options.cwd, options.target);
5
5
  const { framework, targetStylesEntry, targetTsconfig } = resolveSupportedProjectTarget('trine init', targetRoot, {
@@ -9,10 +9,6 @@ export function planInitProject(options) {
9
9
  const updatedFiles = [];
10
10
  const reusedFiles = [];
11
11
  const warnings = [];
12
- const sharedStylesResult = inspectSharedStyleBaseline(targetRoot, 'Trine components');
13
- createdFiles.push(...sharedStylesResult.createdFiles);
14
- reusedFiles.push(...sharedStylesResult.reusedFiles);
15
- warnings.push(...sharedStylesResult.warnings);
16
12
  const uiRootResult = inspectUiRootBarrel(targetRoot);
17
13
  if (uiRootResult.created) {
18
14
  createdFiles.push(uiRootResult.file);
@@ -28,15 +24,16 @@ export function planInitProject(options) {
28
24
  reusedFiles.push(tsconfigDisplayPath);
29
25
  }
30
26
  const stylesDisplayPath = toTargetRelativePath(targetRoot, targetStylesEntry);
31
- const stylesResult = inspectStylesImport(targetStylesEntry);
27
+ const stylesResult = inspectTrineStylesheetBaseline(targetRoot, targetStylesEntry);
32
28
  if (stylesResult.needsUpdate) {
33
29
  updatedFiles.push(stylesDisplayPath);
34
30
  }
35
31
  else {
36
32
  reusedFiles.push(stylesDisplayPath);
37
33
  }
34
+ warnings.push(...stylesResult.warnings);
38
35
  if (stylesResult.authoringImportStillPresent) {
39
- warnings.push(`${stylesDisplayPath} still imports @trine/ui/styles/trine.css for a broader authoring baseline outside the local Trine consumer setup.`);
36
+ warnings.push(`${stylesDisplayPath} keeps the repo-local @trine/ui/styles/trine.css authoring entry so apps/demo can still validate non-localized components during local development.`);
40
37
  }
41
38
  if (isDemoTarget(targetRoot)) {
42
39
  warnings.push('apps/demo keeps a temporary @trine/ui/* bridge for non-localized components so the internal demo app can still build while local Trine source is verified.');
@@ -56,10 +53,9 @@ export function planInitProject(options) {
56
53
  };
57
54
  }
58
55
  export function applyInitPlan(plan, options) {
59
- ensureSharedStyleBaseline(plan.targetRoot, 'Trine components');
60
56
  ensureUiRootBarrel(plan.targetRoot);
61
57
  ensureTsconfigAlias(plan.targetTsconfig, plan.targetRoot, options.cwd);
62
- ensureStylesImport(plan.targetStylesEntry);
58
+ ensureTrineStylesheetBaseline(plan.targetRoot, plan.targetStylesEntry);
63
59
  return plan;
64
60
  }
65
61
  export function initProject(options) {
package/dist/project.js CHANGED
@@ -1,9 +1,26 @@
1
- import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from 'node:fs';
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import ts from 'typescript';
5
- const STYLE_SOURCE_FILES = ['styles/tokens.css', 'styles/trine-consumer.css'];
6
- const STYLE_IMPORT_LINE = "@import './styles/trine-consumer.css';";
5
+ const TRINE_BASELINE_START_LINE = '/* --- Trine styling baseline: start --- */';
6
+ const TRINE_BASELINE_END_LINE = '/* --- Trine styling baseline: end --- */';
7
+ const TRINE_BASELINE_TEMPLATE_FILE = 'styles/trine-baseline.css';
8
+ const LEGACY_STYLE_IMPORT_LINES = [
9
+ "@import './styles/trine-consumer.css';",
10
+ '@import "./styles/trine-consumer.css";',
11
+ ];
12
+ const AUTHORING_STYLE_IMPORT_LINES = [
13
+ "@import '@trine/ui/styles/trine.css';",
14
+ '@import "@trine/ui/styles/trine.css";',
15
+ ];
16
+ const LEGACY_STYLE_FILES = ['src/styles/tokens.css', 'src/styles/trine-consumer.css'];
17
+ const POSTCSS_CONFIG_FILE_NAMES = [
18
+ '.postcssrc.json',
19
+ '.postcssrc',
20
+ 'postcss.config.js',
21
+ 'postcss.config.cjs',
22
+ 'postcss.config.mjs',
23
+ ];
7
24
  const CONVENTIONAL_STYLE_ENTRY_PATHS = ['src/styles.scss', 'src/styles.css', 'src/global.scss'];
8
25
  export const TEMPLATE_ROOT = fileURLToPath(new URL('../templates/', import.meta.url));
9
26
  const LOCAL_REPO_ROOT = path.resolve(TEMPLATE_ROOT, '../../..');
@@ -62,47 +79,6 @@ export function findAngularAppTargets(root) {
62
79
  export function looksLikeAngularAppRoot(root) {
63
80
  return inspectProjectTarget(root).framework !== 'unsupported';
64
81
  }
65
- export function ensureSharedStyleBaseline(targetRoot, componentLabel) {
66
- const inspection = inspectSharedStyleBaseline(targetRoot, componentLabel);
67
- const stylesDestDir = path.join(targetRoot, 'src', 'styles');
68
- mkdirSync(stylesDestDir, { recursive: true });
69
- for (const sourceFile of STYLE_SOURCE_FILES) {
70
- const templatePath = path.join(TEMPLATE_ROOT, sourceFile);
71
- const destinationPath = path.join(stylesDestDir, path.basename(sourceFile));
72
- if (!inspection.createdFiles.includes(toTargetRelativePath(targetRoot, destinationPath))) {
73
- continue;
74
- }
75
- if (!existsSync(destinationPath)) {
76
- copyFileSync(templatePath, destinationPath);
77
- }
78
- }
79
- return inspection;
80
- }
81
- export function inspectSharedStyleBaseline(targetRoot, componentLabel) {
82
- const stylesDestDir = path.join(targetRoot, 'src', 'styles');
83
- const createdFiles = [];
84
- const reusedFiles = [];
85
- const warnings = [];
86
- for (const sourceFile of STYLE_SOURCE_FILES) {
87
- const templatePath = path.join(TEMPLATE_ROOT, sourceFile);
88
- const destinationPath = path.join(stylesDestDir, path.basename(sourceFile));
89
- const relativeDestination = toTargetRelativePath(targetRoot, destinationPath);
90
- if (!existsSync(destinationPath)) {
91
- createdFiles.push(relativeDestination);
92
- continue;
93
- }
94
- if (readFileSync(destinationPath, 'utf8') !== readFileSync(templatePath, 'utf8')) {
95
- warnings.push(`${relativeDestination} already exists and was preserved. Review it manually if the delivered ${componentLabel} expects newer shared styling baseline content.`);
96
- continue;
97
- }
98
- reusedFiles.push(relativeDestination);
99
- }
100
- return {
101
- createdFiles,
102
- reusedFiles,
103
- warnings,
104
- };
105
- }
106
82
  export function ensureUiRootBarrel(targetRoot) {
107
83
  const uiRootPath = path.join(targetRoot, 'src', 'app', 'components', 'ui', 'index.ts');
108
84
  const inspection = inspectUiRootBarrel(targetRoot);
@@ -181,56 +157,58 @@ export function inspectTsconfigAlias(tsconfigPath, targetRoot, cwd) {
181
157
  (Array.isArray(currentWildcardAlias) &&
182
158
  currentWildcardAlias.length === 1 &&
183
159
  currentWildcardAlias[0] === wildcardTarget);
184
- if (aliasIsCurrent && wildcardIsCurrent) {
185
- return {
186
- needsUpdate: false,
187
- };
188
- }
189
160
  return {
190
- needsUpdate: true,
161
+ needsUpdate: !(aliasIsCurrent && wildcardIsCurrent),
191
162
  };
192
163
  }
193
- export function ensureStylesImport(stylesPath) {
194
- const inspection = inspectStylesImport(stylesPath);
164
+ export function ensureTrineStylesheetBaseline(targetRoot, stylesPath) {
165
+ const inspection = inspectTrineStylesheetBaseline(targetRoot, stylesPath);
195
166
  if (!inspection.needsUpdate) {
196
167
  return {
197
168
  updated: false,
198
169
  authoringImportStillPresent: inspection.authoringImportStillPresent,
170
+ warnings: inspection.warnings,
199
171
  };
200
172
  }
201
173
  const current = readFileSync(stylesPath, 'utf8');
202
- const lines = current.split('\n');
203
- let insertAt = -1;
204
- for (let index = 0; index < lines.length; index += 1) {
205
- const trimmed = lines[index].trim();
206
- if (trimmed.startsWith('@use') || trimmed.startsWith('@import')) {
207
- insertAt = index;
208
- }
209
- }
210
- if (insertAt === -1) {
211
- lines.unshift(STYLE_IMPORT_LINE, '');
212
- }
213
- else {
214
- lines.splice(insertAt + 1, 0, STYLE_IMPORT_LINE);
215
- }
216
- writeFileSync(stylesPath, lines.join('\n'));
174
+ const next = isDemoTarget(targetRoot)
175
+ ? buildDemoAuthoringStylesheet(current)
176
+ : buildConsumerInjectedStylesheet(current);
177
+ writeFileSync(stylesPath, next);
217
178
  return {
218
179
  updated: true,
219
- authoringImportStillPresent: inspection.authoringImportStillPresent,
180
+ authoringImportStillPresent: isDemoTarget(targetRoot),
181
+ warnings: inspection.warnings,
220
182
  };
221
183
  }
222
- export function inspectStylesImport(stylesPath) {
184
+ export function inspectTrineStylesheetBaseline(targetRoot, stylesPath) {
223
185
  const current = readFileSync(stylesPath, 'utf8');
186
+ const warnings = buildLegacyStyleWarnings(targetRoot);
187
+ const authoringImportStillPresent = containsExactImportLine(current, AUTHORING_STYLE_IMPORT_LINES);
188
+ if (isDemoTarget(targetRoot)) {
189
+ return {
190
+ needsUpdate: containsManagedBlock(current) ||
191
+ containsExactImportLine(current, LEGACY_STYLE_IMPORT_LINES) ||
192
+ !authoringImportStillPresent,
193
+ authoringImportStillPresent,
194
+ warnings,
195
+ };
196
+ }
197
+ const managedBlock = extractManagedBlock(current);
198
+ const expectedManagedBlock = buildManagedStylesheetBlock();
199
+ const hasLegacyImports = containsExactImportLine(current, LEGACY_STYLE_IMPORT_LINES) || authoringImportStillPresent;
200
+ const hasDuplicateTailwindImport = hasTailwindImportOutsideManagedBlock(current);
224
201
  return {
225
- needsUpdate: !current.includes(STYLE_IMPORT_LINE),
226
- authoringImportStillPresent: current.includes("@import '@trine/ui/styles/trine.css';"),
202
+ needsUpdate: managedBlock !== expectedManagedBlock || hasLegacyImports || hasDuplicateTailwindImport,
203
+ authoringImportStillPresent,
204
+ warnings,
227
205
  };
228
206
  }
229
207
  export function readTargetDependencyWarnings(targetRoot, componentLabel) {
230
208
  const packageJsonPath = findNearestFileUpward(targetRoot, 'package.json');
231
209
  if (!packageJsonPath) {
232
210
  return [
233
- 'No package.json was found at or above the target root, so Tailwind CSS v4 and class-variance-authority prerequisites could not be checked automatically.',
211
+ 'No package.json was found at or above the target root, so Tailwind CSS v4, PostCSS, and class-variance-authority prerequisites could not be checked automatically.',
234
212
  ];
235
213
  }
236
214
  try {
@@ -250,11 +228,21 @@ export function readTargetDependencyWarnings(targetRoot, componentLabel) {
250
228
  else if (!looksLikeTailwindV4(tailwindRange)) {
251
229
  warnings.push(`The target repo declares tailwindcss@${tailwindRange}. The current proven ${componentLabel} baseline expects Tailwind CSS v4.`);
252
230
  }
231
+ if (!deps['@tailwindcss/postcss']) {
232
+ warnings.push(`@tailwindcss/postcss is missing from the target repo. The current proven ${componentLabel} baseline expects it for Tailwind CSS v4 processing.`);
233
+ }
234
+ if (!deps['postcss']) {
235
+ warnings.push(`postcss is missing from the target repo. The current proven ${componentLabel} baseline expects it for Tailwind CSS v4 processing.`);
236
+ }
237
+ const postcssConfigPath = findNearestPostcssConfigPath(targetRoot);
238
+ if (!postcssConfigPath) {
239
+ warnings.push('No PostCSS config was found at or above the target root. The current proven baseline expects a PostCSS config that enables @tailwindcss/postcss.');
240
+ }
253
241
  return warnings;
254
242
  }
255
243
  catch {
256
244
  return [
257
- `package.json could not be parsed for dependency checks. Verify Tailwind CSS v4 and class-variance-authority manually before building the delivered ${componentLabel}.`,
245
+ `package.json could not be parsed for dependency checks. Verify Tailwind CSS v4, PostCSS, and class-variance-authority manually before building the delivered ${componentLabel}.`,
258
246
  ];
259
247
  }
260
248
  }
@@ -403,6 +391,15 @@ function findFilesUpward(startDir, fileName) {
403
391
  function findNearestFileUpward(startDir, fileName) {
404
392
  return findFilesUpward(startDir, fileName)[0];
405
393
  }
394
+ function findNearestPostcssConfigPath(startDir) {
395
+ for (const fileName of POSTCSS_CONFIG_FILE_NAMES) {
396
+ const match = findNearestFileUpward(startDir, fileName);
397
+ if (match) {
398
+ return match;
399
+ }
400
+ }
401
+ return undefined;
402
+ }
406
403
  function looksLikeTailwindV4(range) {
407
404
  const versionTokenPattern = /(?:^|[<>=~^|\s:])v?(\d+)(?:(?:\.\d+){0,2})/g;
408
405
  return [...range.matchAll(versionTokenPattern)].some((match) => Number(match[1]) === 4);
@@ -427,18 +424,46 @@ function resolveStylesEntrySelection(targetRoot, candidates, preferredStylesEntr
427
424
  };
428
425
  }
429
426
  }
430
- const trineOwnedCandidates = candidates.filter((candidate) => {
427
+ const managedBlockCandidates = candidates.filter((candidate) => {
428
+ try {
429
+ return containsManagedBlock(readFileSync(candidate, 'utf8'));
430
+ }
431
+ catch {
432
+ return false;
433
+ }
434
+ });
435
+ if (managedBlockCandidates.length === 1) {
436
+ return {
437
+ targetStylesEntry: managedBlockCandidates[0],
438
+ targetStylesEntryResolution: 'managed-block',
439
+ };
440
+ }
441
+ const legacyImportCandidates = candidates.filter((candidate) => {
431
442
  try {
432
- return readFileSync(candidate, 'utf8').includes(STYLE_IMPORT_LINE);
443
+ return containsExactImportLine(readFileSync(candidate, 'utf8'), LEGACY_STYLE_IMPORT_LINES);
433
444
  }
434
445
  catch {
435
446
  return false;
436
447
  }
437
448
  });
438
- if (trineOwnedCandidates.length === 1) {
449
+ if (legacyImportCandidates.length === 1) {
439
450
  return {
440
- targetStylesEntry: trineOwnedCandidates[0],
441
- targetStylesEntryResolution: 'trine-import',
451
+ targetStylesEntry: legacyImportCandidates[0],
452
+ targetStylesEntryResolution: 'legacy-import',
453
+ };
454
+ }
455
+ const authoringImportCandidates = candidates.filter((candidate) => {
456
+ try {
457
+ return containsExactImportLine(readFileSync(candidate, 'utf8'), AUTHORING_STYLE_IMPORT_LINES);
458
+ }
459
+ catch {
460
+ return false;
461
+ }
462
+ });
463
+ if (authoringImportCandidates.length === 1) {
464
+ return {
465
+ targetStylesEntry: authoringImportCandidates[0],
466
+ targetStylesEntryResolution: 'authoring-import',
442
467
  };
443
468
  }
444
469
  if (candidates.length > 0) {
@@ -452,3 +477,112 @@ function resolveStylesEntrySelection(targetRoot, candidates, preferredStylesEntr
452
477
  targetStylesEntryResolution: 'none',
453
478
  };
454
479
  }
480
+ function buildManagedStylesheetBlock() {
481
+ const templatePath = path.join(TEMPLATE_ROOT, TRINE_BASELINE_TEMPLATE_FILE);
482
+ const templateContent = readFileSync(templatePath, 'utf8').trimEnd();
483
+ return `${TRINE_BASELINE_START_LINE}\n${templateContent}\n${TRINE_BASELINE_END_LINE}`;
484
+ }
485
+ function containsManagedBlock(current) {
486
+ return current.includes(TRINE_BASELINE_START_LINE) && current.includes(TRINE_BASELINE_END_LINE);
487
+ }
488
+ function extractManagedBlock(current) {
489
+ const match = current.match(new RegExp(`${escapeRegExp(TRINE_BASELINE_START_LINE)}[\\s\\S]*?${escapeRegExp(TRINE_BASELINE_END_LINE)}`));
490
+ return match?.[0];
491
+ }
492
+ function stripManagedBlock(current) {
493
+ return current.replace(new RegExp(`\\n?${escapeRegExp(TRINE_BASELINE_START_LINE)}[\\s\\S]*?${escapeRegExp(TRINE_BASELINE_END_LINE)}\\n?`, 'g'), '\n');
494
+ }
495
+ function buildConsumerInjectedStylesheet(current) {
496
+ const withoutManagedBlock = stripManagedBlock(current);
497
+ const withoutLegacyImports = removeExactImportLines(removeExactImportLines(withoutManagedBlock, LEGACY_STYLE_IMPORT_LINES), AUTHORING_STYLE_IMPORT_LINES);
498
+ const withoutTailwindImports = removeTailwindImports(withoutLegacyImports);
499
+ return insertManagedBlock(withoutTailwindImports, buildManagedStylesheetBlock());
500
+ }
501
+ function buildDemoAuthoringStylesheet(current) {
502
+ const withoutManagedBlock = stripManagedBlock(current);
503
+ const withoutLegacyImports = removeExactImportLines(withoutManagedBlock, LEGACY_STYLE_IMPORT_LINES);
504
+ if (containsExactImportLine(withoutLegacyImports, AUTHORING_STYLE_IMPORT_LINES)) {
505
+ return normalizeStylesheetText(withoutLegacyImports);
506
+ }
507
+ return insertManagedBlock(withoutLegacyImports, AUTHORING_STYLE_IMPORT_LINES[0]);
508
+ }
509
+ function removeExactImportLines(current, importsToRemove) {
510
+ return current
511
+ .split('\n')
512
+ .filter((line) => !importsToRemove.includes(line.trim()))
513
+ .join('\n');
514
+ }
515
+ function removeTailwindImports(current) {
516
+ return current
517
+ .split('\n')
518
+ .filter((line) => !isTailwindImportLine(line.trim()))
519
+ .join('\n');
520
+ }
521
+ function containsExactImportLine(current, candidates) {
522
+ return current.split('\n').some((line) => candidates.includes(line.trim()));
523
+ }
524
+ function hasTailwindImportOutsideManagedBlock(current) {
525
+ return stripManagedBlock(current)
526
+ .split('\n')
527
+ .some((line) => isTailwindImportLine(line.trim()));
528
+ }
529
+ function isTailwindImportLine(line) {
530
+ return (line === "@import 'tailwindcss';" ||
531
+ line === '@import "tailwindcss";' ||
532
+ line === "@import url('tailwindcss');" ||
533
+ line === '@import url("tailwindcss");');
534
+ }
535
+ function insertManagedBlock(current, block) {
536
+ const normalized = normalizeStylesheetText(current);
537
+ if (normalized.trim() === '') {
538
+ return `${block}\n`;
539
+ }
540
+ const lines = normalized.split('\n');
541
+ let insertAt = 0;
542
+ for (let index = 0; index < lines.length; index += 1) {
543
+ const trimmed = lines[index].trim();
544
+ if (isPreludeLine(trimmed)) {
545
+ insertAt = index + 1;
546
+ continue;
547
+ }
548
+ break;
549
+ }
550
+ const before = lines.slice(0, insertAt).join('\n').trimEnd();
551
+ const after = lines.slice(insertAt).join('\n').trimStart();
552
+ return joinSections(before, block, after);
553
+ }
554
+ function joinSections(...sections) {
555
+ const populatedSections = sections.filter((section) => section.trim() !== '');
556
+ if (populatedSections.length === 0) {
557
+ return '\n';
558
+ }
559
+ return `${populatedSections.join('\n\n').trimEnd()}\n`;
560
+ }
561
+ function isPreludeLine(trimmedLine) {
562
+ return (trimmedLine === '' ||
563
+ trimmedLine.startsWith('//') ||
564
+ trimmedLine.startsWith('/*') ||
565
+ trimmedLine.startsWith('*') ||
566
+ trimmedLine.startsWith('*/') ||
567
+ trimmedLine.startsWith('@charset') ||
568
+ trimmedLine.startsWith('@use') ||
569
+ trimmedLine.startsWith('@forward') ||
570
+ trimmedLine.startsWith('@import'));
571
+ }
572
+ function normalizeStylesheetText(current) {
573
+ return current
574
+ .replace(/\r\n/g, '\n')
575
+ .replace(/\n{3,}/g, '\n\n')
576
+ .trim();
577
+ }
578
+ function buildLegacyStyleWarnings(targetRoot) {
579
+ return listLegacyStyleFiles(targetRoot).map((file) => `${file} still exists from the older separate-file Trine baseline. The injected global stylesheet model no longer requires it, so remove it manually when you no longer need it.`);
580
+ }
581
+ function listLegacyStyleFiles(targetRoot) {
582
+ return LEGACY_STYLE_FILES.map((relativePath) => path.join(targetRoot, relativePath))
583
+ .filter((filePath) => existsSync(filePath))
584
+ .map((filePath) => toTargetRelativePath(targetRoot, filePath));
585
+ }
586
+ function escapeRegExp(value) {
587
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
588
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trineui/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Copy-paste ownership CLI for Trine UI components.",
6
6
  "main": "./dist/index.js",
@@ -7,47 +7,47 @@ export const buttonSkin = cva(
7
7
  'font-medium leading-none',
8
8
  'select-none',
9
9
  '[&:has(>.sr-only)]:gap-0 [&:has(>.sr-only)]:px-0',
10
- 'rounded-[var(--trine-radius-md)] border',
11
- 'transition-[background-color,border-color,color,box-shadow,transform] duration-[var(--trine-duration-base)] ease-[var(--trine-ease-default)]',
12
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--trine-color-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--trine-color-bg)]',
13
- 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:text-[var(--trine-color-text-disabled)] disabled:shadow-none',
10
+ 'rounded-md border',
11
+ 'transition-[background-color,border-color,color,box-shadow,transform] duration-[var(--duration-base)] ease-[var(--ease-default)]',
12
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background',
13
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:text-muted-foreground disabled:shadow-none',
14
14
  ],
15
15
  {
16
16
  variants: {
17
17
  variant: {
18
18
  primary: [
19
- 'bg-[var(--trine-color-primary-default)]',
20
- 'border-[var(--trine-color-primary-default)]',
21
- 'text-[var(--trine-color-primary-fg)]',
22
- 'shadow-[var(--trine-shadow-sm)]',
23
- 'hover:bg-[var(--trine-color-primary-hover)] hover:border-[var(--trine-color-primary-hover)]',
24
- 'active:bg-[var(--trine-color-primary-active)] active:border-[var(--trine-color-primary-active)] active:translate-y-px active:shadow-none',
25
- 'disabled:border-[var(--trine-color-primary-subtle)] disabled:bg-[var(--trine-color-primary-subtle)]',
19
+ 'bg-primary',
20
+ 'border-primary',
21
+ 'text-primary-foreground',
22
+ 'shadow-[var(--shadow-sm)]',
23
+ 'hover:bg-primary-hover hover:border-primary-hover',
24
+ 'active:bg-primary-active active:border-primary-active active:translate-y-px active:shadow-none',
25
+ 'disabled:border-primary-subtle disabled:bg-primary-subtle',
26
26
  ],
27
27
  secondary: [
28
- 'bg-[var(--trine-color-neutral-subtle)]',
29
- 'border-[var(--trine-color-border)]',
30
- 'text-[var(--trine-color-text)]',
31
- 'hover:bg-[var(--trine-color-neutral-hover)] hover:border-[var(--trine-color-border-strong)]',
32
- 'active:bg-[var(--trine-color-neutral-active)] active:border-[var(--trine-color-border-strong)] active:translate-y-px',
33
- 'disabled:border-[var(--trine-color-border)] disabled:bg-[var(--trine-color-neutral-subtle)]',
28
+ 'bg-secondary',
29
+ 'border-border',
30
+ 'text-secondary-foreground',
31
+ 'hover:bg-secondary-hover hover:border-border-strong',
32
+ 'active:bg-secondary-active active:border-border-strong active:translate-y-px',
33
+ 'disabled:border-border disabled:bg-muted',
34
34
  ],
35
35
  ghost: [
36
36
  'bg-transparent',
37
37
  'border-transparent',
38
- 'text-[var(--trine-color-primary-default)]',
39
- 'hover:bg-[var(--trine-color-primary-subtle)] hover:text-[var(--trine-color-primary-hover)]',
40
- 'active:bg-[var(--trine-color-primary-subtle)] active:text-[var(--trine-color-primary-active)] active:translate-y-px',
41
- 'disabled:bg-transparent disabled:border-transparent disabled:text-[var(--trine-color-text-disabled)]',
38
+ 'text-primary',
39
+ 'hover:bg-accent hover:text-accent-foreground',
40
+ 'active:bg-accent active:text-accent-foreground active:translate-y-px',
41
+ 'disabled:bg-transparent disabled:border-transparent',
42
42
  ],
43
43
  danger: [
44
- 'bg-[var(--trine-color-danger-default)]',
45
- 'border-[var(--trine-color-danger-default)]',
46
- 'text-[var(--trine-color-danger-fg)]',
47
- 'shadow-[var(--trine-shadow-sm)]',
48
- 'hover:bg-[var(--trine-color-danger-hover)] hover:border-[var(--trine-color-danger-hover)]',
49
- 'active:bg-[var(--trine-color-danger-active)] active:border-[var(--trine-color-danger-active)] active:translate-y-px active:shadow-none',
50
- 'disabled:border-[var(--trine-color-danger-subtle)] disabled:bg-[var(--trine-color-danger-subtle)]',
44
+ 'bg-destructive',
45
+ 'border-destructive',
46
+ 'text-destructive-foreground',
47
+ 'shadow-[var(--shadow-sm)]',
48
+ 'hover:bg-destructive-hover hover:border-destructive-hover',
49
+ 'active:bg-destructive-active active:border-destructive-active active:translate-y-px active:shadow-none',
50
+ 'disabled:border-destructive-subtle disabled:bg-destructive-subtle',
51
51
  ],
52
52
  },
53
53
  size: {
@@ -65,19 +65,19 @@ export const buttonSkin = cva(
65
65
  variant: 'primary',
66
66
  loading: true,
67
67
  class: [
68
- '[&[aria-busy=true]]:border-[var(--trine-color-primary-default)]',
69
- '[&[aria-busy=true]]:bg-[var(--trine-color-primary-default)]',
70
- '[&[aria-busy=true]]:text-[var(--trine-color-primary-fg)]',
71
- '[&[aria-busy=true]]:shadow-[var(--trine-shadow-sm)]',
68
+ '[&[aria-busy=true]]:border-primary',
69
+ '[&[aria-busy=true]]:bg-primary',
70
+ '[&[aria-busy=true]]:text-primary-foreground',
71
+ '[&[aria-busy=true]]:shadow-[var(--shadow-sm)]',
72
72
  ],
73
73
  },
74
74
  {
75
75
  variant: 'secondary',
76
76
  loading: true,
77
77
  class: [
78
- '[&[aria-busy=true]]:border-[var(--trine-color-border)]',
79
- '[&[aria-busy=true]]:bg-[var(--trine-color-neutral-subtle)]',
80
- '[&[aria-busy=true]]:text-[var(--trine-color-text)]',
78
+ '[&[aria-busy=true]]:border-border',
79
+ '[&[aria-busy=true]]:bg-secondary',
80
+ '[&[aria-busy=true]]:text-secondary-foreground',
81
81
  ],
82
82
  },
83
83
  {
@@ -85,18 +85,18 @@ export const buttonSkin = cva(
85
85
  loading: true,
86
86
  class: [
87
87
  '[&[aria-busy=true]]:border-transparent',
88
- '[&[aria-busy=true]]:bg-[var(--trine-color-primary-subtle)]',
89
- '[&[aria-busy=true]]:text-[var(--trine-color-primary-default)]',
88
+ '[&[aria-busy=true]]:bg-accent',
89
+ '[&[aria-busy=true]]:text-accent-foreground',
90
90
  ],
91
91
  },
92
92
  {
93
93
  variant: 'danger',
94
94
  loading: true,
95
95
  class: [
96
- '[&[aria-busy=true]]:border-[var(--trine-color-danger-default)]',
97
- '[&[aria-busy=true]]:bg-[var(--trine-color-danger-default)]',
98
- '[&[aria-busy=true]]:text-[var(--trine-color-danger-fg)]',
99
- '[&[aria-busy=true]]:shadow-[var(--trine-shadow-sm)]',
96
+ '[&[aria-busy=true]]:border-destructive',
97
+ '[&[aria-busy=true]]:bg-destructive',
98
+ '[&[aria-busy=true]]:text-destructive-foreground',
99
+ '[&[aria-busy=true]]:shadow-[var(--shadow-sm)]',
100
100
  ],
101
101
  },
102
102
  ],
@@ -0,0 +1,125 @@
1
+ @import 'tailwindcss';
2
+
3
+ @custom-variant dark (&:is(.dark *));
4
+
5
+ @theme inline {
6
+ --color-background: var(--background);
7
+ --color-foreground: var(--foreground);
8
+ --color-card: var(--card);
9
+ --color-card-foreground: var(--card-foreground);
10
+ --color-popover: var(--popover);
11
+ --color-popover-foreground: var(--popover-foreground);
12
+ --color-primary: var(--primary);
13
+ --color-primary-foreground: var(--primary-foreground);
14
+ --color-primary-hover: var(--primary-hover);
15
+ --color-primary-active: var(--primary-active);
16
+ --color-primary-subtle: var(--primary-subtle);
17
+ --color-secondary: var(--secondary);
18
+ --color-secondary-foreground: var(--secondary-foreground);
19
+ --color-secondary-hover: var(--secondary-hover);
20
+ --color-secondary-active: var(--secondary-active);
21
+ --color-muted: var(--muted);
22
+ --color-muted-foreground: var(--muted-foreground);
23
+ --color-accent: var(--accent);
24
+ --color-accent-foreground: var(--accent-foreground);
25
+ --color-destructive: var(--destructive);
26
+ --color-destructive-foreground: var(--destructive-foreground);
27
+ --color-destructive-hover: var(--destructive-hover);
28
+ --color-destructive-active: var(--destructive-active);
29
+ --color-destructive-subtle: var(--destructive-subtle);
30
+ --color-border: var(--border);
31
+ --color-border-strong: var(--border-strong);
32
+ --color-input: var(--input);
33
+ --color-ring: var(--ring);
34
+ }
35
+
36
+ :root {
37
+ --background: oklch(0.9702 0.0015 253.83);
38
+ --foreground: oklch(0.2103 0.0015 253.83);
39
+
40
+ --card: oklch(1 0.0008 253.83);
41
+ --card-foreground: oklch(0.2103 0.0015 253.83);
42
+
43
+ --popover: oklch(1 0.0004 253.83 / 0.82);
44
+ --popover-foreground: oklch(0.2103 0.0015 253.83);
45
+
46
+ --primary: oklch(0.6204 0.195 253.83);
47
+ --primary-foreground: oklch(0.9911 0 0);
48
+ --primary-hover: oklch(0.5804 0.195 253.83);
49
+ --primary-active: oklch(0.5404 0.195 253.83);
50
+ --primary-subtle: oklch(0.9524 0.02 253.83);
51
+
52
+ --secondary: oklch(0.94 0.0015 253.83);
53
+ --secondary-foreground: oklch(0.2103 0.0015 253.83);
54
+ --secondary-hover: oklch(0.9373 0.0012 253.83);
55
+ --secondary-active: oklch(0.92 0.0015 253.83);
56
+
57
+ --muted: oklch(0.94 0.0015 253.83);
58
+ --muted-foreground: oklch(0.5517 0.003 253.83);
59
+
60
+ --accent: oklch(0.9524 0.02 253.83);
61
+ --accent-foreground: oklch(0.6204 0.195 253.83);
62
+
63
+ --destructive: oklch(0.6532 0.2335 25.74);
64
+ --destructive-foreground: oklch(0.9911 0 0);
65
+ --destructive-hover: oklch(0.6132 0.2335 25.74);
66
+ --destructive-active: oklch(0.5732 0.2335 25.74);
67
+ --destructive-subtle: oklch(0.9524 0.025 25.74);
68
+
69
+ --border: oklch(0.9 0.0015 253.83);
70
+ --border-strong: oklch(0.871 0.0015 253.83);
71
+ --input: oklch(0.9 0.0015 253.83);
72
+ --ring: oklch(0.6204 0.195 253.83);
73
+
74
+ --radius: 0.5rem;
75
+
76
+ --shadow-sm: 0 1px 2px 0 oklch(0.16 0.01 258 / 0.12);
77
+ --shadow-md: 0 10px 20px -10px oklch(0.16 0.01 258 / 0.22);
78
+ --shadow-lg: 0 20px 40px -20px oklch(0.16 0.01 258 / 0.28);
79
+
80
+ --duration-fast: 120ms;
81
+ --duration-base: 180ms;
82
+ --duration-slow: 280ms;
83
+ --ease-default: cubic-bezier(0.2, 0, 0, 1);
84
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
85
+ --ease-out: cubic-bezier(0, 0, 0.2, 1);
86
+ }
87
+
88
+ .dark {
89
+ --background: oklch(0.12 0.0015 253.83);
90
+ --foreground: oklch(0.9911 0.0015 253.83);
91
+
92
+ --card: oklch(0.2103 0.003 253.83);
93
+ --card-foreground: oklch(0.9911 0.0015 253.83);
94
+
95
+ --popover: oklch(0.2103 0.003 253.83 / 0.82);
96
+ --popover-foreground: oklch(0.9911 0.0015 253.83);
97
+
98
+ --primary: oklch(0.6204 0.195 253.83);
99
+ --primary-foreground: oklch(0.9911 0 0);
100
+ --primary-hover: oklch(0.6704 0.195 253.83);
101
+ --primary-active: oklch(0.5804 0.195 253.83);
102
+ --primary-subtle: oklch(0.274 0.02 253.83);
103
+
104
+ --secondary: oklch(0.274 0.0015 253.83);
105
+ --secondary-foreground: oklch(0.9911 0.0015 253.83);
106
+ --secondary-hover: oklch(0.3964 0.0015 253.83);
107
+ --secondary-active: oklch(0.257 0.0023 253.83);
108
+
109
+ --muted: oklch(0.274 0.0015 253.83);
110
+ --muted-foreground: oklch(0.705 0.003 253.83);
111
+
112
+ --accent: oklch(0.274 0.02 253.83);
113
+ --accent-foreground: oklch(0.6204 0.195 253.83);
114
+
115
+ --destructive: oklch(0.594 0.1973 24.63);
116
+ --destructive-foreground: oklch(0.9911 0 0);
117
+ --destructive-hover: oklch(0.634 0.1973 24.63);
118
+ --destructive-active: oklch(0.554 0.1973 24.63);
119
+ --destructive-subtle: oklch(0.257 0.02 24.63);
120
+
121
+ --border: oklch(0.28 0.0015 253.83);
122
+ --border-strong: oklch(0.3964 0.0015 253.83);
123
+ --input: oklch(0.28 0.0015 253.83);
124
+ --ring: oklch(0.6204 0.195 253.83);
125
+ }
@@ -1,102 +0,0 @@
1
- :root {
2
- --trine-color-primary-default: oklch(0.6204 0.195 253.83);
3
- --trine-color-primary-hover: oklch(0.5804 0.195 253.83);
4
- --trine-color-primary-active: oklch(0.5404 0.195 253.83);
5
- --trine-color-primary-subtle: oklch(0.9524 0.02 253.83);
6
- --trine-color-primary-fg: oklch(0.9911 0 0);
7
-
8
- --trine-color-danger-default: oklch(0.6532 0.2335 25.74);
9
- --trine-color-danger-hover: oklch(0.6132 0.2335 25.74);
10
- --trine-color-danger-active: oklch(0.5732 0.2335 25.74);
11
- --trine-color-danger-subtle: oklch(0.9524 0.025 25.74);
12
- --trine-color-danger-fg: oklch(0.9911 0 0);
13
-
14
- --trine-color-success-default: oklch(0.7329 0.1941 150.81);
15
- --trine-color-success-hover: oklch(0.6929 0.1941 150.81);
16
- --trine-color-success-active: oklch(0.6529 0.1941 150.81);
17
- --trine-color-success-subtle: oklch(0.9524 0.02 150.81);
18
- --trine-color-success-fg: oklch(0.2103 0.0059 150.81);
19
-
20
- --trine-color-warning-default: oklch(0.7819 0.159 72.33);
21
- --trine-color-warning-hover: oklch(0.7419 0.159 72.33);
22
- --trine-color-warning-active: oklch(0.7019 0.159 72.33);
23
- --trine-color-warning-subtle: oklch(0.9524 0.02 72.33);
24
- --trine-color-warning-fg: oklch(0.2103 0.0059 72.33);
25
-
26
- --trine-color-neutral-default: oklch(0.5517 0.003 253.83);
27
- --trine-color-neutral-hover: oklch(0.9373 0.0012 253.83);
28
- --trine-color-neutral-active: oklch(0.92 0.0015 253.83);
29
- --trine-color-neutral-subtle: oklch(0.94 0.0015 253.83);
30
- --trine-color-neutral-fg: oklch(0.2103 0.0059 253.83);
31
-
32
- --trine-color-bg: oklch(0.9702 0.0015 253.83);
33
- --trine-color-bg-elevated: oklch(1 0.0008 253.83);
34
- --trine-color-bg-overlay: oklch(1 0.0004 253.83 / 0.82);
35
- --trine-color-border: oklch(0.9 0.0015 253.83);
36
- --trine-color-border-strong: oklch(0.871 0.0015 253.83);
37
- --trine-color-ring: oklch(0.6204 0.195 253.83);
38
- --trine-color-text: oklch(0.2103 0.0015 253.83);
39
- --trine-color-text-subtle: oklch(0.5517 0.003 253.83);
40
- --trine-color-text-disabled: oklch(0.5517 0.003 253.83);
41
-
42
- --trine-radius-none: 0;
43
- --trine-radius-sm: 0.375rem;
44
- --trine-radius-md: 0.5rem;
45
- --trine-radius-lg: 0.75rem;
46
- --trine-radius-xl: 1rem;
47
- --trine-radius-2xl: 1.25rem;
48
- --trine-radius-full: 9999px;
49
-
50
- --trine-shadow-sm: 0 1px 2px 0 oklch(0.16 0.01 258 / 0.12);
51
- --trine-shadow-md: 0 10px 20px -10px oklch(0.16 0.01 258 / 0.22);
52
- --trine-shadow-lg: 0 20px 40px -20px oklch(0.16 0.01 258 / 0.28);
53
-
54
- --trine-duration-fast: 120ms;
55
- --trine-duration-base: 180ms;
56
- --trine-duration-slow: 280ms;
57
- --trine-ease-default: cubic-bezier(0.2, 0, 0, 1);
58
- --trine-ease-in: cubic-bezier(0.4, 0, 1, 1);
59
- --trine-ease-out: cubic-bezier(0, 0, 0.2, 1);
60
- }
61
-
62
- html.dark {
63
- --trine-color-primary-default: oklch(0.6204 0.195 253.83);
64
- --trine-color-primary-hover: oklch(0.6704 0.195 253.83);
65
- --trine-color-primary-active: oklch(0.5804 0.195 253.83);
66
- --trine-color-primary-subtle: oklch(0.274 0.02 253.83);
67
- --trine-color-primary-fg: oklch(0.9911 0 0);
68
-
69
- --trine-color-danger-default: oklch(0.594 0.1973 24.63);
70
- --trine-color-danger-hover: oklch(0.634 0.1973 24.63);
71
- --trine-color-danger-active: oklch(0.554 0.1973 24.63);
72
- --trine-color-danger-subtle: oklch(0.257 0.02 24.63);
73
- --trine-color-danger-fg: oklch(0.9911 0 0);
74
-
75
- --trine-color-success-default: oklch(0.7329 0.1941 150.81);
76
- --trine-color-success-hover: oklch(0.7729 0.1941 150.81);
77
- --trine-color-success-active: oklch(0.6929 0.1941 150.81);
78
- --trine-color-success-subtle: oklch(0.257 0.02 150.81);
79
- --trine-color-success-fg: oklch(0.2103 0.0059 150.81);
80
-
81
- --trine-color-warning-default: oklch(0.8203 0.1392 76.34);
82
- --trine-color-warning-hover: oklch(0.8603 0.1392 76.34);
83
- --trine-color-warning-active: oklch(0.7803 0.1392 76.34);
84
- --trine-color-warning-subtle: oklch(0.2721 0.02 76.34);
85
- --trine-color-warning-fg: oklch(0.2103 0.0059 76.34);
86
-
87
- --trine-color-neutral-default: oklch(0.705 0.003 253.83);
88
- --trine-color-neutral-hover: oklch(0.3964 0.0015 253.83);
89
- --trine-color-neutral-active: oklch(0.257 0.0023 253.83);
90
- --trine-color-neutral-subtle: oklch(0.274 0.0015 253.83);
91
- --trine-color-neutral-fg: oklch(0.9911 0.0015 253.83);
92
-
93
- --trine-color-bg: oklch(0.12 0.0015 253.83);
94
- --trine-color-bg-elevated: oklch(0.2103 0.003 253.83);
95
- --trine-color-bg-overlay: oklch(0.2103 0.003 253.83 / 0.82);
96
- --trine-color-border: oklch(0.28 0.0015 253.83);
97
- --trine-color-border-strong: oklch(0.3964 0.0015 253.83);
98
- --trine-color-ring: oklch(0.6204 0.195 253.83);
99
- --trine-color-text: oklch(0.9911 0.0015 253.83);
100
- --trine-color-text-subtle: oklch(0.705 0.003 253.83);
101
- --trine-color-text-disabled: oklch(0.705 0.003 253.83);
102
- }
@@ -1,58 +0,0 @@
1
- /* Consumer-safe styling/bootstrap baseline for copy-paste ownership delivery. */
2
- @import './tokens.css';
3
- @import 'tailwindcss';
4
-
5
- @source '../app';
6
-
7
- @theme {
8
- --color-trine-primary: var(--trine-color-primary-default);
9
- --color-trine-primary-hover: var(--trine-color-primary-hover);
10
- --color-trine-primary-active: var(--trine-color-primary-active);
11
- --color-trine-primary-subtle: var(--trine-color-primary-subtle);
12
- --color-trine-primary-fg: var(--trine-color-primary-fg);
13
-
14
- --color-trine-danger: var(--trine-color-danger-default);
15
- --color-trine-danger-hover: var(--trine-color-danger-hover);
16
- --color-trine-danger-active: var(--trine-color-danger-active);
17
- --color-trine-danger-subtle: var(--trine-color-danger-subtle);
18
- --color-trine-danger-fg: var(--trine-color-danger-fg);
19
-
20
- --color-trine-success: var(--trine-color-success-default);
21
- --color-trine-success-hover: var(--trine-color-success-hover);
22
- --color-trine-success-active: var(--trine-color-success-active);
23
- --color-trine-success-subtle: var(--trine-color-success-subtle);
24
- --color-trine-success-fg: var(--trine-color-success-fg);
25
-
26
- --color-trine-warning: var(--trine-color-warning-default);
27
- --color-trine-warning-hover: var(--trine-color-warning-hover);
28
- --color-trine-warning-active: var(--trine-color-warning-active);
29
- --color-trine-warning-subtle: var(--trine-color-warning-subtle);
30
- --color-trine-warning-fg: var(--trine-color-warning-fg);
31
-
32
- --color-trine-neutral: var(--trine-color-neutral-default);
33
- --color-trine-neutral-hover: var(--trine-color-neutral-hover);
34
- --color-trine-neutral-active: var(--trine-color-neutral-active);
35
- --color-trine-neutral-subtle: var(--trine-color-neutral-subtle);
36
- --color-trine-neutral-fg: var(--trine-color-neutral-fg);
37
-
38
- --color-trine-bg: var(--trine-color-bg);
39
- --color-trine-bg-elevated: var(--trine-color-bg-elevated);
40
- --color-trine-bg-overlay: var(--trine-color-bg-overlay);
41
- --color-trine-border: var(--trine-color-border);
42
- --color-trine-border-strong: var(--trine-color-border-strong);
43
- --color-trine-text: var(--trine-color-text);
44
- --color-trine-text-subtle: var(--trine-color-text-subtle);
45
- --color-trine-text-disabled: var(--trine-color-text-disabled);
46
-
47
- --radius-trine-none: var(--trine-radius-none);
48
- --radius-trine-sm: var(--trine-radius-sm);
49
- --radius-trine-md: var(--trine-radius-md);
50
- --radius-trine-lg: var(--trine-radius-lg);
51
- --radius-trine-xl: var(--trine-radius-xl);
52
- --radius-trine-2xl: var(--trine-radius-2xl);
53
- --radius-trine-full: var(--trine-radius-full);
54
-
55
- --shadow-trine-sm: var(--trine-shadow-sm);
56
- --shadow-trine-md: var(--trine-shadow-md);
57
- --shadow-trine-lg: var(--trine-shadow-lg);
58
- }