diff --git a/deno.json b/deno.json index 06f0730d9..0c40cd290 100644 --- a/deno.json +++ b/deno.json @@ -48,6 +48,7 @@ "docs:check-version-anchors": "deno run --allow-read tools/check-version-anchors.ts", "docs:truth": "deno task docs:check-public && deno task docs:check-current && deno task docs:check-strategy && deno task docs:check-version-anchors && deno task release:evidence:check && deno task www:check-current-truth", "www:check-current-truth": "deno run --allow-read tools/check-www-current-truth.ts", + "www:check-theme-tokens": "deno run --allow-read tools/check-www-theme-tokens.ts", "www:check-artifact-truth": "deno run --allow-read tools/check-www-current-truth.ts --artifacts", "package-surface:check": "deno run --allow-read --allow-env tools/check-package-surface.ts", "interface:snapshot": "deno run --allow-read --allow-env tools/check-public-interface-snapshot.ts", diff --git a/tools/autoflow/policy.ts b/tools/autoflow/policy.ts index 939609cd3..9cb3860a9 100644 --- a/tools/autoflow/policy.ts +++ b/tools/autoflow/policy.ts @@ -126,6 +126,12 @@ export const GATES: readonly GateDefinition[] = [ tiers: ['ci', 'release'], triggers: [/^docs\//, /^README/, /^www\/app\/routes\//], }, + { + name: 'www:check-theme-tokens', + command: ['deno', 'task', 'www:check-theme-tokens'], + tiers: ['dev', 'push', 'ci', 'release'], + triggers: [/^www\/app\//, /^www\/vite\.config\.ts$/, /^packages\/ui\/src\/open-props-tokens/], + }, { name: 'docs:check-version-anchors', command: ['deno', 'task', 'docs:check-version-anchors'], diff --git a/tools/check-www-theme-tokens.test.ts b/tools/check-www-theme-tokens.test.ts new file mode 100644 index 000000000..ef4fd21ec --- /dev/null +++ b/tools/check-www-theme-tokens.test.ts @@ -0,0 +1,52 @@ +import { assertEquals } from '@std/assert'; +import { findThemeTokenFailures } from './check-www-theme-tokens.ts'; + +Deno.test('theme-token gate catches hex, font-family and font-size literals', () => { + const lines = [ + ' .vinyl { background:#18151e; }', + ' code { font-family: "JetBrains Mono", monospace; }', + ' font-size: 12px;', + ' font-size: .75rem;', + ]; + const failures = findThemeTokenFailures('x.tsx', lines); + assertEquals(failures.map((f) => f.rule), [ + 'hex-literal', + 'font-family-literal', + 'font-size-literal', + 'font-size-literal', + ]); +}); + +Deno.test('theme-token gate catches short hex only in CSS contexts', () => { + const violations = findThemeTokenFailures('x.tsx', [ + 'background: color-mix(in srgb, #fff 18%, transparent);', + ]); + assertEquals(violations.length, 1); + const prose = findThemeTokenFailures('x.tsx', [ + 'External adopter #390 and continued browser evidence.', + '#390', + ]); + assertEquals(prose.length, 0); +}); + +Deno.test('theme-token gate skips generated data files', () => { + const failures = findThemeTokenFailures('www/app/data/_generated-blog-data.ts', [ + '"content": "themeColor: \'#000000\', font-size: 12px;"', + ]); + // The caller-level exclusion (path filter) keeps generated content out; + // the pure function still flags it when asked directly. + assertEquals(failures.length > 0, true); +}); + +Deno.test('theme-token gate accepts tokens, inherit and fluid clamp()', () => { + const failures = findThemeTokenFailures('x.tsx', [ + 'color: var(--text-primary);', + 'background: color-mix(in srgb, var(--violet-5) 18%, transparent);', + 'font-family: var(--font-mono);', + 'font-family: inherit;', + 'font-size: var(--font-size-00);', + 'font-size: clamp(4.1rem, 10vw, 10.5rem);', + 'font-size: clamp(var(--font-size-7), 10vw, var(--font-size-8));', + ]); + assertEquals(failures.length, 0); +}); diff --git a/tools/check-www-theme-tokens.ts b/tools/check-www-theme-tokens.ts new file mode 100644 index 000000000..6d7c699b2 --- /dev/null +++ b/tools/check-www-theme-tokens.ts @@ -0,0 +1,81 @@ +/** + * www theme-token gate: theme values in the site must come from open-props + * tokens (packages/ui/src/open-props-tokens.css) and the www alias layer + * (www/vite.config.ts), never from hardcoded literals. + * + * Rules for sources under www/app/ and www/islands/: + * 1. No hex color literals. 6/8-digit forms always fail; 3/4-digit forms + * fail only on lines carrying a CSS property keyword, so issue + * references like `#390` in prose stay legal. + * 2. No `font-family` declarations that bypass var(); `inherit` is allowed. + * 3. No `font-size` literals in px/rem/em outside var(); clamp() fluid + * typography is allowed. + * + * Token definitions belong in www/vite.config.ts (site aliases) or + * packages/ui/src/open-props-tokens.css (source of truth). + */ + +import { walk } from '@std/fs/walk'; + +const SCAN_ROOTS = ['www/app']; +const SOURCE = /\.(ts|tsx)$/; +const HEX_LONG = /#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/; +const HEX_SHORT = /#(?:[0-9a-fA-F]{3,4})\b/; +const CSS_KEYWORD = /color|background|border|shadow|fill|stroke|gradient|outline/i; +const FONT_FAMILY = /font-family\s*:\s*([^;]+);/; +const FONT_SIZE_LITERAL = /font-size\s*:\s*[0-9.]+(?:px|rem|em)\b/; + +export interface ThemeTokenFailure { + file: string; + line: number; + rule: string; + text: string; +} + +export function findThemeTokenFailures( + file: string, + lines: string[], +): ThemeTokenFailure[] { + const failures: ThemeTokenFailure[] = []; + for (let i = 0; i < lines.length; i++) { + const text = lines[i]; + if (HEX_LONG.test(text) || (HEX_SHORT.test(text) && CSS_KEYWORD.test(text))) { + failures.push({ file, line: i + 1, rule: 'hex-literal', text: text.trim() }); + } + const family = FONT_FAMILY.exec(text); + if (family && !family[1].includes('var(') && !family[1].includes('inherit')) { + failures.push({ file, line: i + 1, rule: 'font-family-literal', text: text.trim() }); + } + if (FONT_SIZE_LITERAL.test(text)) { + failures.push({ file, line: i + 1, rule: 'font-size-literal', text: text.trim() }); + } + } + return failures; +} + +async function main(): Promise { + const failures: ThemeTokenFailure[] = []; + for (const root of SCAN_ROOTS) { + for await (const entry of walk(root, { exts: ['.ts', '.tsx'] })) { + if (!SOURCE.test(entry.path)) continue; + if (entry.path.includes('/data/_generated-')) continue; + const text = await Deno.readTextFile(entry.path); + failures.push(...findThemeTokenFailures(entry.path, text.split('\n'))); + } + } + if (failures.length > 0) { + console.error('www theme token check failed:'); + for (const failure of failures) { + console.error(`- ${failure.file}:${failure.line} [${failure.rule}] ${failure.text}`); + } + console.error( + 'Theme values must come from open-props tokens or the www/vite.config.ts alias layer.', + ); + Deno.exit(1); + } + console.log('www theme token check passed.'); +} + +if (import.meta.main) { + await main(); +} diff --git a/www/app/routes/404.tsx b/www/app/routes/404.tsx index 27cab35df..9bcaac639 100644 --- a/www/app/routes/404.tsx +++ b/www/app/routes/404.tsx @@ -84,10 +84,10 @@ styles.replaceSync(` padding: 80px 20px 64px; } .title { - font-size: 64px; + font-size: var(--font-size-7); } .subtitle { - font-size: 20px; + font-size: var(--font-size-2); } } `); diff --git a/www/app/routes/architecture/architecture.tsx b/www/app/routes/architecture/architecture.tsx index 70da9daa1..c0309d25a 100644 --- a/www/app/routes/architecture/architecture.tsx +++ b/www/app/routes/architecture/architecture.tsx @@ -4,6 +4,7 @@ export const tagName = 'engine-architecture'; import { OpenElement, StyleSheet } from '@openelement/element'; import '@openelement/ui/open-badge'; import '@openelement/ui/open-card'; +import '@openelement/ui/open-code-block'; import { OPENELEMENT_VERSION } from '../../data/version.ts'; import '@openelement/site-ui/open-section-frame.tsx'; import '@openelement/site-ui/open-page-hero.tsx'; @@ -15,14 +16,14 @@ pageSheet.replaceSync(` * { box-sizing:border-box; } .eyebrow { display: flex; flex-wrap: wrap; gap: var(--size-2); margin-bottom: 20px; } h1 { margin:0; max-width:760px; color:var(--text); font-size:clamp(3.5rem,7vw,7rem); line-height:.88; letter-spacing:-.07em; } - h2 { margin: 0; color: var(--text); font-size: 34px; line-height: 1.12; letter-spacing: 0; } + h2 { margin: 0; color: var(--text); font-size: var(--font-size-display-md); line-height: 1.12; letter-spacing: 0; } h3 { margin: 0 0 var(--size-2); color: var(--text); } p { color: var(--text-secondary); line-height: var(--line-height-relaxed); } .lede { margin: 20px 0 0; font-size: var(--font-size-subhead); max-width: 650px; } .artifact, .layer-map { border:1px solid color-mix(in srgb,var(--color-border) 72%,var(--brand)); border-radius:var(--radius-2); overflow:hidden; background:color-mix(in srgb,var(--surface-1) 82%,transparent); box-shadow:inset 0 1px 0 var(--edge-highlight),0 28px 90px color-mix(in srgb,var(--violet-10) 24%,transparent); backdrop-filter:blur(18px); } .artifact-head { display: flex; justify-content: space-between; gap: var(--size-3); padding: 14px var(--size-4); border-bottom: 1px solid var(--color-border); font-size: var(--font-size-0); color: var(--text-muted); } pre { margin: 0; padding: var(--size-4); overflow-x: auto; background: var(--code-bg); color: var(--code-text); font-size: var(--font-size-0); line-height: 1.65; } - code { font-family: "JetBrains Mono", monospace; } + code { font-family: var(--font-mono); } .layer { display: grid; grid-template-columns: 170px 1fr 180px; gap: var(--size-4); padding: 14px var(--size-4); border-bottom: 1px solid var(--color-border); align-items: start; } .layer:last-child { border-bottom: 0; } .layer strong { color: var(--text); font-size: var(--font-size-1); } @@ -33,7 +34,7 @@ pageSheet.replaceSync(` .gate strong { color: var(--color-brand); font-size: var(--font-size-1); } .gate span { color: var(--text-secondary); font-size: var(--font-size-0); line-height: 1.55; } .nav-row { display:flex; flex-wrap:wrap; gap:10px; width:min(1180px,calc(100% - 4rem)); margin:var(--size-8) auto 0; } - @media (max-width: 900px) { .cards, .gate-grid { grid-template-columns: 1fr; } .layer { grid-template-columns: 1fr; gap: var(--size-2); } h1 { font-size: 42px; line-height: 1.06; } h2 { font-size: 28px; } } + @media (max-width: 900px) { .cards, .gate-grid { grid-template-columns: 1fr; } .layer { grid-template-columns: 1fr; gap: var(--size-2); } h1 { font-size: var(--font-size-display-lg); line-height: 1.06; } h2 { font-size: var(--font-size-display-sm); } } @media (max-width: 560px) { .nav-row{width:calc(100% - 2rem)} .gate { grid-template-columns: 1fr; display: grid; } } `); @@ -62,7 +63,9 @@ export class ArchitecturePage extends OpenElement { package graph {OPENELEMENT_VERSION} published line -
{PACKAGE_GRAPH}
+ +
{PACKAGE_GRAPH}
+
diff --git a/www/app/routes/architecture/islands-deep.tsx b/www/app/routes/architecture/islands-deep.tsx index b6d220204..2ff59d973 100644 --- a/www/app/routes/architecture/islands-deep.tsx +++ b/www/app/routes/architecture/islands-deep.tsx @@ -11,7 +11,7 @@ const routeSheet = new StyleSheet(); routeSheet.replaceSync( pageStyles + ` .layer-card { padding: 20px var(--size-6); margin: var(--size-4) 0; border-left: 2px solid var(--color-border); background: var(--surface-1); border-radius: 0 3px 3px 0; } - .layer-card .layer-tag { font-size: 11px; font-weight: var(--font-weight-5); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.25rem; } + .layer-card .layer-tag { font-size: var(--font-size-overline); font-weight: var(--font-weight-5); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.25rem; } .layer-card h3 { margin: 0 0 var(--size-2); } .strategy-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-4); margin: var(--size-4) 0 var(--size-6); } .strategy-item { padding: var(--size-4) 20px; border: 0.5px solid var(--color-border); border-radius: var(--radius-xs); background: var(--surface-1); } diff --git a/www/app/routes/blog/[slug].tsx b/www/app/routes/blog/[slug].tsx index 7667342a4..02ea6109d 100644 --- a/www/app/routes/blog/[slug].tsx +++ b/www/app/routes/blog/[slug].tsx @@ -52,7 +52,7 @@ routeSheet.replaceSync( .blog-tags { display: flex; gap: 0.375rem; flex-wrap: wrap; margin-bottom: var(--size-4); } .blog-tag { font-size: var(--font-size-00); font-weight: var(--font-weight-6); text-transform: uppercase; letter-spacing: var(--font-letterspacing-2); padding: 0.125rem 0.375rem; border-radius: 2px; background: var(--bg-surface); border: 0.5px solid var(--border); color: var(--text-secondary); } .blog-content { font-size: var(--font-size-3); line-height: var(--font-lineheight-4); color: var(--text-secondary); } - .blog-content h2 { margin-top: var(--size-10); color: var(--text-primary); font-size: 1.125rem; font-weight: var(--font-weight-6); } + .blog-content h2 { margin-top: var(--size-10); color: var(--text-primary); font-size: var(--font-size-article-title); font-weight: var(--font-weight-6); } .blog-content h3 { margin-top: var(--size-8); color: var(--text-primary); font-size: var(--font-size-4); font-weight: var(--font-weight-6); } .blog-content p { margin: var(--size-3) 0; } .blog-content ul, .blog-content ol { padding-left: var(--size-6); margin: var(--size-3) 0; } @@ -63,7 +63,7 @@ routeSheet.replaceSync( .blog-content pre code { background: none; padding: 0; font-size: var(--font-size-0); line-height: 1.6; } .blog-content table { width: 100%; border-collapse: collapse; margin: var(--size-4) 0; font-size: var(--font-size-1); } .blog-content th, .blog-content td { padding: var(--size-2) var(--size-3); text-align: left; border-bottom: 0.5px solid var(--border); } - .blog-content th { background: var(--bg-surface); color: var(--text-secondary); font-weight: var(--font-weight-6); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: var(--font-letterspacing-2); } + .blog-content th { background: var(--bg-surface); color: var(--text-secondary); font-weight: var(--font-weight-6); font-size: var(--font-size-overline); text-transform: uppercase; letter-spacing: var(--font-letterspacing-2); } .blog-content a { color: var(--brand); text-decoration: none; } .blog-content a:hover { text-decoration: underline; } .blog-content hr { border: none; border-top: 0.5px solid var(--border); margin: var(--size-8) 0; } diff --git a/www/app/routes/index/index.tsx b/www/app/routes/index/index.tsx index 5e65915ca..7755a7327 100644 --- a/www/app/routes/index/index.tsx +++ b/www/app/routes/index/index.tsx @@ -24,7 +24,7 @@ sheet.replaceSync(` .actions { display:flex; flex-wrap:wrap; gap:.8rem; align-items:center; } .action { display:inline-flex; align-items:center; justify-content:center; min-height:3.25rem; padding:0 1.2rem; border:1px solid color-mix(in srgb,var(--text-primary) 42%,transparent); border-radius:999px; color:var(--text-primary); background:color-mix(in srgb,var(--bg-base) 56%,transparent); font-weight:700; text-decoration:none; transition:transform .22s var(--ease-3),background .22s var(--ease-3),border-color .22s var(--ease-3); } .action:hover,.action:focus-visible { transform:translateY(-3px); border-color:var(--violet-7); background:color-mix(in srgb,var(--violet-5) 22%,var(--bg-base)); outline:none; } - .action.primary { border-color:var(--violet-7); background:linear-gradient(120deg,var(--violet-6),var(--violet-4)); color:#fff; box-shadow:0 16px 45px color-mix(in srgb,var(--violet-6) 42%,transparent); } + .action.primary { border-color:var(--violet-7); background:linear-gradient(120deg,var(--violet-6),var(--violet-4)); color:var(--vinyl-highlight); box-shadow:0 16px 45px color-mix(in srgb,var(--violet-6) 42%,transparent); } .version { display:block; margin-block-start:1.1rem; color:var(--text-muted); font-family:var(--font-mono); font-size:var(--font-size-00); } .aperture-stage { position:relative; aspect-ratio:1; width:min(100%,680px); margin-inline:auto; transform-style:preserve-3d; } .aperture { position:absolute; inset:10%; display:grid; place-items:center; border:1px solid color-mix(in srgb,var(--violet-7) 66%,transparent); border-radius:50%; background:radial-gradient(circle at 31% 28%,color-mix(in srgb,var(--violet-5) 32%,transparent),transparent 31%),radial-gradient(circle at 68% 70%,color-mix(in srgb,var(--violet-9) 32%,transparent),transparent 45%); box-shadow:inset 0 0 90px color-mix(in srgb,var(--violet-7) 18%,transparent),0 0 120px color-mix(in srgb,var(--violet-7) 22%,transparent); } @@ -33,9 +33,9 @@ sheet.replaceSync(` .mark { position:relative; z-index:1; color:var(--text-primary); font-family:var(--font-mono); font-size:clamp(3rem,7vw,6.8rem); font-weight:800; letter-spacing:-.09em; text-shadow:0 0 36px color-mix(in srgb,var(--violet-6) 55%,transparent); } .mark b { color:var(--violet-8); } .orbit { position:absolute; inset:0; border:1px dashed color-mix(in srgb,var(--violet-6) 24%,transparent); border-radius:50%; animation:reverse-orbit 20s linear infinite; } - .node { position:absolute; display:grid; place-items:center; width:3.2rem; aspect-ratio:1; border:1px solid color-mix(in srgb,var(--violet-6) 62%,transparent); border-radius:50%; background:color-mix(in srgb,var(--bg-base) 70%,transparent); color:var(--violet-8); font-family:var(--font-mono); font-size:.72rem; box-shadow:0 0 22px color-mix(in srgb,var(--violet-7) 24%,transparent); } + .node { position:absolute; display:grid; place-items:center; width:3.2rem; aspect-ratio:1; border:1px solid color-mix(in srgb,var(--violet-6) 62%,transparent); border-radius:50%; background:color-mix(in srgb,var(--bg-base) 70%,transparent); color:var(--violet-8); font-family:var(--font-mono); font-size:var(--font-size-00); box-shadow:0 0 22px color-mix(in srgb,var(--violet-7) 24%,transparent); } .node.one { inset:9% auto auto 13%; }.node.two { inset:auto 5% 17% auto; }.node.three { inset:44% auto auto -3%; } - .scroll-cue { position:absolute; inset:auto 0 1.5rem; z-index:1; display:flex; justify-content:center; gap:.7rem; color:var(--text-muted); font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; } + .scroll-cue { position:absolute; inset:auto 0 1.5rem; z-index:1; display:flex; justify-content:center; gap:.7rem; color:var(--text-muted); font-family:var(--font-mono); font-size:var(--font-size-00); letter-spacing:.1em; text-transform:uppercase; } .scroll-cue::after { content:"↓"; color:var(--violet-8); animation:bob 1.6s ease-in-out infinite; } .scenes { position:relative; } .scene { position:relative; display:grid; grid-template-columns:minmax(0,.45fr) minmax(0,.55fr); align-items:center; min-height:100svh; gap:clamp(2rem,8vw,10rem); padding:clamp(4.5rem,12vh,9rem) clamp(1.5rem,8vw,10rem); border-block-end:1px solid color-mix(in srgb,var(--violet-6) 25%,var(--border)); } @@ -43,21 +43,21 @@ sheet.replaceSync(` .scene-art { position:relative; min-height:min(62vw,660px); display:grid; place-items:center; perspective:1000px; } .component-stack { display:grid; width:min(100%,560px); transform:rotateX(8deg) rotateY(-12deg); transform-style:preserve-3d; } .component { position:relative; display:grid; gap:.8rem; padding:1.4rem; border:1px solid color-mix(in srgb,var(--violet-6) 45%,var(--border)); background:linear-gradient(135deg,color-mix(in srgb,var(--violet-4) 18%,var(--bg-elevated)),color-mix(in srgb,var(--bg-base) 84%,transparent)); box-shadow:0 25px 65px color-mix(in srgb,var(--violet-11) 45%,transparent); font-family:var(--font-mono); transform:translateZ(var(--depth,0)); } - .component:nth-child(2) { margin:1.4rem 0 0 3rem; --depth:45px; }.component:nth-child(3) { margin:1.4rem 0 0 6rem; --depth:90px; }.component strong { color:var(--violet-8); }.component span { color:var(--text-secondary); font-size:.84rem; } + .component:nth-child(2) { margin:1.4rem 0 0 3rem; --depth:45px; }.component:nth-child(3) { margin:1.4rem 0 0 6rem; --depth:90px; }.component strong { color:var(--violet-8); }.component span { color:var(--text-secondary); font-size:var(--font-size-tiny); } .pulse-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:1rem; width:min(100%,560px); }.island { display:grid; place-items:center; min-height:150px; border:1px solid color-mix(in srgb,var(--violet-6) 42%,var(--border)); border-radius:1.1rem; background:radial-gradient(circle at 50% 50%,color-mix(in srgb,var(--violet-6) 25%,transparent),transparent 60%),var(--bg-elevated); color:var(--text-primary); font-family:var(--font-mono); animation:pulse 3s ease-in-out infinite; }.island:nth-child(2){animation-delay:-1s}.island:nth-child(3){animation-delay:-2s}.island small{color:var(--violet-8)} - .output { display:grid; grid-template-columns:repeat(3,1fr); gap:1rem; width:min(100%,620px); }.target { min-height:230px; display:grid; align-content:space-between; padding:1.25rem; border:1px solid color-mix(in srgb,var(--violet-6) 43%,var(--border)); background:linear-gradient(145deg,color-mix(in srgb,var(--violet-5) 18%,transparent),var(--bg-elevated)); box-shadow:0 18px 50px color-mix(in srgb,var(--violet-10) 38%,transparent); }.target svg{width:42px;color:var(--violet-8)}.target strong{font-size:1.1rem}.target span{color:var(--text-secondary);font-family:var(--font-mono);font-size:.75rem} + .output { display:grid; grid-template-columns:repeat(3,1fr); gap:1rem; width:min(100%,620px); }.target { min-height:230px; display:grid; align-content:space-between; padding:1.25rem; border:1px solid color-mix(in srgb,var(--violet-6) 43%,var(--border)); background:linear-gradient(145deg,color-mix(in srgb,var(--violet-5) 18%,transparent),var(--bg-elevated)); box-shadow:0 18px 50px color-mix(in srgb,var(--violet-10) 38%,transparent); }.target svg{width:42px;color:var(--violet-8)}.target strong{font-size:var(--font-size-lede)}.target span{color:var(--text-secondary);font-family:var(--font-mono);font-size:var(--font-size-00)} .final { grid-template-columns:1fr; text-align:center; min-height:82svh; }.final .scene-copy { max-width:800px; margin:auto; }.command { display:flex; align-items:center; justify-content:space-between; gap:1rem; width:min(100%,680px); margin:2.5rem auto; padding:1rem 1.1rem 1rem 1.35rem; border:1px solid color-mix(in srgb,var(--violet-6) 52%,var(--border)); border-radius:999px; background:color-mix(in srgb,var(--bg-elevated) 72%,transparent); color:var(--violet-8); font-family:var(--font-mono); text-align:left; }.command code{overflow:auto;white-space:nowrap}.final-actions{justify-content:center} - .reference { width:min(1180px,calc(100% - 3rem)); margin:0 auto; padding:7rem 0; }.reference header{display:flex;justify-content:space-between;gap:2rem;align-items:end;margin-block-end:2rem}.reference h2{font-size:clamp(2.2rem,4vw,4.5rem);letter-spacing:-.06em;line-height:.92}.reference p{max-width:500px;color:var(--text-secondary);line-height:1.5}.links{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--border)}.links a{display:grid;gap:.6rem;min-height:180px;padding:1.4rem;border-inline-end:1px solid var(--border);color:inherit;text-decoration:none;background:color-mix(in srgb,var(--bg-elevated) 55%,transparent);transition:background .2s ease,transform .2s ease}.links a:last-child{border:0}.links a:hover{background:color-mix(in srgb,var(--violet-5) 18%,var(--bg-elevated));transform:translateY(-4px)}.links span{color:var(--violet-8);font-family:var(--font-mono);font-size:.75rem}.links strong{font-size:1.25rem}.links small{color:var(--text-secondary);line-height:1.4} + .reference { width:min(1180px,calc(100% - 3rem)); margin:0 auto; padding:7rem 0; }.reference header{display:flex;justify-content:space-between;gap:2rem;align-items:end;margin-block-end:2rem}.reference h2{font-size:clamp(2.2rem,4vw,4.5rem);letter-spacing:-.06em;line-height:.92}.reference p{max-width:500px;color:var(--text-secondary);line-height:1.5}.links{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--border)}.links a{display:grid;gap:.6rem;min-height:180px;padding:1.4rem;border-inline-end:1px solid var(--border);color:inherit;text-decoration:none;background:color-mix(in srgb,var(--bg-elevated) 55%,transparent);transition:background .2s ease,transform .2s ease}.links a:last-child{border:0}.links a:hover{background:color-mix(in srgb,var(--violet-5) 18%,var(--bg-elevated));transform:translateY(-4px)}.links span{color:var(--violet-8);font-family:var(--font-mono);font-size:var(--font-size-00)}.links strong{font-size:var(--font-size-2)}.links small{color:var(--text-secondary);line-height:1.4} .cinematic-v2 { --film-progress:0; --scene-progress:0; --pointer-x:0; --pointer-y:0; position:relative; } .cinematic-v2 .film { position:sticky; top:0; z-index:0; } .cinematic-v2 .hero > div:first-child { opacity:clamp(0,calc(1 - var(--film-progress) * 3.2),1); transform:translateY(calc(var(--film-progress) * -4vh)); } .hero-mark-wrap { position:relative; z-index:3; display:grid; place-items:center; width:clamp(15rem,34vw,31rem); aspect-ratio:1; filter:drop-shadow(0 0 70px color-mix(in srgb,var(--violet-6) 48%,transparent)); transform:translate3d(calc(min(calc(var(--film-progress) * 2.4),1) * -48vw + var(--pointer-x) * 8px),calc(min(calc(var(--film-progress) * 2.4),1) * -42vh + var(--pointer-y) * 8px),0) scale(calc(1 - min(calc(var(--film-progress) * 2.4),1) * .82)); transform-origin:center; will-change:transform; } - .vinyl { position:absolute; inset:0; display:grid; place-items:center; border:1px solid color-mix(in srgb,#fff 18%,transparent); border-radius:50%; background:repeating-radial-gradient(circle at center,transparent 0 5px,color-mix(in srgb,#fff 7%,transparent) 6px,transparent 7px 12px),radial-gradient(circle at 32% 24%,color-mix(in srgb,var(--violet-7) 24%,transparent),transparent 22%),radial-gradient(circle at center,#18151e 0 15%,#09090c 15.5% 62%,#030304 63% 100%); box-shadow:inset 0 0 0 1px color-mix(in srgb,#fff 8%,transparent),inset 0 0 80px #000,0 32px 90px color-mix(in srgb,#000 72%,transparent); transform:rotate(calc(var(--film-progress) * 1080deg)); will-change:transform; } - .vinyl::before { content:""; position:absolute; inset:3%; border-radius:50%; background:conic-gradient(from 210deg,transparent 0 8%,color-mix(in srgb,#fff 15%,transparent) 10%,transparent 14% 49%,color-mix(in srgb,var(--violet-5) 12%,transparent) 53%,transparent 58% 100%); mix-blend-mode:screen; } - .vinyl::after { content:""; position:absolute; width:2.2%; aspect-ratio:1; border-radius:50%; background:#050507; border:1px solid color-mix(in srgb,#fff 28%,transparent); box-shadow:0 0 0 4px color-mix(in srgb,#000 55%,transparent); } - .vinyl-label { position:relative; z-index:1; display:grid; place-items:center; width:31%; aspect-ratio:1; border:1px solid color-mix(in srgb,var(--violet-5) 42%,transparent); border-radius:50%; background:radial-gradient(circle at 35% 28%,color-mix(in srgb,var(--violet-4) 88%,#fff),var(--violet-9) 72%); box-shadow:0 0 0 3px #08070b,0 0 34px color-mix(in srgb,var(--violet-6) 40%,transparent); } - .vinyl-wordmark { display:inline-flex; align-items:center; color:#fff; font-family:var(--font-mono); font-size:clamp(1rem,2.6vw,2.3rem); font-weight:800; letter-spacing:-.09em; line-height:1; text-shadow:0 1px 10px color-mix(in srgb,#000 55%,transparent); white-space:nowrap; } - .vinyl-slash { color:color-mix(in srgb,var(--violet-2) 82%,#fff); } + .vinyl { position:absolute; inset:0; display:grid; place-items:center; border:1px solid color-mix(in srgb,var(--vinyl-highlight) 18%,transparent); border-radius:50%; background:repeating-radial-gradient(circle at center,transparent 0 5px,color-mix(in srgb,var(--vinyl-highlight) 7%,transparent) 6px,transparent 7px 12px),radial-gradient(circle at 32% 24%,color-mix(in srgb,var(--violet-7) 24%,transparent),transparent 22%),radial-gradient(circle at center,var(--vinyl-ink-3) 0 15%,var(--vinyl-ink-2) 15.5% 62%,var(--vinyl-ink-0) 63% 100%); box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--vinyl-highlight) 8%,transparent),inset 0 0 80px var(--vinyl-shadow),0 32px 90px color-mix(in srgb,var(--vinyl-shadow) 72%,transparent); transform:rotate(calc(var(--film-progress) * 1080deg)); will-change:transform; } + .vinyl::before { content:""; position:absolute; inset:3%; border-radius:50%; background:conic-gradient(from 210deg,transparent 0 8%,color-mix(in srgb,var(--vinyl-highlight) 15%,transparent) 10%,transparent 14% 49%,color-mix(in srgb,var(--violet-5) 12%,transparent) 53%,transparent 58% 100%); mix-blend-mode:screen; } + .vinyl::after { content:""; position:absolute; width:2.2%; aspect-ratio:1; border-radius:50%; background:var(--vinyl-ink-1); border:1px solid color-mix(in srgb,var(--vinyl-highlight) 28%,transparent); box-shadow:0 0 0 4px color-mix(in srgb,var(--vinyl-shadow) 55%,transparent); } + .vinyl-label { position:relative; z-index:1; display:grid; place-items:center; width:31%; aspect-ratio:1; border:1px solid color-mix(in srgb,var(--violet-5) 42%,transparent); border-radius:50%; background:radial-gradient(circle at 35% 28%,color-mix(in srgb,var(--violet-4) 88%,var(--vinyl-highlight)),var(--violet-9) 72%); box-shadow:0 0 0 3px var(--vinyl-ink-4),0 0 34px color-mix(in srgb,var(--violet-6) 40%,transparent); } + .vinyl-wordmark { display:inline-flex; align-items:center; color:var(--vinyl-highlight); font-family:var(--font-mono); font-size:clamp(1rem,2.6vw,2.3rem); font-weight:800; letter-spacing:-.09em; line-height:1; text-shadow:0 1px 10px color-mix(in srgb,var(--vinyl-shadow) 55%,transparent); white-space:nowrap; } + .vinyl-slash { color:color-mix(in srgb,var(--violet-2) 82%,var(--vinyl-highlight)); } .cinematic-v2 .scenes { position:relative; z-index:2; margin-block-start:100vh; } .cinematic-v2 .scene { background:linear-gradient(90deg,color-mix(in srgb,var(--bg-base) 96%,transparent),color-mix(in srgb,var(--bg-base) 72%,transparent)); backdrop-filter:blur(2px); } .cinematic-v2 .component-stack { transform:rotateX(calc(18deg - var(--film-progress) * 12deg)) rotateY(calc(-24deg + var(--film-progress) * 18deg)) translateZ(calc(var(--film-progress) * 90px)); } @@ -67,7 +67,7 @@ sheet.replaceSync(` @keyframes orbit{50%{transform:rotate(12deg) scale(1.035)}} @keyframes reverse-orbit{to{transform:rotate(-360deg)}} @keyframes bob{50%{transform:translateY(5px)}} @keyframes pulse{50%{border-color:var(--violet-8);box-shadow:0 0 38px color-mix(in srgb,var(--violet-6) 36%,transparent)}} @media (prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;scroll-behavior:auto!important}.film,.scene{min-height:auto}.hero{min-height:auto}.scene{padding-block:6rem}.component-stack{transform:none}} @media (max-width:800px){.hero,.scene{grid-template-columns:1fr;min-height:auto;padding:6rem 1.4rem}.hero{padding-block-start:5rem}.aperture-stage{width:min(92vw,540px)}.scene-art{min-height:420px}.component:nth-child(2){margin-inline-start:1.5rem}.component:nth-child(3){margin-inline-start:3rem}.output{grid-template-columns:1fr 1fr}.target:last-child{grid-column:span 2}.links{grid-template-columns:1fr 1fr}.links a:nth-child(2){border-inline-end:0}.links a:nth-child(-n+2){border-block-end:1px solid var(--border)}} - @media (max-width:480px){h1{font-size:clamp(3.6rem,18vw,5.6rem)}.pulse-grid{gap:.65rem}.island{min-height:105px;font-size:.72rem}.output{grid-template-columns:1fr}.target:last-child{grid-column:auto;min-height:150px}.command{border-radius:1.1rem;align-items:start;flex-direction:column}.links{grid-template-columns:1fr}.links a{border-inline-end:0;border-block-end:1px solid var(--border)}.links a:last-child{border-block-end:0}.reference header{display:grid}.scene h2{font-size:clamp(2.8rem,13vw,4.4rem)}} + @media (max-width:480px){h1{font-size:clamp(3.6rem,18vw,5.6rem)}.pulse-grid{gap:.65rem}.island{min-height:105px;font-size:var(--font-size-00)}.output{grid-template-columns:1fr}.target:last-child{grid-column:auto;min-height:150px}.command{border-radius:1.1rem;align-items:start;flex-direction:column}.links{grid-template-columns:1fr}.links a{border-inline-end:0;border-block-end:1px solid var(--border)}.links a:last-child{border-block-end:0}.reference header{display:grid}.scene h2{font-size:clamp(2.8rem,13vw,4.4rem)}} `); const references = [ diff --git a/www/app/site-ui/open-brand-mark.tsx b/www/app/site-ui/open-brand-mark.tsx index 46a1db4f2..a8e25f70c 100644 --- a/www/app/site-ui/open-brand-mark.tsx +++ b/www/app/site-ui/open-brand-mark.tsx @@ -42,7 +42,7 @@ sheet.replaceSync(` } .slash { - color: var(--violet-8, #8b5cf6); + color: var(--violet-8); } :host { view-transition-name: open-brand-mark; } diff --git a/www/app/site-ui/open-layout.tsx b/www/app/site-ui/open-layout.tsx index 744b65976..712093bc4 100644 --- a/www/app/site-ui/open-layout.tsx +++ b/www/app/site-ui/open-layout.tsx @@ -261,7 +261,7 @@ sheet.replaceSync(` max-width: 100%; color: var(--text-primary); font-family: var(--font-mono); - font-size: 12px; + font-size: var(--font-size-00); font-weight: 800; letter-spacing: -.09em; line-height: 1; @@ -334,55 +334,6 @@ sheet.replaceSync(` margin-left: auto; } - /* CTA buttons */ - .btn-secondary { - display: inline-flex; - align-items: center; - gap: var(--size-2); - color: var(--text-primary); - text-decoration: none; - font-size: var(--font-size-button); - font-weight: var(--font-weight-semibold); - padding: var(--size-2) var(--size-4); - border: var(--border-size-1) solid color-mix(in srgb, var(--border) 72%, var(--brand)); - border-radius: var(--radius-round); - background: color-mix(in srgb, var(--bg-elevated) 76%, transparent); - box-shadow: inset 0 1px 0 color-mix(in srgb, var(--gray-0) 70%, transparent); - transition: all 0.15s ease; - } - .btn-secondary:hover { - color: var(--brand-deep); - border-color: var(--brand-light); - background: color-mix(in srgb, var(--brand-pale) 42%, var(--bg-elevated)); - } - .btn-secondary svg { flex-shrink: 0; } - - .btn-primary { - display: inline-flex; - align-items: center; - gap: 4px; - color: var(--on-brand); - text-decoration: none; - font-size: var(--font-size-button); - font-weight: var(--font-weight-semibold); - padding: var(--size-2) var(--size-4); - border: var(--border-size-1) solid transparent; - border-radius: var(--radius-round); - background: linear-gradient(135deg, var(--brand), var(--brand-light)); - box-shadow: 0 var(--size-2) var(--size-5) var(--brand-glow); - white-space: nowrap; - transition: all 0.15s ease; - } - .btn-primary:hover { - background: linear-gradient(135deg, var(--brand-hover), var(--brand-light)); - transform: translateY(calc(var(--border-size-1) * -1)); - } - - .header-right .btn-secondary, - .header-right .btn-primary { - display: none; - } - .lang-switch { display: inline-flex; align-items: center; @@ -430,7 +381,7 @@ sheet.replaceSync(` .nav-section { margin-bottom: 1.5rem; } .nav-section summary { - font-size: 0.625rem; + font-size: var(--font-size-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.22em; @@ -452,7 +403,7 @@ sheet.replaceSync(` display: block; color: var(--text-muted); text-decoration: none; - font-size: 0.85rem; + font-size: var(--font-size-tiny); margin: .12rem .7rem; padding: .5rem .8rem; border-left: 2px solid transparent; @@ -539,8 +490,6 @@ sheet.replaceSync(` .header-nav { gap: var(--size-3); } - - .btn-secondary .btn-text { display: none; } } @media (max-width: 1040px) { @@ -551,7 +500,6 @@ sheet.replaceSync(` .mobile-menu-btn { display: flex; } .header-inner { padding: 0 var(--size-4); gap: var(--size-2); } .header-nav { display: none; } - .btn-secondary .btn-text { display: none; } .header-right { gap: 4px; } .docs-sidebar { @@ -602,7 +550,7 @@ sheet.replaceSync(` flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; color: var(--text-muted); - text-decoration: none; font-size: 10px; font-weight: 600; + text-decoration: none; font-size: var(--font-size-micro); font-weight: 600; transition: color 0.15s ease; -webkit-tap-highlight-color: transparent; padding: 4px 0; } @@ -616,9 +564,6 @@ sheet.replaceSync(` .lang-switch { display: none; } } @media (max-width: 480px) { - .btn-primary { display: none; } - .btn-secondary { padding: 8px; border: none; } - .btn-secondary .btn-text { display: none; } .header-inner { padding: 0 var(--size-3); gap: var(--size-1); } } `); diff --git a/www/vite.config.ts b/www/vite.config.ts index 112ff2760..268f9469e 100644 --- a/www/vite.config.ts +++ b/www/vite.config.ts @@ -53,8 +53,23 @@ html[data-theme="light"], --font-size-button: var(--font-size-0); --font-size-body-sm: var(--font-size-0); --font-size-caption: var(--font-size-00); + --font-size-micro: 0.625rem; + --font-size-tiny: 0.85rem; + --font-size-lede: 1.1rem; + --font-size-overline: 0.6875rem; + --font-size-article-title: 1.125rem; + --font-size-display-sm: 1.75rem; + --font-size-display-md: 2.125rem; + --font-size-display-lg: 2.625rem; --font-weight-medium: var(--font-weight-5); --font-weight-semibold: var(--font-weight-7); + --vinyl-ink-0: #030304; + --vinyl-ink-1: #050507; + --vinyl-ink-2: #09090c; + --vinyl-ink-3: #18151e; + --vinyl-ink-4: #08070b; + --vinyl-highlight: #ffffff; + --vinyl-shadow: #000000; } html[data-theme="dark"], :host([data-theme="dark"]),