orbital-ds 0.6.0 → 0.7.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/package.json CHANGED
@@ -1,142 +1,142 @@
1
- {
2
- "name": "orbital-ds",
3
- "version": "0.6.0",
4
- "description": "Sistema de diseño genérico: estructura común + temas con personalidad (qratia, zyro) + ~60 componentes sobre Base UI y Tailwind v4, con contrato verificable y legible por agentes.",
5
- "keywords": [
6
- "design-system",
7
- "design-tokens",
8
- "theme",
9
- "tailwindcss",
10
- "base-ui",
11
- "shadcn",
12
- "react"
13
- ],
14
- "license": "MIT",
15
- "type": "module",
16
- "sideEffects": [
17
- "*.css"
18
- ],
19
- "files": [
20
- "dist",
21
- "scripts/doctor.mjs",
22
- "contract.json",
23
- "components.manifest.json",
24
- "docs",
25
- "AGENTS.md",
26
- "README.md",
27
- "LICENSE",
28
- "scripts/init.mjs",
29
- "scripts/manifest.mjs"
30
- ],
31
- "exports": {
32
- "./styles.css": "./dist/styles.css",
33
- "./themes/*.css": "./dist/themes/*.css",
34
- "./themes/*.md": "./dist/themes/*.md",
35
- "./themes/*.meta.json": "./dist/themes/*.meta.json",
36
- "./ui/*": {
37
- "types": "./dist/ui/*.d.ts",
38
- "import": "./dist/ui/*.js"
39
- },
40
- "./lib/*": {
41
- "types": "./dist/lib/*.d.ts",
42
- "import": "./dist/lib/*.js"
43
- },
44
- "./hooks/*": {
45
- "types": "./dist/hooks/*.d.ts",
46
- "import": "./dist/hooks/*.js"
47
- },
48
- "./package.json": "./package.json",
49
- "./contract.json": "./contract.json",
50
- "./components.manifest.json": "./components.manifest.json"
51
- },
52
- "scripts": {
53
- "build": "tsup && node scripts/build-css.mjs",
54
- "dev": "tsup --watch --onSuccess \"node scripts/copy-css.mjs\"",
55
- "typecheck": "tsc --noEmit",
56
- "verify": "pnpm check:ds && pnpm typecheck && pnpm build",
57
- "manifest": "node scripts/manifest.mjs",
58
- "check:ds": "node scripts/doctor.mjs"
59
- },
60
- "peerDependencies": {
61
- "@base-ui/react": ">=1.9",
62
- "class-variance-authority": ">=0.7",
63
- "clsx": ">=2",
64
- "lucide-react": ">=0.400",
65
- "react": ">=19",
66
- "react-dom": ">=19",
67
- "tailwind-merge": ">=3",
68
- "tailwindcss": ">=4.1",
69
- "tw-animate-css": ">=1.3",
70
- "next-themes": ">=0.4",
71
- "recharts": ">=3",
72
- "sonner": ">=2",
73
- "@shadcn/react": ">=0.3",
74
- "cmdk": ">=1.1",
75
- "date-fns": ">=4",
76
- "embla-carousel-react": ">=8.6",
77
- "input-otp": ">=1.5",
78
- "react-day-picker": ">=10",
79
- "react-resizable-panels": ">=4"
80
- },
81
- "peerDependenciesMeta": {
82
- "next-themes": {
83
- "optional": true
84
- },
85
- "recharts": {
86
- "optional": true
87
- },
88
- "sonner": {
89
- "optional": true
90
- },
91
- "@shadcn/react": {
92
- "optional": true
93
- },
94
- "cmdk": {
95
- "optional": true
96
- },
97
- "date-fns": {
98
- "optional": true
99
- },
100
- "embla-carousel-react": {
101
- "optional": true
102
- },
103
- "input-otp": {
104
- "optional": true
105
- },
106
- "react-day-picker": {
107
- "optional": true
108
- },
109
- "react-resizable-panels": {
110
- "optional": true
111
- }
112
- },
113
- "devDependencies": {
114
- "@base-ui/react": "^1.9.0",
115
- "@types/react": "^19.2.0",
116
- "@types/react-dom": "^19.2.0",
117
- "class-variance-authority": "^0.7.1",
118
- "clsx": "^2.1.1",
119
- "lucide-react": "^0.577.0",
120
- "next-themes": "^0.4.6",
121
- "react": "^19.2.0",
122
- "react-dom": "^19.2.0",
123
- "recharts": "3.8.0",
124
- "sonner": "^2.0.8",
125
- "tailwind-merge": "^3.0.2",
126
- "tailwindcss": "^4.1.18",
127
- "tsup": "^8.5.0",
128
- "tw-animate-css": "^1.3.6",
129
- "typescript": "^5.9.0",
130
- "@shadcn/react": "^0.3.1",
131
- "cmdk": "^1.1.1",
132
- "date-fns": "^4.4.0",
133
- "embla-carousel-react": "^8.6.0",
134
- "input-otp": "^1.5.0",
135
- "react-day-picker": "^10.0.2",
136
- "react-resizable-panels": "^4.14.4"
137
- },
138
- "bin": {
139
- "orbital-ds-doctor": "./scripts/doctor.mjs",
140
- "orbital-ds-init": "./scripts/init.mjs"
141
- }
142
- }
1
+ {
2
+ "name": "orbital-ds",
3
+ "version": "0.7.0",
4
+ "description": "Sistema de diseño genérico: estructura común + temas con personalidad (qratia, zyro) + ~60 componentes sobre Base UI y Tailwind v4, con contrato verificable y legible por agentes.",
5
+ "keywords": [
6
+ "design-system",
7
+ "design-tokens",
8
+ "theme",
9
+ "tailwindcss",
10
+ "base-ui",
11
+ "shadcn",
12
+ "react"
13
+ ],
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "sideEffects": [
17
+ "*.css"
18
+ ],
19
+ "files": [
20
+ "dist",
21
+ "scripts/doctor.mjs",
22
+ "contract.json",
23
+ "components.manifest.json",
24
+ "docs",
25
+ "AGENTS.md",
26
+ "README.md",
27
+ "LICENSE",
28
+ "scripts/init.mjs",
29
+ "scripts/manifest.mjs"
30
+ ],
31
+ "exports": {
32
+ "./styles.css": "./dist/styles.css",
33
+ "./themes/*.css": "./dist/themes/*.css",
34
+ "./themes/*.md": "./dist/themes/*.md",
35
+ "./themes/*.meta.json": "./dist/themes/*.meta.json",
36
+ "./ui/*": {
37
+ "types": "./dist/ui/*.d.ts",
38
+ "import": "./dist/ui/*.js"
39
+ },
40
+ "./lib/*": {
41
+ "types": "./dist/lib/*.d.ts",
42
+ "import": "./dist/lib/*.js"
43
+ },
44
+ "./hooks/*": {
45
+ "types": "./dist/hooks/*.d.ts",
46
+ "import": "./dist/hooks/*.js"
47
+ },
48
+ "./package.json": "./package.json",
49
+ "./contract.json": "./contract.json",
50
+ "./components.manifest.json": "./components.manifest.json"
51
+ },
52
+ "peerDependencies": {
53
+ "@base-ui/react": ">=1.9",
54
+ "class-variance-authority": ">=0.7",
55
+ "clsx": ">=2",
56
+ "lucide-react": ">=0.400",
57
+ "react": ">=19",
58
+ "react-dom": ">=19",
59
+ "tailwind-merge": ">=3",
60
+ "tailwindcss": ">=4.1",
61
+ "tw-animate-css": ">=1.3",
62
+ "next-themes": ">=0.4",
63
+ "recharts": ">=3",
64
+ "sonner": ">=2",
65
+ "@shadcn/react": ">=0.3",
66
+ "cmdk": ">=1.1",
67
+ "date-fns": ">=4",
68
+ "embla-carousel-react": ">=8.6",
69
+ "input-otp": ">=1.5",
70
+ "react-day-picker": ">=10",
71
+ "react-resizable-panels": ">=4"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "next-themes": {
75
+ "optional": true
76
+ },
77
+ "recharts": {
78
+ "optional": true
79
+ },
80
+ "sonner": {
81
+ "optional": true
82
+ },
83
+ "@shadcn/react": {
84
+ "optional": true
85
+ },
86
+ "cmdk": {
87
+ "optional": true
88
+ },
89
+ "date-fns": {
90
+ "optional": true
91
+ },
92
+ "embla-carousel-react": {
93
+ "optional": true
94
+ },
95
+ "input-otp": {
96
+ "optional": true
97
+ },
98
+ "react-day-picker": {
99
+ "optional": true
100
+ },
101
+ "react-resizable-panels": {
102
+ "optional": true
103
+ }
104
+ },
105
+ "devDependencies": {
106
+ "@base-ui/react": "^1.9.0",
107
+ "@types/react": "^19.2.0",
108
+ "@types/react-dom": "^19.2.0",
109
+ "class-variance-authority": "^0.7.1",
110
+ "clsx": "^2.1.1",
111
+ "lucide-react": "^0.577.0",
112
+ "next-themes": "^0.4.6",
113
+ "react": "^19.2.0",
114
+ "react-dom": "^19.2.0",
115
+ "recharts": "3.8.0",
116
+ "sonner": "^2.0.8",
117
+ "tailwind-merge": "^3.0.2",
118
+ "tailwindcss": "^4.1.18",
119
+ "tsup": "^8.5.0",
120
+ "tw-animate-css": "^1.3.6",
121
+ "typescript": "^5.9.0",
122
+ "@shadcn/react": "^0.3.1",
123
+ "cmdk": "^1.1.1",
124
+ "date-fns": "^4.4.0",
125
+ "embla-carousel-react": "^8.6.0",
126
+ "input-otp": "^1.5.0",
127
+ "react-day-picker": "^10.0.2",
128
+ "react-resizable-panels": "^4.14.4"
129
+ },
130
+ "bin": {
131
+ "orbital-ds-doctor": "./scripts/doctor.mjs",
132
+ "orbital-ds-init": "./scripts/init.mjs"
133
+ },
134
+ "scripts": {
135
+ "build": "tsup && node scripts/build-css.mjs",
136
+ "dev": "tsup --watch --onSuccess \"node scripts/copy-css.mjs\"",
137
+ "typecheck": "tsc --noEmit",
138
+ "verify": "pnpm check:ds && pnpm typecheck && pnpm build",
139
+ "manifest": "node scripts/manifest.mjs",
140
+ "check:ds": "node scripts/doctor.mjs"
141
+ }
142
+ }
@@ -147,6 +147,99 @@ for (const f of themeFiles) {
147
147
  if (meta.name !== t) errors.push(`tema ${t}: ${t}.meta.json tiene name «${meta.name}»`);
148
148
  }
149
149
 
150
+ // 1c) CONTRASTE de los textos de estado: info, danger, warning, success y neutral
151
+ // sobre su fondo suave (compuesto sobre el lienzo), mínimo WCAG AA 4.5:1, en claro y oscuro.
152
+ // No incluye `link` ni `primary-foreground`: dependen de colores de marca fijados por la
153
+ // identidad (p. ej. el azul de Twitter), y se documentan como limitación en la guía del tema.
154
+ {
155
+ const parseColor = (raw) => {
156
+ const v = raw.replace(/\s+/g, ' ').trim();
157
+ let m = v.match(/^#([0-9a-f]{3,8})$/i);
158
+ if (m) {
159
+ let h = m[1];
160
+ if (h.length === 3) h = [...h].map((c) => c + c).join('');
161
+ return { r: parseInt(h.slice(0, 2), 16), g: parseInt(h.slice(2, 4), 16), b: parseInt(h.slice(4, 6), 16), a: h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1 };
162
+ }
163
+ m = v.match(/^rgba?\(([^)]+)\)$/i);
164
+ if (m) { const p = m[1].split(/[ ,/]+/).filter(Boolean).map(Number); return { r: p[0], g: p[1], b: p[2], a: p[3] ?? 1 }; }
165
+ m = v.match(/^oklch\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+%?))?\s*\)$/i);
166
+ if (m) {
167
+ const L = m[1].endsWith('%') ? parseFloat(m[1]) / 100 : +m[1];
168
+ const C = +m[2], H = (+m[3] * Math.PI) / 180;
169
+ const a = C * Math.cos(H), b = C * Math.sin(H);
170
+ const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
171
+ const mm = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
172
+ const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
173
+ const lin = [4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s, -1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s, -0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s];
174
+ const g = (x) => Math.round(255 * Math.min(1, Math.max(0, x <= 0.0031308 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055)));
175
+ const al = m[4] ? (m[4].endsWith('%') ? parseFloat(m[4]) / 100 : +m[4]) : 1;
176
+ return { r: g(lin[0]), g: g(lin[1]), b: g(lin[2]), a: al };
177
+ }
178
+ return null;
179
+ };
180
+ const lum = ({ r, g, b }) => {
181
+ const f = (c) => { c /= 255; return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; };
182
+ return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
183
+ };
184
+ const over = (fg, bg) => ({ r: fg.r * fg.a + bg.r * (1 - fg.a), g: fg.g * fg.a + bg.g * (1 - fg.a), b: fg.b * fg.a + bg.b * (1 - fg.a), a: 1 });
185
+ const ratio = (x, y) => { const [p, q] = [lum(x), lum(y)].sort((u, w) => w - u); return (p + 0.05) / (q + 0.05); };
186
+ const varsDe = (css, selector) => {
187
+ const m = new RegExp(`(^|\\n)${selector}\\s*\\{`).exec(css);
188
+ if (!m) return {};
189
+ let i = m.index + m[0].length, d = 1, j = i;
190
+ while (d && j < css.length) { if (css[j] === '{') d++; else if (css[j] === '}') d--; j++; }
191
+ const out = {};
192
+ for (const mm of css.slice(i, j - 1).matchAll(/--([\w-]+):\s*([^;]+);/g)) out[mm[1]] = mm[2];
193
+ return out;
194
+ };
195
+ for (const f of themeFiles) {
196
+ const tema = f.replace(/\.css$/, '');
197
+ const css = stripComments(read(join(themesDir, f)));
198
+ const claro = varsDe(css, ':root');
199
+ for (const [piel, vars] of [['claro', claro], ['oscuro', { ...claro, ...varsDe(css, '\\.dark') }]]) {
200
+ const lienzo = parseColor(vars.background ?? '#fff');
201
+ if (!lienzo) continue;
202
+ for (const estado of ['info', 'danger', 'warning', 'success', 'neutral']) {
203
+ const fg = vars[estado] && parseColor(vars[estado]);
204
+ const suave = vars[`${estado}-soft`] && parseColor(vars[`${estado}-soft`]);
205
+ if (!fg || !suave) continue;
206
+ const fondo = over(suave, over(lienzo, { r: 255, g: 255, b: 255, a: 1 }));
207
+ const r = ratio(over(fg, fondo), fondo);
208
+ if (r < 4.5) errors.push(`tema ${tema} (${piel}): ${estado} sobre ${estado}-soft = ${r.toFixed(2)}:1 (mínimo 4.5:1)`);
209
+ }
210
+
211
+ // Pares principales de texto sobre su superficie. AA (4.5:1) es el mínimo exigible; las
212
+ // excepciones deben estar declaradas en `<tema>.meta.json` → `a11y.exceptions` ("fg/bg").
213
+ // AAA (7:1) es el objetivo del texto del cuerpo: solo avisa, y solo con `--aaa`.
214
+ const PARES = [
215
+ ['foreground', 'background'], ['card-foreground', 'card'], ['popover-foreground', 'popover'],
216
+ ['primary-foreground', 'primary'], ['secondary-foreground', 'secondary'],
217
+ ['muted-foreground', 'background'], ['muted-foreground', 'card'],
218
+ ['sidebar-foreground', 'sidebar'], ['sidebar-primary-foreground', 'sidebar-primary'],
219
+ ['destructive-foreground', 'destructive'],
220
+ ];
221
+ let excepciones = [];
222
+ try { excepciones = JSON.parse(read(join(themesDir, `${tema}.meta.json`))).a11y?.exceptions ?? []; } catch { /* sin ficha: lo avisa 1b */ }
223
+ const blanco = { r: 255, g: 255, b: 255, a: 1 };
224
+ for (const [fgName, bgName] of PARES) {
225
+ const fgRaw = vars[fgName] && parseColor(vars[fgName]);
226
+ const bgRaw = vars[bgName] && parseColor(vars[bgName]);
227
+ if (!fgRaw || !bgRaw) continue;
228
+ const bg = over(bgRaw, over(lienzo, blanco));
229
+ const r = ratio(over(fgRaw, bg), bg);
230
+ const par = `${fgName}/${bgName}`;
231
+ if (r < 4.5) {
232
+ const msg = `tema ${tema} (${piel}): ${par} = ${r.toFixed(2)}:1 (mínimo AA 4.5:1)`;
233
+ if (excepciones.includes(par)) warnings.push(`${msg} — excepción declarada en la ficha`);
234
+ else errors.push(`${msg}; corrígelo o decláralo en ${tema}.meta.json → a11y.exceptions`);
235
+ } else if (process.argv.includes('--aaa') && r < 7 && (par === 'foreground/background' || par === 'card-foreground/card')) {
236
+ warnings.push(`tema ${tema} (${piel}): ${par} = ${r.toFixed(2)}:1 (objetivo AAA 7:1 para el texto del cuerpo)`);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+
150
243
  // 4) fugas desde el tema base
151
244
  const base = contract.baseTheme;
152
245
  if (base && themeInfo[base]) {
package/scripts/init.mjs CHANGED
File without changes