diff --git a/docs/development/ui-ux/design-language.md b/docs/development/ui-ux/design-language.md index 47053fc5..bb70169e 100644 --- a/docs/development/ui-ux/design-language.md +++ b/docs/development/ui-ux/design-language.md @@ -121,6 +121,12 @@ font sizeは、その領域でユーザーが行う読み方によって選ぶ ```css --font-sans --font-mono +--font-size-title /* 24px */ +--line-height-title /* 32px */ +--font-size-body /* 14px */ +--line-height-body /* 20px */ +--font-size-compact /* 12px */ +--line-height-compact /* 16px */ ``` ### Spacing / Shape diff --git a/web/workspace/deno.json b/web/workspace/deno.json index 24641206..83ec883b 100644 --- a/web/workspace/deno.json +++ b/web/workspace/deno.json @@ -6,7 +6,7 @@ "dev": "deno run -A npm:vite@7.2.7 dev", "dev:backend": "cd ../.. && cargo run -p yoi-workspace-server --bin yoi-server -- serve --listen 127.0.0.1:8787", "check": "deno run -A npm:@sveltejs/kit@2.49.4 sync && deno run -A npm:svelte-check@4.3.4 --tsconfig ./tsconfig.json", - "test": "deno test --allow-read=src,test,tests --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/tooltip-contract.test.ts tests/bevel-contract.test.ts tests/bevel-line-contract.test.ts src/lib/workspace/auth/model.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/ui.test.ts", + "test": "deno test --allow-read=src,test,tests,../../docs --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/tooltip-contract.test.ts tests/bevel-contract.test.ts tests/bevel-line-contract.test.ts tests/typography-contract.test.ts src/lib/workspace/auth/model.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/ui.test.ts", "build": "deno run -A npm:vite@7.2.7 build", "preview": "deno run -A npm:vite@7.2.7 preview" }, diff --git a/web/workspace/src/app.css b/web/workspace/src/app.css index 25c6f010..d2f1d833 100644 --- a/web/workspace/src/app.css +++ b/web/workspace/src/app.css @@ -53,6 +53,12 @@ --font-sans: "Gen Interface JP", sans-serif; --font-mono: "IBM Plex Mono", monospace; + --font-size-title: 24px; + --line-height-title: 32px; + --font-size-body: 14px; + --line-height-body: 20px; + --font-size-compact: 12px; + --line-height-compact: 16px; --space-1: 4px; --space-2: 8px; @@ -99,13 +105,14 @@ @layer base { html { - font-size: 14px; + font-size: var(--font-size-body); } body { background: var(--bg); color: var(--text); font-family: var(--font-sans); + line-height: var(--line-height-body); } h1, @@ -124,8 +131,19 @@ color: var(--text-strong); } + h1 { + font-size: var(--font-size-title); + line-height: var(--line-height-title); + } + + h2, + h3 { + font-size: var(--font-size-body); + line-height: var(--line-height-body); + } + p { - line-height: 1.55; + line-height: var(--line-height-body); } code, @@ -136,7 +154,8 @@ small { color: var(--text-muted); - font-size: 0.85em; + font-size: var(--font-size-compact); + line-height: var(--line-height-compact); } :where( diff --git a/web/workspace/src/lib/workspace/alerts/WorkspaceAlerts.svelte b/web/workspace/src/lib/workspace/alerts/WorkspaceAlerts.svelte index 54bc0dfe..2dea47d8 100644 --- a/web/workspace/src/lib/workspace/alerts/WorkspaceAlerts.svelte +++ b/web/workspace/src/lib/workspace/alerts/WorkspaceAlerts.svelte @@ -96,7 +96,7 @@ display: block; margin-bottom: 0.15rem; color: var(--alert-color); - font-size: 0.82rem; + font-size: var(--font-size-body); letter-spacing: 0.02em; text-transform: uppercase; } @@ -105,8 +105,8 @@ margin: 0; overflow-wrap: anywhere; color: var(--text); - font-size: 0.9rem; - line-height: 1.35; + font-size: var(--font-size-body); + line-height: var(--line-height-body); } .workspace-alert-dismiss { @@ -118,7 +118,7 @@ background: transparent; color: var(--text-muted); cursor: pointer; - font-size: 1.2rem; + font-size: var(--font-size-body); line-height: 1; } diff --git a/web/workspace/src/lib/workspace/console/ComposerInput.svelte b/web/workspace/src/lib/workspace/console/ComposerInput.svelte index 9bb6235c..f905af84 100644 --- a/web/workspace/src/lib/workspace/console/ComposerInput.svelte +++ b/web/workspace/src/lib/workspace/console/ComposerInput.svelte @@ -586,7 +586,7 @@ border-radius: 999px; background: color-mix(in srgb, var(--accent) 10%, var(--bg-subtle)); color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 600; line-height: 1.35; text-overflow: ellipsis; diff --git a/web/workspace/src/lib/workspace/console/ConsoleLineItem.svelte b/web/workspace/src/lib/workspace/console/ConsoleLineItem.svelte index bd79b730..2686304e 100644 --- a/web/workspace/src/lib/workspace/console/ConsoleLineItem.svelte +++ b/web/workspace/src/lib/workspace/console/ConsoleLineItem.svelte @@ -192,7 +192,7 @@ .task-reminder-summary { margin: 0; color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-compact); line-height: 1.55; white-space: pre-line; } @@ -203,7 +203,7 @@ gap: var(--space-2); color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.78rem; + font-size: var(--font-size-compact); font-variant-numeric: tabular-nums; font-weight: 750; } @@ -213,7 +213,7 @@ padding-left: 1.25rem; color: var(--tui-dark-gray); font-family: var(--font-mono); - font-size: 0.75rem; + font-size: var(--font-size-compact); } .compaction-summary, @@ -227,7 +227,7 @@ display: block; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.7rem; + font-size: var(--font-size-compact); } .compaction-error { @@ -235,7 +235,7 @@ } .activity-summary { - font-size: 14px; + font-size: var(--font-size-body); } .task-reminder-summary { @@ -252,7 +252,7 @@ margin: 0; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; @@ -294,7 +294,7 @@ min-width: 0; margin: 0; font-family: var(--font-mono); - font-size: 12px; + font-size: var(--font-size-compact); line-height: 1.1; overflow-x: auto; white-space: pre; @@ -313,7 +313,7 @@ align-items: baseline; gap: 0.5rem; color: var(--text-muted); - font-size: 0.88rem; + font-size: var(--font-size-compact); font-weight: 750; } @@ -329,7 +329,7 @@ .tool-status { flex: 0 0 auto; color: var(--tui-dark-gray); - font-size: 0.72rem; + font-size: var(--font-size-compact); white-space: nowrap; } @@ -357,7 +357,7 @@ justify-content: space-between; gap: var(--space-2); color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-compact); font-weight: 750; } @@ -367,7 +367,7 @@ border-radius: 0.65rem; color: var(--text); font-family: var(--font-mono); - font-size: 0.78rem; + font-size: var(--font-size-compact); line-height: 1.45; margin: 0.6rem 0 0; overflow-x: auto; @@ -424,7 +424,7 @@ color: var(--text-muted); cursor: pointer; font: inherit; - font-size: 0.68rem; + font-size: var(--font-size-compact); font-weight: 750; opacity: 0; pointer-events: none; @@ -443,7 +443,7 @@ border-left: 2px solid var(--line); padding-left: 0.6rem; color: var(--text-muted); - font-size: 0.84rem; + font-size: var(--font-size-compact); } .message-detail p { diff --git a/web/workspace/src/lib/workspace/console/ConsoleTasks.svelte b/web/workspace/src/lib/workspace/console/ConsoleTasks.svelte index d4db74f1..c9c33843 100644 --- a/web/workspace/src/lib/workspace/console/ConsoleTasks.svelte +++ b/web/workspace/src/lib/workspace/console/ConsoleTasks.svelte @@ -124,7 +124,7 @@ gap: 0.1rem; min-width: 0; padding-inline: 0.75rem; - font-size: 0.8rem; + font-size: var(--font-size-compact); line-height: 1.35; } @@ -237,7 +237,7 @@ .task-pane h3 { margin: 0 0 1rem; color: var(--accent); - font-size: 0.9rem; + font-size: var(--font-size-body); } .task-empty { @@ -262,8 +262,8 @@ } .task-description { - font-size: 0.8rem; - line-height: 1.45; + font-size: var(--font-size-body); + line-height: var(--line-height-body); } @media (max-width: 900px) { diff --git a/web/workspace/src/lib/workspace/console/RichMarkdown.svelte b/web/workspace/src/lib/workspace/console/RichMarkdown.svelte index da585bd3..e9a56a71 100644 --- a/web/workspace/src/lib/workspace/console/RichMarkdown.svelte +++ b/web/workspace/src/lib/workspace/console/RichMarkdown.svelte @@ -69,7 +69,7 @@ :global(.rich-markdown h4) { margin: 0.7rem 0 0.35rem; color: var(--text-strong); - font-size: 1rem; + font-size: var(--font-size-body); } :global(.rich-markdown blockquote) { diff --git a/web/workspace/src/lib/workspace/console/WorkerRunStatus.svelte b/web/workspace/src/lib/workspace/console/WorkerRunStatus.svelte index f8438c49..2546bb52 100644 --- a/web/workspace/src/lib/workspace/console/WorkerRunStatus.svelte +++ b/web/workspace/src/lib/workspace/console/WorkerRunStatus.svelte @@ -42,7 +42,7 @@ min-height: 1.35rem; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.74rem; + font-size: var(--font-size-compact); font-variant-numeric: tabular-nums; } diff --git a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts index 4b4aa265..9450eb12 100644 --- a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts +++ b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts @@ -547,7 +547,7 @@ Deno.test("Worker Console overview activity summaries use 14px text", async () = assert( consoleLine.includes(".activity-summary {") && - consoleLine.includes("font-size: 14px;"), + consoleLine.includes("font-size: var(--font-size-body);"), "Overview activity summaries such as ran command counts should render at 14px", ); }); @@ -570,7 +570,7 @@ Deno.test("Worker Console renders markdown only for message rows", async () => { consoleLine.includes( ".console-line.tool.tool-bash .console-plain-text", ) && - consoleLine.includes("font-size: 12px;") && + consoleLine.includes("font-size: var(--font-size-compact);") && consoleLine.includes("line-height: 1.1;") && consoleLine.includes("{:else if shouldRenderMarkdown(item)}") && consoleLine.includes("") && diff --git a/web/workspace/src/lib/workspace/header/WorkspaceBreadcrumbs.svelte b/web/workspace/src/lib/workspace/header/WorkspaceBreadcrumbs.svelte index 0f8c2f85..b41c1787 100644 --- a/web/workspace/src/lib/workspace/header/WorkspaceBreadcrumbs.svelte +++ b/web/workspace/src/lib/workspace/header/WorkspaceBreadcrumbs.svelte @@ -61,7 +61,7 @@ gap: 0.55rem; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.84rem; + font-size: var(--font-size-compact); line-height: 1; } diff --git a/web/workspace/src/lib/workspace/sidebar/sidebar.css b/web/workspace/src/lib/workspace/sidebar/sidebar.css index 075f83f9..dfb0e5c1 100644 --- a/web/workspace/src/lib/workspace/sidebar/sidebar.css +++ b/web/workspace/src/lib/workspace/sidebar/sidebar.css @@ -122,7 +122,7 @@ border-radius: var(--radius-soft); padding: 0.45rem 0.6rem; color: var(--text-strong); - font-size: 1.05rem; + font-size: var(--font-size-compact); font-weight: 800; line-height: 1.25; text-overflow: ellipsis; @@ -149,7 +149,7 @@ background: transparent; color: var(--text-strong); font: inherit; - font-size: 1.05rem; + font-size: var(--font-size-compact); font-weight: 800; line-height: 1.25; text-align: left; @@ -184,7 +184,7 @@ padding: 0.35rem 0.45rem; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.84rem; + font-size: var(--font-size-compact); font-weight: 650; line-height: 1; } @@ -227,7 +227,7 @@ border-radius: var(--radius-soft); padding: var(--space-2); color: var(--text-strong); - font-size: 0.82rem; + font-size: var(--font-size-compact); font-weight: 650; line-height: 1.3; text-decoration: none; @@ -257,7 +257,7 @@ gap: var(--space-2); padding: var(--space-1) var(--space-2) 0; color: var(--text-faint); - font-size: 0.68rem; + font-size: var(--font-size-compact); font-weight: 750; letter-spacing: 0.08em; text-transform: uppercase; @@ -269,7 +269,7 @@ place-items: center; border-radius: var(--radius-soft); color: var(--text-muted); - font-size: 1rem; + font-size: var(--font-size-body); line-height: 1; text-decoration: none; } @@ -291,7 +291,7 @@ .workspace-menu-status { margin: var(--space-1) var(--space-2); color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); line-height: 1.35; } .workspace-menu-status.error { @@ -300,13 +300,13 @@ .section-state { margin: var(--space-2) 0 0; color: var(--text-muted); - font-size: 0.74rem; + font-size: var(--font-size-compact); line-height: 1.35; } .workspace-status { margin: var(--space-1) 0 0; color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-compact); line-height: 1.35; } .workspace-status.error, @@ -365,7 +365,7 @@ .sidebar-nav-section__header, .sidebar-section-label { color: var(--text-faint); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 750; letter-spacing: 0.11em; text-transform: uppercase; @@ -389,7 +389,7 @@ } .section-count { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); line-height: 1; } .nav-list, @@ -408,7 +408,7 @@ padding: var(--space-2) var(--space-3); border-radius: var(--radius-soft); color: var(--text-strong); - font-size: 0.9rem; + font-size: var(--font-size-compact); text-align: left; text-decoration: none; transition: background-color 140ms ease, color 140ms ease; @@ -490,7 +490,7 @@ background: transparent; color: var(--text-strong); font: inherit; - font-size: 0.76rem; + font-size: var(--font-size-compact); font-weight: 650; line-height: 1.2; text-align: left; @@ -551,7 +551,7 @@ align-items: center; justify-content: center; width: 0.75rem; - font-size: 0.7rem; + font-size: var(--font-size-compact); line-height: 1; } .worker-status-spinner.is-subworker { @@ -563,7 +563,7 @@ min-width: 0; overflow: hidden; color: inherit; - font-size: 0.78rem; + font-size: var(--font-size-compact); font-weight: 600; line-height: 1.1rem; text-overflow: ellipsis; @@ -576,7 +576,7 @@ overflow: hidden; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.66rem; + font-size: var(--font-size-compact); line-height: 1rem; text-overflow: ellipsis; white-space: nowrap; diff --git a/web/workspace/src/lib/workspace/styles/settings.css b/web/workspace/src/lib/workspace/styles/settings.css index de9792a2..0c287c8b 100644 --- a/web/workspace/src/lib/workspace/styles/settings.css +++ b/web/workspace/src/lib/workspace/styles/settings.css @@ -72,7 +72,7 @@ align-items: center; gap: var(--space-1); color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; @@ -202,7 +202,7 @@ .account-details dt { color: var(--text-muted); - font-size: 0.8rem; + font-size: var(--font-size-compact); font-weight: 700; text-transform: uppercase; } @@ -213,7 +213,7 @@ } .account-details code { - font-size: 0.78rem; + font-size: var(--font-size-compact); } .account-panel { @@ -232,7 +232,7 @@ display: grid; gap: 0.25rem; color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-body); } .settings-runtime-form button, @@ -289,7 +289,7 @@ .settings-runtime-table th { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); letter-spacing: 0.05em; text-transform: uppercase; } @@ -333,7 +333,7 @@ .settings-muted-action { color: var(--text-muted); - font-size: 0.82rem; + font-size: var(--font-size-compact); } .settings-action-row { @@ -432,7 +432,7 @@ display: grid; gap: var(--space-1); color: var(--text-muted); - font-size: 0.82rem; + font-size: var(--font-size-body); } .settings-form input, .settings-form select, @@ -524,7 +524,7 @@ } .settings-identity-list dt { color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-compact); } .settings-identity-list dd { margin: 0; @@ -536,7 +536,7 @@ align-items: center; gap: var(--space-1); color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 800; text-transform: uppercase; } @@ -593,7 +593,7 @@ } .config-source-tree nav button small { color: var(--text-muted); - font-size: 0.65rem; + font-size: var(--font-size-compact); letter-spacing: 0.06em; text-transform: uppercase; } @@ -608,10 +608,13 @@ padding: var(--space-3); border-top: 1px solid var(--line); } - .config-source-create label, + .config-source-create label { + color: var(--text-muted); + font-size: var(--font-size-body); + } .config-source-workbench__header span { color: var(--text-muted); - font-size: 0.75rem; + font-size: var(--font-size-compact); } .config-source-create input, .config-source-actions input { @@ -670,7 +673,7 @@ padding: var(--space-2) var(--space-3); border-top: 1px solid var(--line); color: var(--text-muted); - font-size: 0.8rem; + font-size: var(--font-size-compact); } .config-source-conflict { display: flex; @@ -742,7 +745,7 @@ display: grid; gap: var(--space-2); color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-body); font-weight: 700; } .settings-repository-form input { @@ -786,7 +789,7 @@ } .settings-repository-table th { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); text-transform: uppercase; } .settings-repository-table tbody tr:last-child td { diff --git a/web/workspace/src/lib/workspace/styles/tickets.css b/web/workspace/src/lib/workspace/styles/tickets.css index 095bbb97..8790b87b 100644 --- a/web/workspace/src/lib/workspace/styles/tickets.css +++ b/web/workspace/src/lib/workspace/styles/tickets.css @@ -10,7 +10,7 @@ border-radius: 0.55rem; padding: 0.5rem 0.8rem; font: inherit; - font-size: 0.82rem; + font-size: var(--font-size-compact); font-weight: 700; text-decoration: none; cursor: pointer; @@ -38,7 +38,7 @@ } .workspace-back-link { color: var(--text-muted); - font-size: 0.82rem; + font-size: var(--font-size-compact); text-decoration: none; } .workspace-callout { @@ -94,11 +94,11 @@ } .orchestrator-status strong { color: var(--text-strong); - font-size: 0.76rem; + font-size: var(--font-size-compact); } .orchestrator-status span { color: var(--text-muted); - font-size: 0.68rem; + font-size: var(--font-size-compact); } .orchestrator-status-dot { width: 0.55rem; @@ -116,8 +116,8 @@ } .ticket-panel-summary strong { color: var(--text-strong); - font-size: 1.8rem; - line-height: 1; + font-size: var(--font-size-compact); + line-height: var(--line-height-compact); } .ticket-kanban { display: grid; @@ -149,7 +149,7 @@ } .ticket-lane-header h2 { margin: 0; - font-size: 0.78rem; + font-size: var(--font-size-compact); text-transform: uppercase; letter-spacing: 0.04em; } @@ -170,7 +170,7 @@ } .ticket-lane-count { color: var(--text-muted); - font-size: 0.75rem; + font-size: var(--font-size-compact); } .ticket-lane-cards { display: grid; @@ -188,7 +188,7 @@ margin: 0; color: var(--text-muted); padding: 0.5rem; - font-size: 0.7rem; + font-size: var(--font-size-compact); text-align: center; } .ticket-lane-page-state { @@ -198,7 +198,7 @@ margin: 0; padding: 0.45rem; color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); text-align: center; } .ticket-lane-page-error { @@ -229,17 +229,17 @@ overflow: hidden; color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.68rem; + font-size: var(--font-size-compact); text-overflow: ellipsis; } .ticket-card strong { - font-size: 0.85rem; + font-size: var(--font-size-compact); line-height: 1.35; } .ticket-card-meta { justify-content: space-between; color: var(--text-muted); - font-size: 0.68rem; + font-size: var(--font-size-compact); } .ticket-risk-list { display: flex; @@ -251,7 +251,7 @@ align-items: center; gap: var(--space-1); color: var(--warning); - font-size: 0.62rem; + font-size: var(--font-size-compact); } .ticket-risk-list span::before { width: 0.4rem; @@ -263,7 +263,7 @@ .ticket-lane-empty { padding: 1.2rem 0.4rem; color: var(--text-muted); - font-size: 0.75rem; + font-size: var(--font-size-compact); text-align: center; } .ticket-detail-page { @@ -274,26 +274,26 @@ .ticket-detail-header h1 { margin: 0.45rem 0 0.25rem; color: var(--text-strong); - font-size: clamp(1.5rem, 3vw, 2.4rem); + font-size: var(--font-size-title); } .ticket-detail-header p { margin: 0; color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-compact); } .ticket-detail-kicker { gap: 0.6rem; } .ticket-detail-kicker code { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); } .workspace-status-pill { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--text-muted); - font-size: 0.68rem; + font-size: var(--font-size-compact); text-transform: uppercase; } .workspace-status-pill::before { @@ -332,11 +332,11 @@ .ticket-section-heading h2, .ticket-control-card h2 { margin: 0; color: var(--text-strong); - font-size: 0.9rem; + font-size: var(--font-size-body); } .ticket-section-heading span, .ticket-control-card header span { color: var(--text-muted); - font-size: 0.7rem; + font-size: var(--font-size-compact); } .ticket-editor, .ticket-control-form { display: grid; @@ -346,7 +346,7 @@ display: grid; gap: 0.3rem; color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-body); } .ticket-editor input, .ticket-editor textarea, @@ -363,8 +363,8 @@ } .ticket-worker-card p { color: var(--text-muted); - font-size: 0.78rem; - line-height: 1.5; + font-size: var(--font-size-body); + line-height: var(--line-height-body); } .ticket-role-actions { gap: 0.55rem; @@ -379,7 +379,7 @@ details.ticket-control-card summary { cursor: pointer; color: var(--text-strong); - font-size: 0.85rem; + font-size: var(--font-size-compact); font-weight: 700; } details.ticket-control-card[open] summary { @@ -411,7 +411,7 @@ .ticket-relations-list small, .ticket-blocker-list span { color: var(--text-muted); - font-size: 0.68rem; + font-size: var(--font-size-compact); } .ticket-timeline { display: grid; @@ -449,7 +449,7 @@ } .ticket-timeline time, .ticket-event-author { color: var(--text-muted); - font-size: 0.68rem; + font-size: var(--font-size-compact); } .ticket-event-author { margin: 0.2rem 0; diff --git a/web/workspace/src/lib/workspace/styles/workers.css b/web/workspace/src/lib/workspace/styles/workers.css index b7561730..b9146d2d 100644 --- a/web/workspace/src/lib/workspace/styles/workers.css +++ b/web/workspace/src/lib/workspace/styles/workers.css @@ -12,7 +12,7 @@ border-radius: var(--radius-soft); background: var(--bg-subtle); color: var(--text-strong); - font-size: 0.72rem; + font-size: var(--font-size-compact); padding: 0.2rem 0.55rem; cursor: pointer; text-decoration: none; @@ -35,7 +35,7 @@ display: grid; gap: 0.25rem; color: var(--text-muted); - font-size: 0.78rem; + font-size: var(--font-size-body); } .worker-launch-form input, @@ -85,8 +85,8 @@ } .worker-new-page-header h1 { color: var(--text-strong); - font-size: clamp(1.6rem, 4vw, 2.4rem); - line-height: 1.05; + font-size: var(--font-size-title); + line-height: var(--line-height-title); } .worker-new-page-header p:not(.eyebrow) { margin-top: var(--space-2); @@ -110,11 +110,11 @@ .worker-ticket-context span, .worker-ticket-context code { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); } .worker-ticket-context a { color: var(--accent); - font-size: 0.78rem; + font-size: var(--font-size-compact); } .worker-launch-form { display: grid; @@ -128,14 +128,14 @@ .worker-form-section h2 { margin: 0; color: var(--text-strong); - font-size: 1rem; + font-size: var(--font-size-body); } .worker-launch-sentence { display: flex; align-items: center; gap: var(--space-2); color: var(--text-muted); - font-size: 0.95rem; + font-size: var(--font-size-body); white-space: nowrap; } .worker-inline-select { @@ -154,7 +154,7 @@ .new-working-directory-panel h3 { margin: 0; color: var(--text-strong); - font-size: 0.95rem; + font-size: var(--font-size-body); } .new-working-directory-fields, .worker-detail-grid { diff --git a/web/workspace/src/lib/workspace/styles/workspace-catalog.css b/web/workspace/src/lib/workspace/styles/workspace-catalog.css index 99b6dd44..487eb10c 100644 --- a/web/workspace/src/lib/workspace/styles/workspace-catalog.css +++ b/web/workspace/src/lib/workspace/styles/workspace-catalog.css @@ -1,3 +1,4 @@ +@layer reset, tokens, base, layout, components; @layer components { .workspace-catalog-shell { width: min(1120px, calc(100% - 2rem)); @@ -32,7 +33,7 @@ .workspace-catalog-eyebrow { color: var(--accent) !important; - font-size: 0.75rem; + font-size: var(--font-size-compact); font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; @@ -78,7 +79,7 @@ align-items: center; gap: var(--space-1); color: var(--text-muted); - font-size: 0.7rem; + font-size: var(--font-size-compact); text-transform: uppercase; } @@ -121,7 +122,7 @@ display: grid; gap: 0.35rem; color: var(--text-muted); - font-size: 0.8rem; + font-size: var(--font-size-body); } .workspace-create-panel input, @@ -181,7 +182,7 @@ } .workspace-switcher label { - font-size: 0.68rem; + font-size: var(--font-size-compact); letter-spacing: 0.08em; color: var(--text-muted); text-transform: uppercase; @@ -190,18 +191,18 @@ .workspace-switcher select { width: 100%; padding: 0.45rem 0.55rem; - font-size: 0.78rem; + font-size: var(--font-size-compact); } .workspace-switcher-actions { display: flex; gap: 0.75rem; - font-size: 0.72rem; + font-size: var(--font-size-compact); } .workspace-switcher-error { color: var(--danger); - font-size: 0.7rem; + font-size: var(--font-size-compact); } @media (max-width: 760px) { diff --git a/web/workspace/src/lib/workspace/styles/workspace-pages.css b/web/workspace/src/lib/workspace/styles/workspace-pages.css index a61691c5..c244f6ff 100644 --- a/web/workspace/src/lib/workspace/styles/workspace-pages.css +++ b/web/workspace/src/lib/workspace/styles/workspace-pages.css @@ -81,7 +81,7 @@ color: var(--text-muted); } .workspace-action-card span { - font-size: 0.78rem; + font-size: var(--font-size-compact); } .workspace-action-card strong { color: var(--text-strong); @@ -152,7 +152,7 @@ .state-pill { flex: 0 0 auto; color: var(--success); - font-size: 0.76rem; + font-size: var(--font-size-compact); font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; @@ -161,7 +161,7 @@ .objective-summary { margin: 0; color: var(--text-muted); - font-size: 0.88rem; + font-size: var(--font-size-compact); line-height: 1.4; } .ticket-meta, @@ -171,7 +171,7 @@ justify-content: flex-end; gap: var(--space-2); color: var(--text-faint); - font-size: 0.78rem; + font-size: var(--font-size-compact); line-height: 1.35; text-align: right; white-space: nowrap; @@ -211,7 +211,7 @@ } .detail-heading span { color: var(--text-muted); - font-size: 0.8rem; + font-size: var(--font-size-compact); } .ticket-detail-grid { margin-bottom: var(--space-4); @@ -228,7 +228,7 @@ align-items: center; gap: var(--space-1); color: var(--warning); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; @@ -264,7 +264,7 @@ } .main-content dt { color: var(--text-faint); - font-size: 0.72rem; + font-size: var(--font-size-compact); letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; @@ -294,7 +294,7 @@ } .main-content th { color: var(--text-faint); - font-size: 0.78rem; + font-size: var(--font-size-compact); letter-spacing: 0.08em; text-transform: uppercase; } @@ -314,8 +314,8 @@ .page-header-row h1, .workers-page-header h1 { color: var(--text-strong); - font-size: clamp(1.6rem, 4vw, 2.4rem); - line-height: 1.05; + font-size: var(--font-size-title); + line-height: var(--line-height-title); } .page-header-row p:not(.breadcrumb), .workers-page-header p { @@ -325,7 +325,7 @@ .breadcrumb { margin-bottom: var(--space-2); color: var(--text-muted); - font-size: 0.85rem; + font-size: var(--font-size-compact); } .breadcrumb a { color: inherit; diff --git a/web/workspace/src/lib/workspace/ui/Tooltip.svelte b/web/workspace/src/lib/workspace/ui/Tooltip.svelte index 64906e87..87f5ab63 100644 --- a/web/workspace/src/lib/workspace/ui/Tooltip.svelte +++ b/web/workspace/src/lib/workspace/ui/Tooltip.svelte @@ -156,7 +156,7 @@ box-shadow: var(--shadow-overlay); color: var(--bg); font-family: var(--font-sans); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 500; line-height: 16px; opacity: 0; diff --git a/web/workspace/src/routes/design-lab/workspace-web-ux/showroom.css b/web/workspace/src/routes/design-lab/workspace-web-ux/showroom.css index beb3ec13..4c7d89be 100644 --- a/web/workspace/src/routes/design-lab/workspace-web-ux/showroom.css +++ b/web/workspace/src/routes/design-lab/workspace-web-ux/showroom.css @@ -1,3 +1,4 @@ +@layer reset, tokens, base, layout, components; @layer components { .design-lab-header { display: flex; @@ -5,7 +6,7 @@ align-items: center; gap: var(--space-2); color: var(--text-muted); - font-size: 14px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 20px; } @@ -34,7 +35,7 @@ padding: var(--space-1) var(--space-2); border-radius: var(--radius-soft); color: var(--accent); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 650; line-height: 16px; text-decoration: none; @@ -69,7 +70,7 @@ margin: 0; padding-bottom: var(--space-2); color: var(--text-strong); - font-size: 14px; + font-size: var(--font-size-body); font-weight: 650; line-height: 20px; } @@ -95,7 +96,7 @@ padding: 7px var(--space-4); background: transparent; color: inherit; - font-size: 14px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 20px; cursor: pointer; @@ -150,7 +151,8 @@ background: var(--interactive-selected); color: var(--text-strong); cursor: pointer; - font: 600 12px/1 var(--font-mono); + font: 600 var(--font-size-compact) / var(--line-height-compact) + var(--font-mono); } .border-matrix { @@ -200,13 +202,13 @@ gap: var(--space-3); color: var(--text-muted); font-family: var(--font-mono); - font-size: 12px; + font-size: var(--font-size-compact); } .line-sample__label { color: var(--text-muted); font-family: var(--font-mono); - font-size: 12px; + font-size: var(--font-size-compact); } .status { @@ -215,7 +217,7 @@ align-items: center; gap: var(--space-2); color: var(--text); - font-size: 13px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 18px; white-space: nowrap; @@ -276,14 +278,14 @@ .feedback strong { color: var(--text-strong); - font-size: 14px; + font-size: var(--font-size-body); } .feedback p { margin: 2px 0 0; color: var(--text-muted); - font-size: 13px; - line-height: 18px; + font-size: var(--font-size-body); + line-height: var(--line-height-body); } .feedback--error { @@ -334,13 +336,13 @@ min-width: 0; overflow-wrap: anywhere; color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); line-height: 16px; } .resource-row__meta { color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); line-height: 16px; text-align: right; } @@ -373,7 +375,7 @@ .showroom thead th { color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 16px; } @@ -381,7 +383,7 @@ .showroom tbody th { max-width: 360px; color: var(--text-strong); - font-size: 14px; + font-size: var(--font-size-compact); font-weight: 600; } @@ -396,7 +398,7 @@ display: block; margin-top: 2px; color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 400; } @@ -423,7 +425,7 @@ .key-value dt, .technical-values dt { color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 18px; } @@ -433,7 +435,7 @@ min-width: 0; margin: 0; color: var(--text-strong); - font-size: 13px; + font-size: var(--font-size-compact); line-height: 18px; } @@ -497,7 +499,7 @@ .type-samples code, .space-samples code { color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); line-height: 16px; } @@ -513,21 +515,21 @@ .type-route { color: var(--text-strong); - font-size: 24px; + font-size: var(--font-size-title); font-weight: 650; - line-height: 32px; + line-height: var(--line-height-title); } .type-body { color: var(--text); - font-size: 14px; + font-size: var(--font-size-body); line-height: 20px; } .type-meta { color: var(--text-muted); font-family: var(--font-mono); - font-size: 12px; + font-size: var(--font-size-compact); line-height: 16px; } @@ -565,7 +567,7 @@ display: grid; gap: var(--space-2); color: var(--text-strong); - font-size: 13px; + font-size: var(--font-size-compact); font-weight: 600; } @@ -582,14 +584,14 @@ .form-field small { color: var(--text-muted); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 400; line-height: 16px; } .field-error { color: var(--danger); - font-size: 12px; + font-size: var(--font-size-compact); font-weight: 600; line-height: 16px; } @@ -608,7 +610,7 @@ align-items: center; gap: var(--space-2); color: var(--text-strong); - font-size: 13px; + font-size: var(--font-size-compact); font-weight: 600; } @@ -620,7 +622,7 @@ .permission-read-view p { margin: 0; color: var(--text-muted); - font-size: 13px; + font-size: var(--font-size-body); } @media (prefers-reduced-motion: reduce) { diff --git a/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte index 99789aaa..d852919c 100644 --- a/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/memory/+page.svelte @@ -81,7 +81,7 @@ .memory-document-summary span { color: var(--text-muted); display: block; - font-size: 0.76rem; + font-size: var(--font-size-compact); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; @@ -90,7 +90,7 @@ .memory-document-summary strong { color: var(--text); display: block; - font-size: 0.95rem; + font-size: var(--font-size-compact); margin-top: 0.25rem; overflow-wrap: anywhere; } @@ -101,8 +101,8 @@ border-radius: 0.9rem; color: var(--text); font-family: var(--font-mono); - font-size: 0.88rem; - line-height: 1.6; + font-size: var(--font-size-body); + line-height: var(--line-height-body); margin: 1rem 0 0; overflow: auto; padding: 1rem; diff --git a/web/workspace/src/routes/w/[workspaceId]/memory/staging/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/memory/staging/+page.svelte index 3cc98778..d8aae539 100644 --- a/web/workspace/src/routes/w/[workspaceId]/memory/staging/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/memory/staging/+page.svelte @@ -164,7 +164,7 @@ .staging-meta dt { color: var(--text-muted); display: block; - font-size: 0.76rem; + font-size: var(--font-size-compact); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; @@ -173,7 +173,7 @@ .staging-summary-grid strong { color: var(--text); display: block; - font-size: 0.95rem; + font-size: var(--font-size-compact); margin-top: 0.25rem; overflow-wrap: anywhere; } @@ -200,7 +200,7 @@ .staging-entry h3 { color: var(--text-strong); - font-size: 1rem; + font-size: var(--font-size-body); margin: 0.35rem 0 0; } @@ -219,7 +219,7 @@ border-radius: 999px; color: var(--accent); display: inline-flex; - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 700; letter-spacing: 0.04em; padding: 0.18rem 0.55rem; diff --git a/web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte index 03efcf8d..9d6764be 100644 --- a/web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte @@ -1918,7 +1918,7 @@ color: var(--text-muted); padding: 0.42rem 0.65rem; font: inherit; - font-size: 0.7rem; + font-size: var(--font-size-compact); font-weight: 700; cursor: pointer; } @@ -1945,7 +1945,7 @@ .rewind-targets h3 { margin: 0; - font-size: 0.9rem; + font-size: var(--font-size-body); } .rewind-target-list { @@ -2007,7 +2007,7 @@ background: var(--bg-raised); color: var(--text-muted); cursor: pointer; - font-size: 0.76rem; + font-size: var(--font-size-compact); font-weight: 800; padding: 0.35rem 0.5rem; text-align: left; @@ -2079,7 +2079,7 @@ .console-side-panel dt { color: var(--text-muted); - font-size: 0.72rem; + font-size: var(--font-size-compact); font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; @@ -2093,7 +2093,7 @@ .metadata-details { color: var(--text-muted); - font-size: 0.84rem; + font-size: var(--font-size-compact); } .metadata-details summary { @@ -2153,7 +2153,7 @@ border: 1px solid var(--line); border-radius: 999px; padding: 0.2rem 0.5rem; - font: 500 0.75rem/1.2 var(--font-mono); + font: 500 var(--font-size-compact) / var(--line-height-compact) var(--font-mono); } .composer-attachment.failed { @@ -2242,12 +2242,12 @@ gap: var(--space-2); align-items: center; color: var(--text-muted); - font-size: 0.86rem; + font-size: var(--font-size-compact); } .composer-notice { color: var(--text-muted); - font-size: 0.86rem; + font-size: var(--font-size-compact); } .composer-actions { diff --git a/web/workspace/tests/typography-contract.test.ts b/web/workspace/tests/typography-contract.test.ts new file mode 100644 index 00000000..4593a37f --- /dev/null +++ b/web/workspace/tests/typography-contract.test.ts @@ -0,0 +1,84 @@ +// @ts-nocheck +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +async function collectStyleSources(directory: URL): Promise { + const files: URL[] = []; + + for await (const entry of Deno.readDir(directory)) { + const url = new URL(entry.name, directory); + if (entry.isDirectory) { + url.pathname += "/"; + files.push(...await collectStyleSources(url)); + } else if ( + entry.isFile && + (entry.name.endsWith(".css") || entry.name.endsWith(".svelte")) + ) { + files.push(url); + } + } + + return files; +} + +Deno.test("Workspace typography uses only the title body and compact scales", async () => { + const sourceRoot = new URL("../src/", import.meta.url); + const allowedSizes = new Set([ + "var(--font-size-title)", + "var(--font-size-body)", + "var(--font-size-compact)", + "inherit", + ]); + + for (const url of await collectStyleSources(sourceRoot)) { + const source = await Deno.readTextFile(url); + for (const match of source.matchAll(/font-size:\s*([^;]+);/g)) { + const value = match[1].trim(); + assert( + allowedSizes.has(value), + `${url.pathname} must not use the font-size value ${value}`, + ); + } + assert( + !/font:\s*[^;]*(?:\d+(?:\.\d+)?(?:px|rem|em))[^;]*;/g.test(source), + `${url.pathname} font shorthand must use a shared typography token`, + ); + } +}); + +Deno.test("Workspace typography tokens bind the documented three-level scale", async () => { + const appCss = await Deno.readTextFile( + new URL("../src/app.css", import.meta.url), + ); + const designLanguage = await Deno.readTextFile( + new URL( + "../../../docs/development/ui-ux/design-language.md", + import.meta.url, + ), + ); + + for ( + const contract of [ + "--font-size-title: 24px", + "--line-height-title: 32px", + "--font-size-body: 14px", + "--line-height-body: 20px", + "--font-size-compact: 12px", + "--line-height-compact: 16px", + "font-size: var(--font-size-title)", + "font-size: var(--font-size-body)", + "font-size: var(--font-size-compact)", + ] + ) { + assert(appCss.includes(contract), `app.css must preserve ${contract}`); + } + + assert( + designLanguage.includes("--font-size-title /* 24px */") && + designLanguage.includes("--font-size-body /* 14px */") && + designLanguage.includes("--font-size-compact /* 12px */") && + designLanguage.includes("`12px`未満と`13px`を使わない"), + "The design language must preserve the title, body, and compact typography contract", + ); +});