diff --git a/package.json b/package.json index 6cfa7ae..527f8ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lumyx/ui", - "version": "0.2.0", + "version": "0.3.0", "description": "The Lumyx design system: React components and Tailwind CSS v4 tokens.", "license": "MIT", "author": "Mathieu Chambaud", diff --git a/scripts/verify-ds.mjs b/scripts/verify-ds.mjs index 2d6d8fa..fdfb321 100644 --- a/scripts/verify-ds.mjs +++ b/scripts/verify-ds.mjs @@ -124,6 +124,39 @@ for (const size of uniqueScale) { } } +// 7-9. Les namespaces par defaut de Tailwind restent actifs (styles.css ne les remet pas a +// zero) : rien n'empeche a la compilation un `text-sm`, un `bg-white` ou un `duration-300`. +// Ces regles ferment la porte cote source. +const CODE_FILES = files.filter((f) => ['.ts', '.tsx', '.js'].includes(extname(f))); +function scanClasses(rule, pattern, accept = () => false) { + for (const f of CODE_FILES) { + readFileSync(f, 'utf8') + .split('\n') + .forEach((line, i) => { + for (const m of line.matchAll(pattern)) { + if (!accept(m)) fail(rule, `${rel(f)}:${i + 1} — ${m[0]}`); + } + }); + } +} + +// 7. Taille de texte sur l'echelle numerique (text-11 … text-44), jamais l'echelle Tailwind. +scanClasses('type-scale', /(? DURATIONS.has(m[1])); + if (failures.length) { console.error(`\n✗ ${failures.length} violation(s) des contraintes du design system\n`); failures.forEach((f) => console.error(' ' + f)); diff --git a/scripts/verify-ds.test.mjs b/scripts/verify-ds.test.mjs index 4949e9e..b38e9be 100644 --- a/scripts/verify-ds.test.mjs +++ b/scripts/verify-ds.test.mjs @@ -61,3 +61,47 @@ describe('verify-ds regle 2 — monospace par token', () => { expect(proc.exitCode).toBe(0); }); }); + +describe('verify-ds regle 7 — echelle typographique', () => { + test('accepte l echelle numerique', () => { + expect(runWith('export const A = () =>

;\n').code).toBe(0); + }); + + test('refuse l echelle Tailwind par defaut', () => { + const { code, output } = runWith('export const A = () =>

;\n'); + expect(code).toBe(1); + expect(output).toContain('type-scale'); + }); +}); + +describe('verify-ds regle 8 — couleurs par token', () => { + test('accepte les utilitaires semantiques', () => { + expect(runWith('export const A = () =>

;\n').code).toBe(0); + }); + + test('refuse black / white', () => { + const { code, output } = runWith('export const A = () =>

;\n'); + expect(code).toBe(1); + expect(output).toContain('no-raw-palette'); + }); + + test('refuse la palette Tailwind par defaut', () => { + expect(runWith('export const A = () =>

;\n').code).toBe(1); + }); +}); + +describe('verify-ds regle 9 — durees de motion', () => { + test('accepte 120 / 180 / 260', () => { + expect(runWith('export const A = () =>

;\n').code).toBe(0); + }); + + test('refuse une duree hors systeme', () => { + const { code, output } = runWith('export const A = () =>

;\n'); + expect(code).toBe(1); + expect(output).toContain('motion-duration'); + }); + + test('refuse une valeur arbitraire', () => { + expect(runWith('export const A = () =>

;\n').code).toBe(1); + }); +}); diff --git a/src/components/lumyx/peer-card.tsx b/src/components/lumyx/peer-card.tsx index 816f1e3..714cc26 100644 --- a/src/components/lumyx/peer-card.tsx +++ b/src/components/lumyx/peer-card.tsx @@ -16,7 +16,7 @@ export type Peer = { /** Peers are indigo. Rooms and sessions are coral. They are never blended. */ export function PeerCard({ peer, className }: { peer: Peer; className?: string }) { return ( -

+
diff --git a/src/components/lumyx/room-card.tsx b/src/components/lumyx/room-card.tsx index bc880c1..e741c01 100644 --- a/src/components/lumyx/room-card.tsx +++ b/src/components/lumyx/room-card.tsx @@ -17,7 +17,7 @@ export type Room = { export function RoomCard({ room, href, className }: { room: Room; href?: string; className?: string }) { const { Link } = useLumyxLink(); const body = ( -
+
diff --git a/src/components/lumyx/sidebar-nav.tsx b/src/components/lumyx/sidebar-nav.tsx index 7c49b41..537f2df 100644 --- a/src/components/lumyx/sidebar-nav.tsx +++ b/src/components/lumyx/sidebar-nav.tsx @@ -46,7 +46,7 @@ export function SidebarNav({ asChild isActive={active} className={cn( - "h-auto gap-2.5 rounded-sm px-2 py-1.5 text-13 transition-colors duration-[120ms]", + "h-auto gap-2.5 rounded-sm px-2 py-1.5 text-13 transition-colors duration-120", active ? "bg-accent-tint font-medium text-accent-text shadow-[inset_2px_0_0_var(--accent)] hover:bg-accent-tint hover:text-accent-text" : "text-body hover:bg-hover hover:text-strong", diff --git a/src/components/lumyx/time-series-chart.tsx b/src/components/lumyx/time-series-chart.tsx index 77cddeb..b94890b 100644 --- a/src/components/lumyx/time-series-chart.tsx +++ b/src/components/lumyx/time-series-chart.tsx @@ -28,7 +28,7 @@ export function TimeSeriesChart({ {ticks.map((t) => ( - + {Math.round(t)}{yUnit} @@ -38,7 +38,7 @@ export function TimeSeriesChart({ return ; })} {xLabels.map((l, i) => ( - + {l} ))} diff --git a/src/components/lumyx/video-tile.tsx b/src/components/lumyx/video-tile.tsx index 9fcc5ca..43ea788 100644 --- a/src/components/lumyx/video-tile.tsx +++ b/src/components/lumyx/video-tile.tsx @@ -22,7 +22,7 @@ export function VideoTile({ No stream
)} - + {peerId} {latencyMs != null ? {latencyMs}ms : null} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index e8b3ccf..c2a9f7f 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -8,14 +8,14 @@ import { cn } from '../../lib/utils'; * Focus is the 3px indigo ring, never a browser outline. */ const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors duration-[120ms] ease-[var(--ease-out)] outline-none focus-visible:border-accent focus-visible:shadow-[var(--ring-accent)] disabled:opacity-45 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4 [&_svg]:stroke-[1.75]", + "inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors duration-120 ease-[var(--ease-out)] outline-none focus-visible:border-accent focus-visible:shadow-[var(--ring-accent)] disabled:opacity-45 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4 [&_svg]:stroke-[1.75]", { variants: { variant: { primary: "bg-accent text-on-accent border border-transparent hover:bg-accent-hover shadow-[var(--shadow-xs)]", secondary: "bg-card text-strong border border-stroke hover:bg-hover shadow-[var(--shadow-xs)]", ghost: "bg-transparent text-body border border-transparent hover:bg-hover", - danger: "bg-danger text-white border border-transparent hover:brightness-95 focus-visible:shadow-[var(--ring-danger)]", + danger: "bg-danger text-on-accent border border-transparent hover:brightness-95 focus-visible:shadow-[var(--ring-danger)]", link: "bg-transparent text-accent-text underline-offset-2 hover:underline p-0 h-auto", }, size: { diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index bbe3dce..7ce78a3 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -21,7 +21,7 @@ function DialogContent({ className, children, ...props }: React.ComponentProps {children} - + Close diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index f0ca715..e9c279d 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) { data-slot="input" className={cn( "h-9 w-full rounded-sm border border-stroke bg-card px-3 text-13 text-strong shadow-[var(--shadow-xs)]", - "placeholder:text-faint transition-colors duration-[120ms]", + "placeholder:text-faint transition-colors duration-120", "outline-none focus-visible:border-accent focus-visible:shadow-[var(--ring-accent)]", "disabled:opacity-45 disabled:cursor-not-allowed", "aria-invalid:border-danger aria-invalid:focus-visible:shadow-[var(--ring-danger)]", diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx index 5482688..0e7a0c2 100644 --- a/src/components/ui/progress.tsx +++ b/src/components/ui/progress.tsx @@ -13,7 +13,7 @@ function Progress({ {...props} > diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index 5d9de65..f84f5d7 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -14,7 +14,7 @@ function SelectTrigger({ className, children, ...props }: React.ComponentProps ) diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 292679e..7226e1e 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -218,7 +218,7 @@ function Sidebar({