Add a design system for Claude Design, generated from garage_ui

Tokens come out of garage_ui/tool/export_design_tokens_test.dart, the
components are CSS on top of them, and the cards cover foundations,
components and whole hub and Arcs & Angles screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
This commit is contained in:
ImBenji
2026-09-23 23:19:02 +01:00
co-authored by Claude Opus 5.5
parent 572c09039f
commit b5e5ed5b75
25 changed files with 3381 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
<!-- @dsCard group="Patterns" -->
<!doctype html>
<html lang="en" data-scheme="carbon" data-density="compact">
<head>
<meta charset="utf-8">
<title>Editor shell</title>
<link rel="stylesheet" href="../components.css">
<style>
/* scaffolding - GarageShell: header, a chrome workspace with the canvas Panel and a
sidebar split top/bottom, footer. panelGap gutters, no gutter above or below */
html, body { background: var(--chrome); }
.screen { width: 1200px; height: 760px; display: flex; flex-direction: column; background: var(--chrome); }
.workspace { flex: 1; min-height: 0; display: flex; gap: var(--panel-gap); padding: 0 var(--panel-gap); }
.canvas { flex: 1; min-width: 0; }
.sidebar { width: 320px; flex: none; display: flex; flex-direction: column; gap: var(--panel-gap); }
.sidebar > .panel { flex: 1; min-height: 0; display: flex; }
/* header: menu words left, perf + file name right, toggles on the true centre */
.header { position: relative; }
.header .menus { display: flex; }
.header .spacer { flex: 1; }
.header .file { width: 150px; text-align: center; }
.header .centre { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); display: flex; gap: var(--gap-sm); }
/* a panel page: sunken body, PanelHeader, then the list */
.page { flex: 1; min-width: 0; background: var(--surface-sunken); display: flex; flex-direction: column; }
.page .body { flex: 1; min-height: 0; overflow: hidden; padding-top: var(--gap-md); }
/* properties tab strip - hand-rolled in the app too (see "Dont copy these") */
.tabs { width: 40px; flex: none; background: var(--chrome); border-right: 1px solid var(--divider);
padding: var(--gap-xs) 0 var(--gap-xs) var(--gap-xs); display: flex; flex-direction: column; gap: var(--gap-xs); }
.tabs .tab { display: flex; justify-content: center; padding-right: var(--gap-xs); border: 1px solid transparent; }
.tabs .tab.on { background: var(--surface-sunken); border-color: var(--divider); border-right-color: transparent;
border-radius: var(--radius-sm) 0 0 var(--radius-sm); margin-right: -1px; }
.tabs .split { height: var(--gap-xl); }
/* footer: three blocks, muted mono */
.footer > div { flex: 1; display: flex; align-items: center; gap: var(--gap-md); }
.footer .mid { justify-content: center; }
.footer .end { justify-content: flex-end; }
</style>
</head>
<body>
<div class="screen">
<!-- header ChromeBar -->
<header class="chrome-bar header">
<div class="menus">
<button class="btn ghost">File</button>
<button class="btn ghost">Edit</button>
<button class="btn ghost">Tools</button>
<button class="btn ghost">Insert</button>
<button class="btn ghost">View</button>
<button class="btn ghost">Info</button>
</div>
<div class="spacer"></div>
<span class="mono muted">60 fps</span>
<span class="vr"></span>
<span class="file muted">Northern loop.ana</span>
<div class="centre">
<div class="btn-group">
<button class="btn primary icon-only" aria-label="Snapping, on"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m12 15 4 4" /> <path d="M2.352 10.648a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.029-6.029a1 1 0 1 1 3 3l-6.029 6.029a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.365-6.367A1 1 0 0 0 8.716 4.282z" /> <path d="m5 8 4 4" /></svg></button>
<button class="btn secondary icon-only" aria-label="Snap options"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg></button>
</div>
<button class="btn outline icon-only" aria-label="Continuous arcs, off"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="19" cy="5" r="2" /> <circle cx="5" cy="19" r="2" /> <path d="M5 17A12 12 0 0 1 17 5" /></svg></button>
</div>
</header>
<div class="workspace">
<!-- the canvas: an empty Panel, the app paints the map in it -->
<main class="panel canvas"></main>
<aside class="sidebar">
<!-- top: explorer -->
<section class="panel">
<div class="page">
<div class="panel-header"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z" /> <path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12" /> <path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17" /></svg><span class="title">Explorer</span></div>
<div class="body">
<div class="explorer-row"><span class="slot"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></svg></span><span class="name">Northern loop</span></div>
<div class="explorer-row depth-1 is-hovered"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="6" cy="19" r="3" /> <path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" /> <circle cx="18" cy="5" r="3" /></svg></span><span class="name">Loop line</span></div>
<div class="explorer-row depth-1"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="6" cy="19" r="3" /> <path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" /> <circle cx="18" cy="5" r="3" /></svg></span><span class="name">Harbour branch</span></div>
<div class="explorer-row depth-1"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /> <circle cx="12" cy="10" r="3" /></svg></span><span class="name">Market Street</span></div>
<div class="explorer-row depth-1"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /> <circle cx="12" cy="10" r="3" /></svg></span><span class="name">Quayside</span></div>
<div class="explorer-row depth-1"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /> <circle cx="12" cy="10" r="3" /></svg></span><span class="name">Mill Lane</span></div>
<div class="explorer-row depth-1"><span class="slot"></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /> <circle cx="12" cy="10" r="3" /></svg></span><span class="name">Northgate</span></div>
<div class="explorer-row"><span class="slot"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></svg></span><span class="name">Labels</span></div>
<div class="explorer-row"><span class="slot"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></span><span class="slot"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 12.5 8 15l2 2.5" /> <path d="m14 12.5 2 2.5-2 2.5" /></svg></span><span class="name">Scripts</span></div>
</div>
</div>
</section>
<!-- bottom: properties tab strip + the selected panel page -->
<section class="panel active">
<div class="tabs">
<div class="tab on"><button class="btn ghost icon-only" aria-label="Canvas"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 15h4" /> <path d="m14.817 10.995-.971-1.45 1.034-1.232a2 2 0 0 0-2.025-3.238l-1.82.364L9.91 3.885a2 2 0 0 0-3.625.748L6.141 6.55l-1.725.426a2 2 0 0 0-.19 3.756l.657.27" /> <path d="m18.822 10.995 2.26-5.38a1 1 0 0 0-.557-1.318L16.954 2.9a1 1 0 0 0-1.281.533l-.924 2.122" /> <path d="M4 12.006A1 1 0 0 1 4.994 11H19a1 1 0 0 1 1 1v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z" /></svg></button></div>
<div class="tab"><button class="btn ghost icon-only" aria-label="Lines"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M9 8h7" /> <path d="M8 12h6" /> <path d="M11 16h5" /></svg></button></div>
<div class="split"></div>
<div class="tab"><button class="btn ghost icon-only" aria-label="Object Properties"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 5H3" /> <path d="M12 19H3" /> <path d="M14 3v4" /> <path d="M16 17v4" /> <path d="M21 12h-9" /> <path d="M21 19h-5" /> <path d="M21 5h-7" /> <path d="M8 10v4" /> <path d="M8 12H3" /></svg></button></div>
<div class="tab"><button class="btn ghost icon-only" aria-label="Script"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 12.5 8 15l2 2.5" /> <path d="m14 12.5 2 2.5-2 2.5" /></svg></button></div>
</div>
<div class="page">
<div class="panel-header"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 15h4" /> <path d="m14.817 10.995-.971-1.45 1.034-1.232a2 2 0 0 0-2.025-3.238l-1.82.364L9.91 3.885a2 2 0 0 0-3.625.748L6.141 6.55l-1.725.426a2 2 0 0 0-.19 3.756l.657.27" /> <path d="m18.822 10.995 2.26-5.38a1 1 0 0 0-.557-1.318L16.954 2.9a1 1 0 0 0-1.281.533l-.924 2.122" /> <path d="M4 12.006A1 1 0 0 1 4.994 11H19a1 1 0 0 1 1 1v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z" /></svg><span class="title">Canvas</span></div>
<div class="body">
<section class="properties-section">
<div class="header"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>Grid</div>
<div class="rows">
<div class="property-row">
<div class="label">Show grid</div>
<div class="value"><span class="checkbox checked"></span></div>
</div>
<div class="property-row">
<div class="label">Grid Z-level</div>
<div class="value"><div class="field secondary center"><input value="0"></div></div>
</div>
<div class="property-row">
<div class="label">Watermark</div>
<div class="value"><span class="checkbox"></span></div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>Bounds</div>
<div class="rows">
<div class="property-row">
<div class="label">Unbounded</div>
<div class="value"><span class="checkbox"></span></div>
</div>
<div class="property-row">
<div class="label">Size</div>
<div class="value">
<div class="btn-group vertical span">
<div class="field secondary center"><span class="unit">W</span><input value="120"></div>
<div class="field secondary center"><span class="unit">H</span><input value="80"></div>
</div>
</div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header"><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>Stations</div>
<div class="rows">
<div class="property-row">
<div class="label">Label size</div>
<div class="value"><div class="field secondary center"><input value="14"><span class="unit">px</span></div></div>
</div>
<div class="property-row">
<div class="label">Size subtitle by scale</div>
<div class="value"><span class="checkbox checked"></span></div>
</div>
</div>
</section>
</div>
</div>
</section>
</aside>
</div>
<!-- footer ChromeBar: muted mono stats -->
<footer class="chrome-bar footer">
<div><span class="mono muted">© 2026 IMBENJI.NET LTD - ARCS &amp; ANGLES</span></div>
<div class="mid"><span class="mono muted">X: 0 · Y: 0</span><span class="vr"></span><span class="mono muted">Zoom: 100%</span></div>
<div class="end">
<span class="mono muted">Segments: 48</span><span class="vr"></span>
<span class="mono muted">Nodes: 112</span><span class="vr"></span>
<span class="mono muted">Stations: 36</span><span class="vr"></span>
<span class="mono muted">v2.4.0</span>
</div>
</footer>
</div>
</body>
</html>
+135
View File
@@ -0,0 +1,135 @@
<!-- @dsCard group="Patterns" -->
<!doctype html>
<html lang="en" data-scheme="carbon" data-density="product">
<head>
<meta charset="utf-8">
<title>Hub form</title>
<link rel="stylesheet" href="../components.css">
<style>
/* scaffolding only - the mock screen and the hub's Measure (railWidth = fontSize*18,
contentMax = fontSize*56, pageGutter = fontSize*3). every value is a token */
html, body { background: var(--chrome); }
.screen { width: 1100px; height: 720px; }
.shell { height: 100%; align-items: stretch; }
.rail { width: calc(var(--font-size) * 18); flex: none; display: flex; flex-direction: column; }
.rail .wordmark { height: calc(var(--font-size) * 9); display: flex; align-items: center; justify-content: center; }
.rail .groups { flex: 1; min-height: 0; overflow: hidden; }
.rail .foot { padding: var(--gap-md) var(--gap-md) var(--gap-lg); }
.workspace { flex: 1; min-width: 0; }
/* contentMax plus a pageGutter either side, centred in the pane */
.column { width: 100%; max-width: calc(var(--font-size) * (56 + 6)); margin: 0 auto; align-self: center;
padding: var(--gap-xxl) calc(var(--font-size) * 3); }
.workspace { display: flex; flex-direction: column; }
.pane-back { padding: var(--gap-sm) var(--gap-sm) 0; }
.column { padding-top: var(--gap-lg); }
.column > .page-heading { margin-bottom: var(--gap-md); }
</style>
</head>
<body>
<div class="screen">
<div class="shell">
<!-- nav rail: a sunken Panel -->
<nav class="panel sunken rail">
<div class="wordmark"><span class="text-lg semibold">Garage</span></div>
<hr class="divider">
<div class="groups">
<div class="nav-group">
<div class="caption">Settings</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /></svg>Account</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> <path d="m9 12 2 2 4-4" /></svg>Security</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 17V7" /> <path d="M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" /> <path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" /></svg>Payments</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 22V7a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5a1 1 0 0 0-1-1H2" /> <rect x="14" y="2" width="8" height="8" rx="1" /></svg>Authorised apps</button>
</div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Developer</div>
<button class="btn secondary stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z" /> <path d="M8 10v4" /> <path d="M12 10v2" /> <path d="M16 10v6" /></svg>Projects</button>
</div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Garage Platform</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z" /> <path d="M20 2v4" /> <path d="M22 4h-4" /> <circle cx="4" cy="20" r="2" /></svg>Assistant</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 15H6a4 4 0 0 0-4 4v2" /> <path d="m14.305 16.53.923-.382" /> <path d="m15.228 13.852-.923-.383" /> <path d="m16.852 12.228-.383-.923" /> <path d="m16.852 17.772-.383.924" /> <path d="m19.148 12.228.383-.923" /> <path d="m19.53 18.696-.382-.924" /> <path d="m20.772 13.852.924-.383" /> <path d="m20.772 16.148.924.383" /> <circle cx="18" cy="15" r="3" /> <circle cx="9" cy="7" r="4" /></svg>Users</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" /> <circle cx="16.5" cy="7.5" r=".5" fill="currentColor" /></svg>Roles</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M15 12h-5" /> <path d="M15 8h-5" /> <path d="M19 17V5a2 2 0 0 0-2-2H4" /> <path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3" /></svg>Audit log</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" /></svg>Status</button>
</div>
</div>
<hr class="divider">
<div class="foot">
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m16 17 5-5-5-5" /> <path d="M21 12H9" /> <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /></svg>Sign out</button>
</div>
</nav>
<!-- workspace: a routed detail, so the pane carries the way back and the page wears its own heading -->
<main class="panel workspace">
<div class="pane-back"><button class="btn ghost"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m12 19-7-7 7-7" /> <path d="M19 12H5" /></svg>Back to project</button></div>
<div class="column">
<div class="page-heading">
<div class="title">Pro licence</div>
<div class="subtitle">Something you sell, and what it goes for.</div>
<div class="trailing">
<button class="btn ghost-destructive"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>Delete</button>
<button class="btn ghost"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>Preview checkout</button>
</div>
</div>
<div class="sub-heading">
<div class="title">Details</div>
<div class="subtitle">What it is and how its sold</div>
</div>
<div class="settings-list">
<div class="settings-row">
<div class="label">Name<span class="tag">Required</span><span class="subtitle">What a buyer sees</span></div>
<div class="field-slot"><div class="field"><input value="Pro licence"></div></div>
</div>
<div class="settings-row">
<div class="label">SKU<span class="subtitle">How an app asks for it. Fixed once created</span></div>
<div class="field-slot"><div class="field readonly mono"><input value="pro_licence" disabled></div></div>
</div>
<div class="settings-row">
<div class="label">Use case<span class="subtitle">What's being sold, which sets the tax</span></div>
<div class="field-slot"><div class="select ghost"><span>Downloadable software</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
<div class="settings-row">
<div class="label">Checkout<span class="subtitle">Who takes the card details</span></div>
<div class="field-slot"><div class="select ghost"><span>Stripe Checkout</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
<div class="settings-row">
<div class="label">Key lifetime<span class="subtitle">Seconds. Empty inherits the project's</span></div>
<div class="field-slot"><div class="field error"><input value="a day"></div></div>
<div class="error">Has to be a whole number of seconds</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Pricing</div>
<div class="subtitle">What it costs, and how often</div>
<div class="action"><button class="btn ghost"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14" /> <path d="M12 5v14" /></svg>Add price</button></div>
</div>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Default price</div><span class="subtitle">What it goes on sale at</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Price</div>
<div class="value"><div class="field secondary"><input value="12.00"><span class="unit">GBP</span></div></div>
</div>
<div class="property-row">
<div class="label">Billing</div>
<div class="value"><div class="select secondary"><span>Monthly</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
</body>
</html>
+151
View File
@@ -0,0 +1,151 @@
<!-- @dsCard group="Patterns" -->
<!doctype html>
<html lang="en" data-scheme="carbon" data-density="product">
<head>
<meta charset="utf-8">
<title>Hub page</title>
<link rel="stylesheet" href="../components.css">
<style>
/* scaffolding only - the mock screen and the hub's Measure (railWidth = fontSize*18,
contentMax = fontSize*56, pageGutter = fontSize*3). every value is a token */
html, body { background: var(--chrome); }
.screen { width: 1100px; height: 720px; }
.shell { height: 100%; align-items: stretch; }
.rail { width: calc(var(--font-size) * 18); flex: none; display: flex; flex-direction: column; }
.rail .wordmark { height: calc(var(--font-size) * 9); display: flex; align-items: center; justify-content: center; }
.rail .groups { flex: 1; min-height: 0; overflow: hidden; }
.rail .foot { padding: var(--gap-md) var(--gap-md) var(--gap-lg); }
.workspace { flex: 1; min-width: 0; }
/* contentMax plus a pageGutter either side, centred in the pane */
.column { width: 100%; max-width: calc(var(--font-size) * (56 + 6)); margin: 0 auto; align-self: center;
padding: var(--gap-xxl) calc(var(--font-size) * 3); }
.column > .page-heading { margin-bottom: var(--gap-xxl); }
</style>
</head>
<body>
<div class="screen">
<div class="shell">
<!-- nav rail: a sunken Panel -->
<nav class="panel sunken rail">
<div class="wordmark"><span class="text-lg semibold">Garage</span></div>
<hr class="divider">
<div class="groups">
<div class="nav-group">
<div class="caption">Settings</div>
<button class="btn secondary stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /></svg>Account</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> <path d="m9 12 2 2 4-4" /></svg>Security</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 17V7" /> <path d="M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" /> <path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" /></svg>Payments</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 22V7a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5a1 1 0 0 0-1-1H2" /> <rect x="14" y="2" width="8" height="8" rx="1" /></svg>Authorised apps</button>
</div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Developer</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z" /> <path d="M8 10v4" /> <path d="M12 10v2" /> <path d="M16 10v6" /></svg>Projects</button>
</div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Garage Platform</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z" /> <path d="M20 2v4" /> <path d="M22 4h-4" /> <circle cx="4" cy="20" r="2" /></svg>Assistant</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 15H6a4 4 0 0 0-4 4v2" /> <path d="m14.305 16.53.923-.382" /> <path d="m15.228 13.852-.923-.383" /> <path d="m16.852 12.228-.383-.923" /> <path d="m16.852 17.772-.383.924" /> <path d="m19.148 12.228.383-.923" /> <path d="m19.53 18.696-.382-.924" /> <path d="m20.772 13.852.924-.383" /> <path d="m20.772 16.148.924.383" /> <circle cx="18" cy="15" r="3" /> <circle cx="9" cy="7" r="4" /></svg>Users</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" /> <circle cx="16.5" cy="7.5" r=".5" fill="currentColor" /></svg>Roles</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M15 12h-5" /> <path d="M15 8h-5" /> <path d="M19 17V5a2 2 0 0 0-2-2H4" /> <path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3" /></svg>Audit log</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" /></svg>Status</button>
</div>
</div>
<hr class="divider">
<div class="foot">
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m16 17 5-5-5-5" /> <path d="M21 12H9" /> <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /></svg>Sign out</button>
</div>
</nav>
<!-- workspace: a Panel with the capped, centred reading column -->
<main class="panel workspace">
<div class="column">
<div class="page-heading">
<div class="title">Account</div>
<div class="subtitle">Your personal details and how you appear on Garage.</div>
</div>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Account</div><span class="subtitle">Your names, sign-in email and account identifiers</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Username</div>
<div class="value">
<div class="btn-group span">
<div class="field secondary readonly grow"><input value="ellis" disabled></div>
<button class="btn secondary">Change</button>
</div>
</div>
</div>
<div class="property-row">
<div class="label">Display name</div>
<div class="value"><div class="field secondary"><input value="Ellis Hart"></div></div>
</div>
<div class="property-row">
<div class="label">Email</div>
<div class="value"><div class="field secondary readonly"><input value="ellis@hartworks.dev" disabled></div></div>
</div>
<div class="property-row">
<div class="label">Member since</div>
<div class="value"><div class="field secondary readonly"><input value="6 Sep 2026" disabled></div></div>
</div>
<div class="property-row">
<div class="label">User ID</div>
<div class="value">
<div class="btn-group span">
<div class="field secondary readonly mono grow"><input value="usr_8f2c41d9a7" disabled></div>
<button class="btn secondary icon-only" aria-label="Copy"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" /> <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></svg></button>
</div>
</div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Personal information</div><span class="subtitle">The name and details on your account</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Legal name</div>
<div class="value"><div class="field secondary readonly"><input value="Ellis Jordan Hart" disabled></div></div>
</div>
<div class="property-row">
<div class="label">Date of birth</div>
<div class="value"><div class="field secondary readonly"><input value="14 Mar 1994" disabled></div></div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Password</div><span class="subtitle">Used to sign in when a passkey isn't available</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Password</div>
<div class="value">
<div class="btn-group span">
<div class="field secondary readonly grow"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg><input value="Hidden" disabled></div>
<button class="btn secondary icon-only" aria-label="Change password"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></svg></button>
</div>
</div>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
</body>
</html>
+112
View File
@@ -0,0 +1,112 @@
<!-- @dsCard group="Patterns" -->
<!doctype html>
<html lang="en" data-scheme="carbon" data-density="compact">
<head>
<meta charset="utf-8">
<title>Settings pane</title>
<link rel="stylesheet" href="../components.css">
<style>
/* scaffolding - the editor's chrome at dialog size: 580 x 420 at compact, a 176 rail
and a content Panel on the chrome ground */
html, body { background: var(--background); }
.stage { width: 700px; height: 520px; display: flex; align-items: center; justify-content: center; }
.pane { width: 580px; height: 420px; border-radius: var(--panel-radius); overflow: hidden; }
.shell { height: 100%; align-items: stretch; }
.rail { width: 176px; flex: none; display: flex; flex-direction: column; }
.rail .search { padding: var(--gap-sm); }
/* the editor's rail is narrower than the hub's, so its categories inset gapSm not gapMd */
.rail .nav-group { padding-inline: var(--gap-sm); }
.content { flex: 1; min-width: 0; }
.content .scroll { padding: var(--gap-xl) var(--gap-sm) var(--gap-xxl); }
.content .page-heading { margin-bottom: var(--gap-lg); }
</style>
</head>
<body>
<div class="stage">
<div class="pane">
<div class="shell">
<nav class="panel sunken rail">
<div class="search"><label class="field stretch"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></svg><input placeholder="Search"></label></div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Settings</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /></svg>Account</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="20" height="14" x="2" y="5" rx="2" /> <line x1="2" x2="22" y1="10" y2="10" /> <path d="M6 14h2" /></svg>Billing</button>
<button class="btn secondary stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z" /> <circle cx="13.5" cy="6.5" r=".5" fill="currentColor" /> <circle cx="17.5" cy="10.5" r=".5" fill="currentColor" /> <circle cx="6.5" cy="12.5" r=".5" fill="currentColor" /> <circle cx="8.5" cy="7.5" r=".5" fill="currentColor" /></svg>Appearance</button>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22v-5" /> <path d="M15 8V2" /> <path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z" /> <path d="M9 8V2" /></svg>Integrations</button>
</div>
<hr class="divider">
<div class="nav-group">
<div class="caption">Editor</div>
<button class="btn ghost stretch align-left"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 8h.01" /> <path d="M12 12h.01" /> <path d="M14 8h.01" /> <path d="M16 12h.01" /> <path d="M18 8h.01" /> <path d="M6 8h.01" /> <path d="M7 16h10" /> <path d="M8 12h.01" /> <rect width="20" height="16" x="2" y="4" rx="2" /></svg>Keymap</button>
</div>
</nav>
<main class="panel content">
<div class="scroll">
<div class="page-heading">
<div class="title">Appearance</div>
<div class="subtitle">How the editor and the map are drawn.</div>
</div>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Editor</div><span class="subtitle">Colours the interface itself is drawn in</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Colour scheme</div>
<div class="value"><div class="select secondary"><span>Carbon</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
<div class="property-row">
<div class="label">Accent</div>
<div class="value"><div class="select secondary"><span>Default</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
<div class="property-row">
<div class="label">Dark mode</div>
<div class="value"><span class="checkbox checked"></span></div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Interface</div><span class="subtitle">How much room a control takes up</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Density</div>
<div class="value"><div class="select secondary"><span>Compact</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
</div>
</section>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Canvas</div><span class="subtitle">The map's own colours, separate from the editor's</span></div>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Colour scheme</div>
<div class="value"><div class="select secondary"><span>Follow editor</span><svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
</div>
<div class="property-row">
<div class="label">Dark mode</div>
<div class="value"><span class="checkbox checked"></span></div>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
</div>
</body>
</html>
+68
View File
@@ -0,0 +1,68 @@
<!-- @dsCard group="Patterns" -->
<!doctype html>
<html lang="en" data-scheme="carbon" data-density="product">
<head>
<meta charset="utf-8">
<title>Page states</title>
<link rel="stylesheet" href="../components.css">
<style>
/* scaffolding - three workspace Panels on the chrome ground, one state each:
loading (a spinner), empty (one muted line), error (muted message + outline retry) */
html, body { background: var(--chrome); }
.screen { width: 1100px; height: 440px; }
.shell { height: 100%; align-items: stretch; }
.shell > .panel { flex: 1; min-width: 0; display: flex; flex-direction: column;
padding: var(--gap-xxl) var(--gap-xl); }
.shell > .panel > .page-heading { margin-bottom: var(--gap-xxl); }
.fill { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--gap-md); }
.note { padding: var(--gap-md) var(--container-padding); display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-md); }
</style>
</head>
<body>
<div class="screen">
<div class="shell">
<!-- loading -->
<section class="panel">
<div class="page-heading">
<div class="title">Payments</div>
<div class="subtitle">What you've bought, and what you're still paying for.</div>
</div>
<div class="fill"><div class="spinner" role="status" aria-label="Loading"></div></div>
</section>
<!-- empty -->
<section class="panel">
<div class="page-heading">
<div class="title">Projects</div>
<div class="subtitle">Applications you build and sell on Garage.</div>
</div>
<div class="fill">
<div class="muted">No projects yet</div>
</div>
</section>
<!-- error: still a section, with nothing in it -->
<section class="panel">
<div class="page-heading">
<div class="title">Security</div>
<div class="subtitle">Your password, passkeys and signed-in devices.</div>
</div>
<section class="properties-section">
<div class="header">
<svg class="icon chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<div><div>Passkeys</div><span class="subtitle">Sign in with your device instead of a password</span></div>
</div>
<div class="rows">
<div class="note">
<div class="muted">Couldn't load your passkeys.</div>
<button class="btn outline">Try again</button>
</div>
</div>
</section>
</section>
</div>
</div>
</body>
</html>