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
+125
View File
@@ -0,0 +1,125 @@
# Garage design system
The look of every Garage app — the hub, the payment portal, Arcs & Angles — as
tokens, CSS and preview cards, for designing new Garage screens.
It's generated from the Flutter package, not drawn separately:
`tokens.css` and `tokens.json` come out of
`garage_ui/tool/export_design_tokens_test.dart`, and `components.css` only
uses those tokens. Don't edit the token files by hand — change garage_ui and
rerun:
```sh
cd garage_ui && flutter test tool/export_design_tokens_test.dart
```
```
design_system/
tokens.css, tokens.json colours, density, type, shape — generated
components.css the components, built on the tokens
foundations/ colour, type, spacing, shape, density
components/ buttons, fields, selects, lists, dialogs, …
patterns/ whole screens from the hub and Arcs & Angles
```
## The one rule: let the theme speak
The colour scheme, the density and the component's variant decide every
colour, font size, weight, icon size, padding, radius and gap. Designing a
Garage screen is choosing **which component** and **which variant**. Then
leave it alone.
- No custom colours, font sizes or weights on a component or its contents.
- No icon sizes or colours — an icon takes them from where it sits.
- No hand-picked padding, gaps or radii. Use the `--gap-*` and `--radius-*`
tokens, and let components that already carry spacing keep it.
- No home-made badges, pills, warning boxes or popovers built from a box with
a fill and a border. If a component doesnt exist, that's a gap in the system,
not an invitation.
- No shadows. Everything is flat — surfaces are separated by a step in
lightness and a hairline border.
If something looks wrong, the answer is a different variant or a different
component.
## Setting up a screen
```html
<html data-scheme="carbon" data-density="product">
<link rel="stylesheet" href="components.css">
```
- **Scheme:** `carbon` (default — true black, no accent hue; what the Garage
apps run), `dark`, `light`.
- **Density:** `product` (default — web and product apps, 12px text, 33px
controls), `compact` (dense desktop tools like Arcs & Angles, 10px text, 23px
controls), `normal` (compact but roomier). Pick one per app. Don't mix.
Type is Geist, and Geist Mono for numbers and readouts. Body text is weight
300, control labels 400, primary buttons 600.
## Layout
Every Garage screen is the same shape: a `chrome` ground, `panel`s standing on
it, and `panel-gap` gutters between them.
```html
<div class="shell">
<nav class="panel sunken">…rail…</nav>
<main class="panel">…workspace…</main>
</div>
```
Headers and footers are `chrome-bar`s. Only one panel is highlighted
(`panel active`) at a time.
A hub page is a centred column inside the workspace panel: a `page-heading`,
then either a stack of `properties-section`s or `sub-heading` + `settings-list`
forms. See `patterns/`.
## Choosing components
**Buttons**
| Variant | For |
|---|---|
| `primary` | the one action on the view, or the thing that's on / chosen |
| `secondary` | a neutral filled control; the selected nav item; a button welded to a field |
| `outline` | a normal action that isn't the main one — Cancel, Retry |
| `ghost` | chrome, toolbars, nav items, row actions |
| `ghost-destructive` | remove / revoke / delete as a row action |
| `destructive` | the confirm button of a destructive dialog |
| `link`, `text` | inline in copy |
Icon-only buttons are `btn icon-only`. A nav item is ghost with a leading icon,
left-aligned, secondary when selected — never primary. Toolbar toggles are
primary when on, outline when off.
**Fields**
- Free text: the default outline `field`.
- Values inside a properties section: `field secondary`. A read-only value is a
read-only secondary field — **never bare text** in a value slot.
- Selects: secondary in a properties section, `ghost` in a settings list,
outline elsewhere.
- Numbers in an editor: secondary, centred, unit as a muted suffix.
**Lists**
- `properties-section` — a boxed card of label/value rows. Inspectors,
account pages, one card per record in a list. Controls inside are secondary.
- `settings-list` — unboxed rows ruled between entries, for forms. Controls
inside are ghost; text fields are outline.
**Subtitles** are short static descriptions of what something is. Never live
data — no counts, dates, names, emails or statuses. Facts go in rows.
**Dialogs** are `dialog` with the actions in their band: outline Cancel, then
a primary or destructive confirm. Max width is fixed; don't widen it.
**Loading** is a spinner. No skeletons. **Empty** is one muted line.
**Error** is the message, muted, and an outline Retry.
+538
View File
@@ -0,0 +1,538 @@
/* garage_ui components, in plain css. Built on tokens.css - every value here
is a token or a formula the flutter package uses too. If something needs a
new number, it goes in garage_ui first and comes out through the export.
The odd literal is only here because the dart source has the same literal,
and its commented where it happens.
Set data-scheme / data-density on <html> (or any container) and everything
below follows.
States: every :hover / :active has an .is-hovered / .is-active twin, so a
card can show a state sat still without anyone pointing at it. */
@import url("tokens.css");
*, *::before, *::after { box-sizing: border-box; }
body, .gu {
margin: 0;
background: var(--background);
color: var(--foreground);
font-family: var(--font-sans);
font-size: var(--font-size);
line-height: var(--line-height);
font-weight: var(--weight-body);
-webkit-font-smoothing: antialiased;
}
/* a nested scheme / density container has to re-resolve the things body set,
or it just inherits the outer values and only the components change */
[data-scheme] { background: var(--background); color: var(--foreground); }
[data-density] { font-size: var(--font-size); line-height: var(--line-height); }
/* ---- type ------------------------------------------------------------ */
.text-xxs { font-size: var(--text-xxs); }
.text-xs { font-size: var(--text-xs); }
.text-sm { font-size: var(--text-sm); }
.text-lg { font-size: var(--text-lg); line-height: 1.3; }
.medium { font-weight: var(--weight-medium); }
.semibold { font-weight: var(--weight-semibold); }
.bold { font-weight: 700; }
.muted { color: var(--muted-foreground); }
.mono { font-family: var(--font-mono); font-weight: 400; }
/* PageHeading - title a step up, muted line under it, gapXs in from the side
(the margin a properties section carries). trailing sits gapMd off the
heading, centred on the whole block not the title line */
.page-heading { padding: 0 var(--gap-xs); display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--gap-md); align-items: center; }
.page-heading > :not(.trailing) { grid-column: 1; }
.page-heading .title { font-size: var(--text-lg); font-weight: var(--weight-control); line-height: 1.3; }
.page-heading .subtitle { margin-top: var(--gap-xs); color: var(--muted-foreground); }
/* the hub puts a gapXs between two asides when theres more then one */
.page-heading > .trailing { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: var(--gap-xs); }
/* subHeading - the same shape one size down, more room over it than under */
.sub-heading { padding: var(--gap-xxl) var(--gap-xs) var(--gap-lg); display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--gap-md); align-items: center; }
.sub-heading > :not(.action) { grid-column: 1; }
.sub-heading .title { font-size: var(--text-sm); font-weight: var(--weight-semibold); }
.sub-heading .subtitle { margin-top: var(--gap-xxs); font-size: var(--text-xxs); color: var(--muted-foreground); }
.sub-heading > .action { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: var(--gap-xs); }
.caption { font-size: var(--text-xxs); font-weight: var(--weight-semibold); color: var(--muted-foreground); }
/* ---- icons ----------------------------------------------------------- */
/* lucide, as inline svg. size comes from where it sits, never the call site */
.icon { width: var(--icon-sm); height: var(--icon-sm); flex: none; stroke: currentColor; fill: none;
stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.icon-md { width: var(--icon-md); height: var(--icon-md); }
.icon-lg { width: var(--icon-lg); height: var(--icon-lg); }
/* ---- buttons --------------------------------------------------------- */
/* every variant shares the same control padding - only the density moves it.
link and text included, they just dont paint a box */
.btn {
display: inline-flex; align-items: center; justify-content: center;
gap: var(--control-gap);
height: var(--control-height);
padding: 0 var(--control-padding-x);
border: var(--control-border-width) solid transparent;
border-radius: var(--radius-md);
background: transparent;
color: var(--foreground);
font: inherit; font-size: var(--font-size); font-weight: var(--weight-control);
white-space: nowrap; cursor: pointer; user-select: none;
outline: none;
}
.btn:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent); }
.btn:disabled, .btn.disabled { cursor: default; color: var(--muted-foreground); }
.btn.icon-only { width: var(--control-height); padding: 0; }
.btn.stretch { display: flex; width: 100%; }
.btn.align-left { justify-content: flex-start; }
.btn.primary { background: var(--primary); border-color: var(--control-border); color: var(--primary-foreground); font-weight: var(--weight-semibold); }
.btn.primary:hover:not(:disabled), .btn.primary.is-hovered { background: var(--primary-hovered); }
.btn.secondary { background: var(--secondary); border-color: var(--control-border); color: var(--secondary-foreground); }
.btn.secondary:hover:not(:disabled), .btn.secondary.is-hovered { background: var(--secondary-hovered); }
/* outline shares the text field's fill + stroke on purpose - one family */
.btn.outline { background: var(--control-fill); border-color: var(--control-border); }
.btn.outline .icon { color: var(--muted-foreground); }
.btn.outline:hover:not(:disabled), .btn.outline.is-hovered { background: var(--control-fill-hovered); }
/* ghost tints what its standing on instead of painting a colour */
.btn.ghost:hover:not(:disabled), .btn.ghost.is-hovered { background: color-mix(in srgb, var(--foreground) 7%, transparent); }
.btn.ghost:active:not(:disabled), .btn.ghost.is-active { background: color-mix(in srgb, var(--foreground) 12%, transparent); }
.btn.ghost-destructive { color: var(--destructive); }
.btn.ghost-destructive:hover:not(:disabled), .btn.ghost-destructive.is-hovered { background: color-mix(in srgb, var(--destructive) 12%, transparent); }
.btn.ghost-destructive:active:not(:disabled), .btn.ghost-destructive.is-active { background: color-mix(in srgb, var(--destructive) 20%, transparent); }
/* white is a literal in button.dart - "straight up white regardless of light or dark mode" */
.btn.destructive { background: color-mix(in srgb, var(--destructive) 50%, transparent); color: #fff; border-color: transparent; }
.btn.destructive:hover:not(:disabled), .btn.destructive.is-hovered { background: color-mix(in srgb, var(--destructive) 80%, transparent); }
.btn.link:hover:not(:disabled), .btn.link.is-hovered { text-decoration: underline; }
.btn.text { color: var(--muted-foreground); }
.btn.text:hover:not(:disabled), .btn.text.is-hovered { color: var(--primary); }
/* disabled, per variant, straight off button.dart. primary goes to a muted
foreground slab so its label still reads; secondary keeps its fill and
only the label dims; destructive swaps to the primary foreground */
.btn.primary:disabled, .btn.primary.disabled { background: var(--muted-foreground); color: var(--primary-foreground); }
.btn.secondary:disabled, .btn.secondary.disabled { background: var(--secondary); color: var(--muted-foreground); }
.btn.ghost-destructive:disabled, .btn.ghost-destructive.disabled { background: transparent; color: var(--muted-foreground); }
.btn.destructive:disabled, .btn.destructive.disabled { background: var(--primary-foreground); color: var(--muted-foreground); }
/* text has no disabled look in the source, it just stays muted */
.btn.text:disabled, .btn.text.disabled { color: var(--muted-foreground); }
/* button group - joined, only the outer corners round */
.btn-group { display: inline-flex; }
.btn-group > * { border-radius: 0; }
.btn-group > *:first-child { border-top-left-radius: var(--radius-md); border-bottom-left-radius: var(--radius-md); }
.btn-group > *:last-child { border-top-right-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
.btn-group > * + * { margin-left: calc(var(--control-border-width) * -1); }
.btn-group.fill { display: flex; }
.btn-group.fill > * { flex: 1; }
/* span: the group takes the width its given and ONE member (.grow) takes the
slack - a field with its IconButton welded on, the button keeping its square */
.btn-group.span { display: flex; width: 100%; }
.btn-group > .grow { flex: 1; min-width: 0; }
.btn-group.vertical { flex-direction: column; }
.btn-group.vertical > * { border-radius: 0; }
.btn-group.vertical > *:first-child { border-radius: var(--radius-md) var(--radius-md) 0 0; }
.btn-group.vertical > *:last-child { border-radius: 0 0 var(--radius-md) var(--radius-md); }
.btn-group.vertical > *:only-child { border-radius: var(--radius-md); }
.btn-group.vertical > * + * { margin-left: 0; margin-top: calc(var(--control-border-width) * -1); }
/* a group inside a group - a corner only rounds where BOTH groups say so
(ButtonGroupCorners.merge, its a product of the two) */
.btn-group:not(.vertical) > .btn-group:not(:first-child) > * { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.btn-group:not(.vertical) > .btn-group:not(:last-child) > * { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.btn-group.vertical > .btn-group:not(:first-child) > * { border-top-left-radius: 0; border-top-right-radius: 0; }
.btn-group.vertical > .btn-group:not(:last-child) > * { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
/* ---- text fields ----------------------------------------------------- */
.field {
display: inline-flex; align-items: center; gap: var(--control-gap);
height: var(--control-height);
padding: 0 calc(var(--control-padding-x) - var(--control-border-width));
background: var(--control-fill);
border: var(--control-border-width) solid var(--control-border);
border-radius: var(--radius-md);
color: var(--foreground);
font-weight: var(--weight-control);
min-width: 0;
}
/* only an outline field that can be typed in reacts - secondary is a flat
secondary fill in every state, and a read only one is just looked at */
.field:not(.secondary):not(.readonly):hover, .field:not(.secondary):not(.readonly).is-hovered { background: var(--control-fill-hovered); }
.field:not(.secondary):not(.readonly):focus-within, .field:not(.secondary):not(.readonly).is-focused { background: var(--control-fill-focused); }
.field input, .field textarea {
all: unset; flex: 1; min-width: 0; width: 100%;
font-family: var(--font-sans); font-size: var(--font-size); font-weight: var(--weight-control);
color: inherit;
}
.field input::placeholder { color: var(--muted-foreground); }
.field .icon, .field .unit { color: var(--muted-foreground); }
.field.secondary { background: var(--secondary); color: var(--secondary-foreground); }
.field.secondary .icon { color: var(--secondary-foreground); }
/* one rule for "you cant change this" - the value AND the icons beside it */
.field.readonly, .field.readonly .icon { color: var(--muted-foreground); }
.field.center input { text-align: center; }
/* ValueField(monospace:) - for ids and skus, read a character at a time */
.field.mono input { font-family: var(--font-mono); }
/* a rejected value: the outline goes full destructive, never the fill */
.field.error { border-color: var(--destructive); }
.field.stretch { display: flex; width: 100%; }
/* ---- select ---------------------------------------------------------- */
/* the value sits in the middle of the TRIGGER - a chevron's worth of nothing
on the left balances the chevron on the right */
.select { display: inline-flex; align-items: center; justify-content: space-between; cursor: pointer; gap: var(--control-gap);
height: var(--control-height); padding: 0 calc(var(--control-padding-x) - var(--control-border-width));
background: var(--control-fill); border: var(--control-border-width) solid var(--control-border);
border-radius: var(--radius-md); color: var(--foreground); font-weight: var(--weight-control); white-space: nowrap; }
.select::before { content: ""; flex: none; width: var(--icon-sm); }
.select > span { flex: 1; min-width: 0; text-align: center; }
.select .placeholder { color: var(--muted-foreground); }
.select .chevron { color: color-mix(in srgb, currentColor 50%, transparent); }
.select:hover, .select.is-hovered { background: var(--control-fill-hovered); }
.select.secondary { background: var(--secondary); color: var(--secondary-foreground); }
.select.secondary:hover, .select.secondary.is-hovered { background: var(--secondary-hovered); }
.select.ghost { background: transparent; border-color: transparent; }
.select.ghost:hover, .select.ghost.is-hovered { background: var(--popover-item-hovered); }
.select.disabled { color: var(--muted-foreground); cursor: default; }
.select.disabled:hover { background: var(--control-fill); }
.select.secondary.disabled:hover { background: var(--secondary); }
.select.ghost.disabled:hover { background: transparent; }
.select.error { border-color: var(--destructive); }
/* the select popup - its own surface, not a menu. inset is half the container
gap, the corner is concentric with the rows (less 2, a literal in
select.dart - the full sum read too soft) */
.select-popup {
display: inline-flex; flex-direction: column; gap: var(--gap-xxs);
padding: calc(var(--container-gap) * 0.5);
background: var(--popover);
border: var(--control-border-width) solid var(--popover-border);
border-radius: calc(var(--radius-md) + var(--container-gap) * 0.5 + var(--control-border-width) - 2px);
color: var(--foreground);
}
.select-popup .option {
display: flex; align-items: center; justify-content: space-between; gap: var(--control-gap);
height: var(--popup-row-height);
padding: 0 calc(var(--control-padding-x) - var(--control-border-width));
border: var(--control-border-width) solid transparent;
border-radius: var(--radius-md);
font-weight: var(--weight-control); white-space: nowrap; cursor: default;
}
.select-popup .option:hover, .select-popup .option.is-hovered { background: var(--popover-item-hovered); }
.select-popup .option.disabled { color: var(--muted-foreground); }
/* the selected row IS a secondary button, stroke and all, with the check */
.select-popup .option.selected { background: var(--secondary); border-color: var(--control-border); color: var(--secondary-foreground); }
.select-popup .option.selected:hover, .select-popup .option.selected.is-hovered { background: var(--secondary-hovered); }
/* once anything is selected every row reserves the check column, mirrored on
the left, so the labels stay centred on the row */
.select-popup:has(.option.selected) .option::before,
.select-popup:has(.option.selected) .option:not(.selected)::after { content: ""; flex: none; width: var(--icon-sm); }
/* ---- popovers, menus, tooltips --------------------------------------- */
/* MenuPopup - the 2px inset and 1px border are literals in menu.dart */
.popover {
display: inline-flex; flex-direction: column;
background: var(--popover);
border: 1px solid var(--popover-border);
border-radius: var(--radius-md);
padding: 2px;
font-weight: var(--weight-control);
}
/* a row: an 18px icon slot and a 4px gap after it, the icon nudged toward the
edge by (4 - padding) / 2. 18, 4 and the 4px row radius are all literals in
menu.dart */
.menu-item {
display: flex; align-items: center; gap: 4px;
height: var(--menu-row-height);
padding: 0 var(--control-padding-x);
border-radius: 4px;
color: var(--foreground); font-weight: var(--weight-control); white-space: nowrap; cursor: default;
}
.menu-item > .icon:first-child { margin: 0 calc((18px - var(--icon-sm)) / 2); position: relative; left: calc((4px - var(--control-padding-x)) / 2); }
.menu-item .icon { color: var(--muted-foreground); }
.menu-item .shortcut { margin-left: auto; padding-left: var(--container-gap); color: var(--muted-foreground); font-size: var(--font-size); }
.menu-item:hover, .menu-item.is-hovered { background: var(--row-hovered); }
.menu-item.disabled { color: color-mix(in srgb, var(--foreground) 50%, transparent); }
.menu-item.disabled .icon { color: color-mix(in srgb, var(--muted-foreground) 50%, transparent); }
.menu-item.disabled:hover { background: transparent; }
/* MenuLabel - same padding as a row, so the same height, just semibold */
.menu-label { display: flex; align-items: center; height: var(--menu-row-height); padding: 0 var(--control-padding-x); font-weight: var(--weight-semibold); white-space: nowrap; }
/* 4px either side is a literal in menu.dart */
.menu-divider { height: 1px; background: var(--divider); margin: 4px 0; }
/* TooltipContainer - three quarters of the container padding across, three
quarters of the container gap down */
.tooltip {
display: inline-block;
background: var(--tooltip-background);
border: 1px solid var(--tooltip-border);
border-radius: var(--radius-sm);
padding: calc(var(--container-gap) * 0.75) calc(var(--container-padding) * 0.75);
color: var(--foreground);
font-size: var(--font-size); font-weight: var(--weight-control);
}
/* ---- selection controls ---------------------------------------------- */
/* 16px box, 1px border - literals in selection_controls.dart */
.checkbox {
display: inline-flex; align-items: center; justify-content: center; flex: none;
width: 16px; height: 16px;
background: var(--secondary);
border: 1px solid var(--control-border);
border-radius: var(--radius-sm);
}
.checkbox.checked { background: var(--primary); border-color: var(--primary); }
/* the tick is drawn, not an icon: a 9 x 6.5 check on a 1px round stroke, same
geometry as AnimatedCheckPainter. the mask box is half a pixel bigger each
side so the round caps dont get clipped */
.checkbox.checked::after {
content: ""; width: 10px; height: 7.5px;
background: var(--primary-foreground);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-0.5 -0.5 10 7.5'%3E%3Cpolyline points='0,3.25 3.15,6.5 9,0' fill='none' stroke='black' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-0.5 -0.5 10 7.5'%3E%3Cpolyline points='0,3.25 3.15,6.5 9,0' fill='none' stroke='black' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
.checkbox.indeterminate::after { content: ""; width: 8px; height: 8px; background: var(--primary); border-radius: var(--radius-xs); }
.checkbox.disabled { border-color: var(--muted); }
/* (32 + 4) x (16 + 4) is literal in the source; the inset is a quarter of the
container gap and the thumb is a square filling whats left */
.switch {
display: inline-flex; align-items: stretch; flex: none;
width: 36px; height: 20px; padding: calc(var(--container-gap) * 0.25);
background: var(--switch-track-inactive);
border-radius: var(--radius-xl);
}
.switch::after { content: ""; height: 100%; aspect-ratio: 1; border-radius: var(--radius-lg); background: var(--foreground); }
.switch.on { background: var(--primary); justify-content: flex-end; }
.switch.on::after { background: var(--background); }
.switch.disabled { background: var(--muted); }
.switch.disabled::after { background: var(--muted-foreground); }
/* ---- surfaces & layout ----------------------------------------------- */
/* the whole look: chrome ground, panels standing on it, panel-gap gutters */
.shell { background: var(--chrome); padding: var(--panel-gap); display: flex; gap: var(--panel-gap); }
.panel {
background: var(--background);
border: var(--panel-border-width) solid var(--panel-border);
border-radius: var(--panel-radius);
overflow: hidden;
}
.panel.active { border-color: var(--panel-border-highlighted); }
.panel.sunken { background: var(--surface-sunken); }
.chrome-bar {
display: flex; align-items: center; gap: var(--gap-sm);
height: var(--chrome-bar-height);
background: var(--chrome);
padding: 0 var(--gap-lg);
}
.chrome-bar .vr { width: 1px; height: 12px; background: var(--divider); }
.panel-header { display: flex; align-items: center; gap: 2px; padding: 10px 10px 8px; }
.panel-header .icon { width: 12px; height: 12px; margin: 0 3px; color: var(--foreground); }
.panel-header .title { font-weight: var(--weight-medium); flex: 1; }
/* a nav rail's category, as the hub draws it - gapMd round the run, a
semibold caption over it, gapXxs between the buttons only */
.nav-group { padding: var(--gap-md); display: flex; flex-direction: column; gap: var(--gap-xxs); }
.nav-group > .caption { padding: 0 var(--gap-sm) calc(var(--gap-xs) - var(--gap-xxs)); }
/* explorer tree rows (A&A). an 18px slot for the chevron and one for the
icon, 2px between, 12px glyphs - all literals in explorer_panel.dart. the
hover is inset 2px with a 3px corner, same file */
.explorer-row {
--depth: 0;
position: relative; isolation: isolate;
display: flex; align-items: center; gap: 2px;
height: var(--explorer-row-height);
padding: 0 var(--explorer-row-end-padding) 0 calc(var(--explorer-row-base-padding) + var(--depth) * 18px);
color: var(--row-text); white-space: nowrap;
}
.explorer-row.depth-1 { --depth: 1; }
.explorer-row.depth-2 { --depth: 2; }
.explorer-row.depth-3 { --depth: 3; }
.explorer-row > .slot { flex: none; width: 18px; display: flex; align-items: center; justify-content: center; }
.explorer-row .icon { width: 12px; height: 12px; }
.explorer-row .chevron { color: var(--muted-foreground); }
.explorer-row > .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.explorer-row:hover::before, .explorer-row.is-hovered::before {
content: ""; position: absolute; inset: 2px; z-index: -1;
background: var(--row-hovered); border-radius: 3px;
}
.card {
background: var(--card);
border: 1px solid var(--properties-section-border);
border-radius: var(--radius-xl);
padding: var(--container-padding);
}
.outlined {
background: var(--background);
border: 1px solid var(--control-border);
border-radius: var(--radius-xl);
}
.divider { height: 1px; background: var(--divider); border: 0; margin: 0; }
/* ---- properties section --------------------------------------------- */
.properties-section {
margin: 0 var(--gap-xs) var(--properties-section-gap);
background: var(--card);
border: 1px solid var(--properties-section-border);
border-radius: var(--radius-sm);
overflow: hidden;
}
.properties-section > .header {
display: flex; align-items: center; gap: var(--gap-sm);
padding: var(--control-padding-y) var(--control-padding-x);
color: var(--row-text); font-weight: var(--weight-semibold);
}
.properties-section > .header .chevron { color: var(--muted-foreground); }
/* the header's own subtitle - captionStyle, so the ambient weight not the title's */
.properties-section > .header .subtitle { display: block; font-size: var(--text-xxs); color: var(--muted-foreground); font-weight: var(--weight-body); }
.properties-section > .header .trailing { margin-left: auto; font-weight: var(--weight-control); }
/* the last row gets more room under it than the step between rows */
.properties-section > .rows { padding: 0 var(--control-padding-x) var(--gap-md); display: flex; flex-direction: column; gap: var(--gap-xs); }
.properties-section > .actions {
display: flex; justify-content: flex-end; gap: var(--gap-sm);
border-top: 1px solid var(--properties-section-border);
background: color-mix(in srgb, var(--card) 55%, var(--muted));
padding: var(--gap-sm) var(--control-padding-x);
}
/* PropertyRow - the label up to the split, the control from it. the gap comes
out of the label's padding, so the control's edge lands right on the split.
it wraps, so an .error or .description can take a line of its own under it */
.property-row { display: flex; flex-wrap: wrap; align-items: center; }
.property-row > .label { flex: 0 0 calc(var(--property-split) * 100%); padding-right: var(--property-label-gap); min-width: 0; color: var(--row-text); text-align: right; }
.property-row > .label .subtitle { display: block; font-size: var(--text-xxs); color: var(--muted-foreground); }
.property-row > .label .tag { font-size: var(--text-xxs); color: var(--muted-foreground); margin-left: var(--gap-xs); }
.property-row > .value { flex: 1 1 0; min-width: 0; min-height: var(--property-row-height); display: flex; align-items: center; gap: var(--control-gap); }
.property-row > .value > .field, .property-row > .value > .select, .property-row > .value > .btn-group.span { flex: 1; }
/* why the value was refused - under the row, centred, destructive */
.property-row > .error { flex: 0 0 100%; margin-top: var(--gap-xs); text-align: center; font-size: var(--text-xxs); color: var(--destructive); }
/* copy about the setting, in a box that matches the outline controls */
.property-row > .description {
flex: 0 0 100%; margin-top: var(--gap-xs);
padding: var(--gap-xs) var(--control-padding-x);
background: var(--control-fill);
border: var(--control-border-width) solid var(--control-border);
border-radius: var(--radius-md);
text-align: center; font-size: var(--text-xxs); color: var(--muted-foreground);
}
/* ---- settings list --------------------------------------------------- */
/* ruled BETWEEN rows, and the rule overshoots the text by the row inset so it
reads as a divider rather than the edge of a box */
.settings-list { display: flex; flex-direction: column; }
.settings-list > .settings-row + .settings-row { border-top: 1px solid var(--divider); }
.settings-row { display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--gap-md); padding: var(--gap-md) var(--gap-sm); }
.settings-row > .label { flex: 1 1 0; min-width: 0; font-size: var(--text-xs); font-weight: var(--weight-control); color: var(--foreground); }
.settings-row > .label .subtitle { display: block; margin-top: var(--gap-xxs); font-size: var(--text-xxs); font-weight: var(--weight-body); color: var(--muted-foreground); }
.settings-row > .label .tag { font-size: var(--text-xxs); font-weight: var(--weight-control); color: var(--muted-foreground); margin-left: var(--gap-xs); }
/* a loose Flexible: at most what the label leaves, field pinned right. a
select or a button keeps its own width, a text field fills the slot.
the minimum lives here rather than on the label so the label can stay a
plain block - its the height of a row WITH a subtitle, so a list mixing the
two doesnt come out ragged */
.settings-row > .field-slot {
flex: 1 1 0; min-width: 0;
min-height: calc(var(--label-column-height) + var(--gap-xxs));
display: flex; align-items: center; justify-content: flex-end;
}
.settings-row > .field-slot > .field { flex: 1; }
.settings-row > .error { flex: 0 0 100%; margin-top: var(--gap-sm); font-size: var(--text-xxs); color: var(--destructive); }
.settings-row > .description { flex: 0 0 100%; margin-top: var(--gap-sm); font-size: var(--text-xxs); color: var(--muted-foreground); }
.settings-row > .error + .description { margin-top: var(--gap-xxs); }
/* ---- dialogs, toasts ------------------------------------------------- */
/* AlertDialog's barrier - black at 0.8, no blur */
.modal-backdrop { background: rgb(0 0 0 / 0.8); }
/* pane overlays keep the lighter frosted scrim */
.scrim { background: color-mix(in srgb, var(--background) 35%, transparent); backdrop-filter: blur(3px); }
.dialog {
width: 100%; max-width: var(--dialog-max-width);
background: var(--card);
border: 1px solid var(--properties-section-border);
border-radius: var(--radius-xxl);
overflow: hidden;
}
.dialog > .body { padding: calc(var(--container-padding) * 1.2) calc(var(--container-padding) * 1.4) calc(var(--container-padding) * 0.6); display: flex; flex-direction: column; gap: var(--container-gap); }
.dialog > .body .title { font-size: var(--text-xs); font-weight: var(--weight-semibold); }
.dialog > .body .content { font-size: var(--text-xs); color: var(--muted-foreground); }
.dialog > .actions {
display: flex; justify-content: flex-end; gap: var(--container-gap);
border-top: 1px solid var(--properties-section-border);
background: color-mix(in srgb, var(--card) 55%, var(--muted));
padding: var(--gap-sm) var(--control-padding-x);
}
/* the desktop toast. 300 wide, the 32px badge, the 18px glyph, the white on
it and the 3px bar are all literals in toast.dart */
.toast {
--toast-accent: var(--primary);
display: flex; flex-direction: column;
width: 300px;
background: var(--popover);
border: 1px solid var(--popover-border);
border-radius: var(--radius-md);
overflow: hidden;
}
.toast.success { --toast-accent: var(--chart-2); }
.toast.error { --toast-accent: var(--destructive); }
.toast > .body { display: flex; align-items: flex-start; gap: var(--container-gap); padding: var(--container-gap); }
.toast .badge {
flex: none; width: 32px; height: 32px;
display: flex; align-items: center; justify-content: center;
border: var(--control-border-width) solid var(--control-border);
border-radius: var(--radius-md);
color: #fff;
}
.toast .badge .icon { width: 18px; height: 18px; }
.toast .text { flex: 1; min-width: 0; }
/* title and subtitle are the same text, told apart by colour only */
.toast .title { font-weight: var(--weight-control); color: var(--foreground); }
.toast .subtitle { margin-top: calc(var(--container-gap) * 0.375); font-weight: var(--weight-control); color: var(--muted-foreground); }
/* the countdown - the accent drains off the right, over the border colour at 0.4 */
.toast > .drain { display: flex; height: 3px; background: color-mix(in srgb, var(--popover-border) 40%, transparent); }
.toast > .drain > span { background: var(--toast-accent); }
/* CircularProgressIndicator - no track, a 1.35pi arc (~243deg) on a 4px
round stroke, 18px, primary, once round every 900ms. all literal in
misc.dart. the two dots are the round caps */
.spinner {
position: relative; flex: none;
width: 18px; height: 18px; border-radius: 50%;
background: conic-gradient(var(--primary) 0 243deg, transparent 0);
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3.5px));
mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3.5px));
animation: gu-spin 0.9s linear infinite;
}
.spinner::before, .spinner::after {
content: ""; position: absolute; left: 7px; top: 0;
width: 4px; height: 4px; border-radius: 50%;
background: var(--primary);
transform-origin: 2px 9px;
}
.spinner::after { transform: rotate(243deg); }
@keyframes gu-spin { to { transform: rotate(360deg); } }
@@ -0,0 +1,94 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Button groups</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-block { padding: 0 var(--gap-xs); }
.ds-half + .ds-half { margin-top: var(--gap-lg); }
.ds-block > .caption { padding-bottom: var(--gap-xs); }
.ds-half { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-xl); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Button groups</div>
<div class="subtitle">Controls joined into one. Only the outer corners round.</div>
</div>
<div class="ds-half">
<div class="ds-block">
<div class="caption">Button welded to a field</div>
<div class="btn-group span">
<label class="field grow"><input value="gk_live_4f8a2c91" aria-label="Client ID"></label>
<button class="btn secondary icon-only" aria-label="Copy"><svg class="icon" viewBox="0 0 24 24"><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 class="ds-block">
<div class="caption">Segmented choice</div>
<div class="btn-group fill">
<button class="btn secondary">Off</button>
<button class="btn primary">Optional</button>
<button class="btn secondary">Required</button>
</div>
</div>
</div>
<div class="ds-half">
<div class="ds-block">
<div class="caption">Vertical, with a group nested in it</div>
<div class="btn-group vertical span">
<label class="field"><input placeholder="Email" aria-label="Email"></label>
<div class="btn-group span">
<label class="field grow"><input placeholder="Password" type="password" aria-label="Password"></label>
<button class="btn outline icon-only" aria-label="Show password"><svg class="icon" viewBox="0 0 24 24"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" /><circle cx="12" cy="12" r="3" /></svg></button>
</div>
</div>
</div>
<div class="ds-block">
<div class="caption">Toolbar toggles - primary on, outline off</div>
<div class="chrome-bar">
<button class="btn primary icon-only" aria-label="Bold"><svg class="icon" viewBox="0 0 24 24"><path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" /></svg></button>
<button class="btn outline icon-only" aria-label="Italic"><svg class="icon" viewBox="0 0 24 24"><line x1="19" x2="10" y1="4" y2="4" /><line x1="14" x2="5" y1="20" y2="20" /><line x1="15" x2="9" y1="4" y2="20" /></svg></button>
<button class="btn outline icon-only" aria-label="Underline"><svg class="icon" viewBox="0 0 24 24"><path d="M6 4v6a6 6 0 0 0 12 0V4" /><line x1="4" x2="20" y1="20" y2="20" /></svg></button>
<span class="vr"></span>
<div class="btn-group">
<button class="btn primary icon-only" aria-label="Align left"><svg class="icon" viewBox="0 0 24 24"><path d="M21 5H3" /><path d="M15 12H3" /><path d="M17 19H3" /></svg></button>
<button class="btn outline icon-only" aria-label="Align centre"><svg class="icon" viewBox="0 0 24 24"><path d="M21 5H3" /><path d="M17 12H7" /><path d="M19 19H5" /></svg></button>
<button class="btn outline icon-only" aria-label="Align right"><svg class="icon" viewBox="0 0 24 24"><path d="M21 5H3" /><path d="M21 12H9" /><path d="M21 19H7" /></svg></button>
</div>
</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>A field with its one action welded on: a spanning group, the field grows, then a secondary IconButton.</li>
<li>Groups nest. A corner only rounds where both groups agree it's an outer one.</li>
<li>A segmented choice: fill the group, chosen one primary, the rest secondary, labels centred.</li>
<li>Editor toolbar toggles: primary when on, outline when off.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Leave a segmented control shrunk to its labels. It should span its field.</li>
<li>Mix the primary-on toggle pattern and Toggle on the same surface.</li>
<li>Use a secondary off state unless its a master switch whose off state matters.</li>
</ul>
</main>
</body>
</html>
+130
View File
@@ -0,0 +1,130 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Buttons</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-sm); padding: 0 var(--gap-xs); }
.ds-block + .ds-block { margin-top: var(--gap-lg); }
.ds-block > .caption { padding: 0 var(--gap-xs) var(--gap-xs); }
.ds-table { width: 100%; border-collapse: collapse; }
.ds-table td { padding: var(--gap-xs) var(--gap-xs); border-top: var(--control-border-width) solid var(--divider); vertical-align: middle; }
.ds-table tr:first-child td { border-top: 0; }
.ds-table td:first-child { width: 30%; }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Buttons</div>
<div class="subtitle">Pick the variant, then stop. The theme does the rest.</div>
</div>
<div class="ds-block">
<div class="caption">Variants</div>
<div class="ds-row">
<button class="btn primary">Save</button>
<button class="btn secondary">Duplicate</button>
<button class="btn outline">Cancel</button>
<button class="btn ghost">Settings</button>
<button class="btn ghost-destructive">Remove</button>
<button class="btn destructive">Delete</button>
<button class="btn link">Learn more</button>
<button class="btn text">Forgot password?</button>
</div>
</div>
<div class="ds-block">
<div class="caption">With a leading icon</div>
<div class="ds-row">
<button class="btn primary"><svg class="icon" viewBox="0 0 24 24"><path d="M5 12h14" /><path d="M12 5v14" /></svg>New product</button>
<button class="btn outline"><svg class="icon" viewBox="0 0 24 24"><path d="M12 15V3" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="m7 10 5 5 5-5" /></svg>Export</button>
<button class="btn ghost"><svg class="icon" viewBox="0 0 24 24"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>Settings</button>
<button class="btn ghost-destructive"><svg class="icon" viewBox="0 0 24 24"><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>Revoke</button>
</div>
</div>
<div class="ds-block">
<div class="caption">Icon only - IconButton, never a Button with just an icon in it</div>
<div class="ds-row">
<button class="btn primary icon-only" aria-label="Add"><svg class="icon" viewBox="0 0 24 24"><path d="M5 12h14" /><path d="M12 5v14" /></svg></button>
<button class="btn secondary icon-only" aria-label="Copy"><svg class="icon" viewBox="0 0 24 24"><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>
<button class="btn outline icon-only" aria-label="Refresh"><svg class="icon" viewBox="0 0 24 24"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" /><path d="M21 3v5h-5" /><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" /><path d="M8 16H3v5" /></svg></button>
<button class="btn ghost icon-only" aria-label="Edit"><svg class="icon" viewBox="0 0 24 24"><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>
<button class="btn ghost-destructive icon-only" aria-label="Delete"><svg class="icon" viewBox="0 0 24 24"><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></button>
</div>
</div>
<div class="ds-block">
<div class="caption">Disabled</div>
<div class="ds-row">
<button class="btn primary" disabled>Save</button>
<button class="btn secondary" disabled>Duplicate</button>
<button class="btn outline" disabled>Cancel</button>
<button class="btn ghost" disabled>Settings</button>
<button class="btn ghost-destructive" disabled>Remove</button>
<button class="btn destructive" disabled>Delete</button>
<button class="btn link" disabled>Learn more</button>
</div>
</div>
<div class="ds-block">
<div class="caption">Hovered</div>
<div class="ds-row">
<button class="btn primary is-hovered">Save</button>
<button class="btn secondary is-hovered">Duplicate</button>
<button class="btn outline is-hovered">Cancel</button>
<button class="btn ghost is-hovered">Settings</button>
<button class="btn ghost-destructive is-hovered">Remove</button>
<button class="btn destructive is-hovered">Delete</button>
<button class="btn link is-hovered">Learn more</button>
<button class="btn text is-hovered">Forgot password?</button>
</div>
</div>
<div class="sub-heading">
<div class="title">Variants mean something</div>
</div>
<table class="ds-table">
<tr><td><button class="btn primary">Primary</button></td><td>The one action, or the thing thats on or chosen. One per view, ideally.</td></tr>
<tr><td><button class="btn secondary">Secondary</button></td><td>A neutral filled control. A resting toggle, a button welded to a field, the selected nav item.</td></tr>
<tr><td><button class="btn outline">Outline</button></td><td>A normal action that isnt the main one. Cancel, Retry, Previous and Next.</td></tr>
<tr><td><button class="btn ghost">Ghost</button></td><td>Chrome, toolbars, nav items, row actions. Anything that shouldnt compete.</td></tr>
<tr><td><button class="btn destructive">Destructive</button></td><td>The confirm button of a destructive dialog.</td></tr>
<tr><td><button class="btn ghost-destructive">Ghost destructive</button></td><td>A destructive trigger that isnt a filled slab. Remove, Revoke, Delete in a row.</td></tr>
<tr><td><button class="btn link">Link</button> <button class="btn text">Text</button></td><td>Inline, in running copy.</td></tr>
</table>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Dialogs: Cancel is outline, confirm is primary or destructive.</li>
<li>Retry after an error, and an empty state's call to action: outline.</li>
<li>Panel header actions: a ghost IconButton, each in a Tooltip.</li>
<li>An action in a settings row: ghost. In a property row: secondary.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Bold a button label, or colour its text or icon by hand.</li>
<li>Hand-colour a ghost button red. Thats what ghost destructive is for.</li>
<li>Set an alignment unless the button is stretched wider than its label.</li>
<li>Force a compact density on a button to make it fit.</li>
</ul>
</main>
</body>
</html>
+66
View File
@@ -0,0 +1,66 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dialogs</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-stage { display: flex; flex-direction: column; align-items: center; gap: var(--gap-xl); padding: var(--gap-xxl) var(--container-padding); border-radius: var(--radius-xl); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Dialogs</div>
<div class="subtitle">A small confirm, over a dimmed page.</div>
</div>
<div class="modal-backdrop ds-stage">
<div class="dialog" role="alertdialog" aria-labelledby="d1-title">
<div class="body">
<div class="title" id="d1-title">Delete this product?</div>
<div class="content">People who already bought it keep their licence. It cant be sold again.</div>
</div>
<div class="actions">
<button class="btn outline">Cancel</button>
<button class="btn destructive">Delete</button>
</div>
</div>
<div class="dialog" role="alertdialog" aria-labelledby="d2-title">
<div class="body">
<div class="title" id="d2-title">Publish changes?</div>
<div class="content">The new price goes live at checkout straight away.</div>
</div>
<div class="actions">
<button class="btn outline">Cancel</button>
<button class="btn primary">Publish</button>
</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>showDialog with an AlertDialog: a title, a line of content and its actions.</li>
<li>Cancel is outline. Confirm is primary, or destructive when it removes something.</li>
<li>Bigger jobs, like settings or export, go in a pane overlay instead.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Constrain the dialog's width. It caps itself at the dialog max width.</li>
<li>Build the action row by hand. The dialog draws its own band.</li>
</ul>
</main>
</body>
</html>
+110
View File
@@ -0,0 +1,110 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Feedback</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-row { display: flex; flex-wrap: wrap; gap: var(--gap-md); padding: 0 var(--gap-xs); align-items: flex-start; }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap-xl); padding: 0 var(--gap-xs); }
.ds-cell { display: flex; flex-direction: column; gap: var(--gap-xs); }
.ds-state { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--gap-sm); padding: var(--gap-xxl) var(--gap-md); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Feedback</div>
<div class="subtitle">Toasts, and the three states a page can be in.</div>
</div>
<div class="ds-row">
<div class="toast" role="status">
<div class="body">
<div class="badge"><svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg></div>
<div class="text">
<div class="title">Link copied</div>
<div class="subtitle">Paste it anywhere a buyer can reach.</div>
</div>
<button class="btn outline icon-only" aria-label="Close"><svg class="icon" viewBox="0 0 24 24"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
<div class="drain"><span style="width: 80%"></span></div>
</div>
<div class="toast success" role="status">
<div class="body">
<div class="badge"><svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg></div>
<div class="text">
<div class="title">Saved</div>
<div class="subtitle">Your changes are live.</div>
</div>
<button class="btn outline icon-only" aria-label="Close"><svg class="icon" viewBox="0 0 24 24"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
<div class="drain"><span style="width: 45%"></span></div>
</div>
<div class="toast error" role="alert">
<div class="body">
<div class="badge"><svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg></div>
<div class="text">
<div class="title">Couldnt save</div>
<div class="subtitle">Check your connection and try again.</div>
</div>
<button class="btn outline icon-only" aria-label="Close"><svg class="icon" viewBox="0 0 24 24"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
<div class="drain"><span style="width: 20%"></span></div>
</div>
</div>
<div class="sub-heading">
<div class="title">Loading, empty, error</div>
<div class="subtitle">The plainest thing that works.</div>
</div>
<div class="ds-grid">
<div class="ds-cell">
<div class="caption">Loading</div>
<div class="ds-state outlined">
<div class="spinner" role="progressbar" aria-label="Loading"></div>
</div>
</div>
<div class="ds-cell">
<div class="caption">Empty</div>
<div class="ds-state outlined">
<div class="text-xs muted">No coupons yet.</div>
</div>
</div>
<div class="ds-cell">
<div class="caption">Error</div>
<div class="ds-state outlined">
<div class="text-xs muted">Couldnt load orders.</div>
<button class="btn outline">Retry</button>
</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>showAppToast with a title, a subtitle and a severity: info, success or error. The severity colours the drain bar, nothing else.</li>
<li>Loading is a spinner. Empty is one muted line. Error is the message muted and an outline Retry.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Use skeletons or shimmer.</li>
<li>Hand-build a toast out of a card and some padding.</li>
<li>Hand-build a warning box. A page that wants more is asking for a new garage_ui component.</li>
</ul>
</main>
</body>
</html>
+73
View File
@@ -0,0 +1,73 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Menus and tooltips</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap-xl); padding: 0 var(--gap-xs); align-items: start; }
.ds-cell { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-xs); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Menus and tooltips</div>
<div class="subtitle">Floating surfaces. Flat, no shadow.</div>
</div>
<div class="ds-grid">
<div class="ds-cell">
<div class="caption">Menu</div>
<div class="popover" role="menu">
<div class="menu-label">Edit</div>
<div class="menu-item" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><circle cx="6" cy="6" r="3" /><path d="M8.12 8.12 12 12" /><path d="M20 4 8.12 15.88" /><circle cx="6" cy="18" r="3" /><path d="M14.8 14.8 20 20" /></svg>Cut<span class="shortcut">⌘ + X</span></div>
<div class="menu-item is-hovered" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><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>Copy<span class="shortcut">⌘ + C</span></div>
<div class="menu-item" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><path d="M11 14h10" /><path d="M16 4h2a2 2 0 0 1 2 2v1.344" /><path d="m17 18 4-4-4-4" /><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" /><rect x="8" y="2" width="8" height="4" rx="1" /></svg>Paste<span class="shortcut">⌘ + V</span></div>
<div class="menu-divider"></div>
<div class="menu-item" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><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</div>
</div>
</div>
<div class="ds-cell">
<div class="caption">Context menu</div>
<div class="popover" role="menu">
<div class="menu-item" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /><path d="M3 3v5h5" /></svg>Reset to Default</div>
<div class="menu-item" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><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>Copy</div>
<div class="menu-item disabled" aria-disabled="true" role="menuitem"><svg class="icon" viewBox="0 0 24 24"><path d="M11 14h10" /><path d="M16 4h2a2 2 0 0 1 2 2v1.344" /><path d="m17 18 4-4-4-4" /><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" /><rect x="8" y="2" width="8" height="4" rx="1" /></svg>Paste</div>
</div>
</div>
<div class="ds-cell">
<div class="caption">Tooltip</div>
<button class="btn ghost icon-only" aria-label="Copy"><svg class="icon" viewBox="0 0 24 24"><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>
<span class="tooltip" role="tooltip">Copy client ID</span>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>MenuButton rows, with MenuLabel and MenuDivider to group them.</li>
<li>showContextMenu for right-click. Property rows get Reset, Copy and Paste, greyed when theres nothing to do.</li>
<li>A tooltip on every icon-only button, with plain text in it.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Hand-build a popover out of a container with a fill, border and radius.</li>
<li>Colour tooltip text, or pad the tooltip yourself.</li>
<li>Add a shadow to a menu. Surfaces are separated by their border and fill.</li>
</ul>
</main>
</body>
</html>
@@ -0,0 +1,94 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Properties section</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Properties section</div>
<div class="subtitle">A boxed card of label and value rows.</div>
</div>
<div class="properties-section">
<div class="header" role="button" aria-expanded="true">
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6" /></svg>
<span>Subscription</span>
<span class="trailing muted">Active</span>
</div>
<div class="rows">
<div class="property-row">
<div class="label">Plan</div>
<div class="value"><label class="field secondary readonly"><input value="Pro, monthly" readonly disabled aria-label="Plan"></label></div>
</div>
<div class="property-row">
<div class="label">Seats<span class="subtitle">People who can sign in</span></div>
<div class="value"><label class="field secondary error"><input value="0" aria-label="Seats"></label></div>
<div class="error">Needs at least one seat</div>
</div>
<div class="property-row">
<div class="label">Billing</div>
<div class="value">
<div class="select secondary" role="button">
<span>Monthly</span>
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg>
</div>
</div>
<div class="description">Switching takes effect from the next invoice, not this one.</div>
</div>
<div class="property-row">
<div class="label">Invoices</div>
<div class="value"><button class="btn secondary">Open billing portal</button></div>
</div>
</div>
<div class="actions">
<button class="btn ghost">Reset</button>
<button class="btn ghost-destructive">Cancel plan</button>
</div>
</div>
<div class="sub-heading">
<div class="title">The value slot is never bare text</div>
<div class="subtitle">A read-only value is a secondary field, read only and disabled.</div>
</div>
<ul class="ds-rules">
<li>Beside real fields, bare text reads as a caption and the column goes ragged.</li>
<li>A button in the value slot sits left and keeps its own width. It doesnt stretch.</li>
<li>A refused value says why in the row's error, under it. Copy about the setting goes in the description box.</li>
</ul>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Inspectors, account and security pages, and lists of records, one section per record.</li>
<li>A status or count in the header's trailing slot. It stays visible when collapsed.</li>
<li>What the section can do, in the actions band under the rows.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Use outline fields or ghost selects in here. Controls in a section are secondary.</li>
<li>Put live data in the subtitle. No counts, dates, names or statuses.</li>
<li>Add gaps between sections. Each one carries its own margin.</li>
</ul>
</main>
</body>
</html>
+84
View File
@@ -0,0 +1,84 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Select</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap-xl); padding: 0 var(--gap-xs); align-items: start; }
.ds-cell { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-xs); min-width: 0; }
.ds-open { display: flex; flex-direction: column; gap: var(--gap-xs); align-items: flex-start; padding: var(--gap-lg) var(--gap-xs) 0; }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Select</div>
<div class="subtitle">One value from a list, in a popup.</div>
</div>
<div class="ds-grid">
<div class="ds-cell">
<div class="caption">Outline - the default</div>
<div class="select" role="button">
<span>Engineering</span>
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg>
</div>
</div>
<div class="ds-cell">
<div class="caption">Secondary - in a property row</div>
<div class="select secondary" role="button">
<span>Monthly</span>
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg>
</div>
</div>
<div class="ds-cell">
<div class="caption">Ghost - in a settings row</div>
<div class="select ghost" role="button">
<span>All regions</span>
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg>
</div>
</div>
</div>
<div class="ds-open">
<div class="caption">Open</div>
<div class="select" role="button">
<span>Engineering</span>
<svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg>
</div>
<div class="select-popup" role="listbox">
<div class="option" role="option">Design</div>
<div class="option selected" role="option" aria-selected="true">Engineering<svg class="icon" viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5" /></svg></div>
<div class="option is-hovered" role="option">Finance</div>
<div class="option" role="option">Support</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Outline by default, beside other outline fields.</li>
<li>Secondary in a property row. The row forces it anyway.</li>
<li>Ghost in a settings row, or where it reports a value rather than offering a control.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Give a toolbar select a literal width.</li>
<li>Put a ghost select inside a properties section.</li>
</ul>
</main>
</body>
</html>
@@ -0,0 +1,59 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Selection controls</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-xl); padding: 0 var(--gap-xs); }
.ds-cell { display: flex; flex-direction: column; gap: var(--gap-md); }
.ds-cell > .caption { margin-bottom: calc(var(--gap-xs) - var(--gap-md)); }
.ds-pair { display: inline-flex; align-items: center; gap: var(--gap-sm); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Selection controls</div>
<div class="subtitle">On or off. Checkbox or Switch, one per surface.</div>
</div>
<div class="ds-grid">
<div class="ds-cell">
<div class="caption">Checkbox</div>
<label class="ds-pair"><span class="checkbox" role="checkbox" aria-checked="false"></span>Snap to grid</label>
<label class="ds-pair"><span class="checkbox checked" role="checkbox" aria-checked="true"></span>Show labels</label>
</div>
<div class="ds-cell">
<div class="caption">Switch</div>
<label class="ds-pair"><span class="switch" role="switch" aria-checked="false"></span>Email receipts</label>
<label class="ds-pair"><span class="switch on" role="switch" aria-checked="true"></span>Active</label>
<label class="ds-pair muted"><span class="switch disabled" role="switch" aria-checked="false" aria-disabled="true"></span>Managed by your organisation</label>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Checkbox in dense editor panels. Arcs and Angles uses it throughout.</li>
<li>Switch in rows on a product page. The hub uses it in settings rows.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Mix checkboxes and switches on one surface.</li>
<li>Recolour the track, thumb or tick. The accent is primary.</li>
</ul>
</main>
</body>
</html>
@@ -0,0 +1,74 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Settings list</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Product</div>
<div class="subtitle">What people see at checkout</div>
</div>
<div class="settings-list">
<div class="settings-row">
<div class="label">Name<span class="tag">Required</span><span class="subtitle">Shown on receipts</span></div>
<div class="field-slot"><label class="field"><input value="Pro plan" aria-label="Name"></label></div>
</div>
<div class="settings-row">
<div class="label">Billing<span class="subtitle">How often it charges</span></div>
<div class="field-slot">
<div class="select ghost" role="button">
<span>Monthly</span>
<svg class="icon chevron" viewBox="0 0 24 24"><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 link</div>
<div class="field-slot">
<button class="btn ghost"><svg class="icon" viewBox="0 0 24 24"><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>Copy link</button>
</div>
</div>
<div class="settings-row">
<div class="label">Price<span class="tag">Required</span></div>
<div class="field-slot"><label class="field error"><input value="0.00" aria-label="Price"><span class="unit">GBP</span></label></div>
<div class="error">A paid product needs a price above zero</div>
<div class="description">Buyers see this converted to their own currency at checkout.</div>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Forms on the page ground: products, coupons, clients.</li>
<li>Selects, buttons and icon buttons are ghost. Text fields are outline, read-only ones too.</li>
<li>"Required" goes in the row's action slot, beside the label.</li>
<li>Why a value was refused goes in the row's error slot, under the row.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Use secondary fields here. With no card behind them, filled controls float.</li>
<li>Stretch a select or a button to a column. They keep their own width - only a text field fills its slot.</li>
<li>Write a sentence in the subtitle. That goes in the description.</li>
</ul>
</main>
</body>
</html>
+98
View File
@@ -0,0 +1,98 @@
<!-- @dsCard group="Components" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Text fields</title>
<link rel="stylesheet" href="../components.css">
<style>
.ds { padding: 0 var(--container-padding) var(--container-padding); }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap-lg) var(--gap-xl); padding: 0 var(--gap-xs); }
.ds-cell { display: flex; flex-direction: column; gap: var(--gap-xs); min-width: 0; }
.ds-rules { margin: 0; padding: 0 var(--gap-xs) 0 var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-xxs); }
</style>
</head>
<body>
<main class="ds">
<div class="sub-heading">
<div class="title">Text fields</div>
<div class="subtitle">Outline for free text, secondary for values.</div>
</div>
<div class="ds-grid">
<div class="ds-cell">
<div class="caption">Outline - the default</div>
<label class="field stretch"><input placeholder="Product name" aria-label="Product name"></label>
</div>
<div class="ds-cell">
<div class="caption">Outline, filled in</div>
<label class="field stretch"><input value="Pro plan" aria-label="Product name"></label>
</div>
<div class="ds-cell">
<div class="caption">With a leading icon</div>
<label class="field stretch">
<svg class="icon" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
<input placeholder="Search" aria-label="Search">
</label>
</div>
<div class="ds-cell">
<div class="caption">Secondary</div>
<label class="field secondary stretch"><input value="1280" aria-label="Width"></label>
</div>
<div class="ds-cell">
<div class="caption">Read only</div>
<label class="field secondary readonly stretch"><input value="prod_8f2k1c" readonly disabled aria-label="Product ID"></label>
</div>
<div class="ds-cell">
<div class="caption">Numeric, centred, with a unit</div>
<label class="field secondary center stretch">
<input value="24" aria-label="Stroke width">
<span class="unit">px</span>
</label>
</div>
<div class="ds-cell">
<div class="caption">Error - the outline reddens, never the fill</div>
<label class="field error stretch"><input value="pro plan!" aria-label="Slug"></label>
</div>
<div class="ds-cell">
<div class="caption">Outline, hovered</div>
<label class="field stretch is-hovered"><input value="Pro plan" aria-label="Product name"></label>
</div>
<div class="ds-cell">
<div class="caption">Read only, monospace - ids and skus</div>
<label class="field secondary readonly mono stretch"><input value="usr_8f2c41d9a7" readonly disabled aria-label="User ID"></label>
</div>
</div>
<div class="sub-heading">
<div class="title">Use it for</div>
</div>
<ul class="ds-rules">
<li>Outline for free text: names, search, a composer, a form.</li>
<li>Secondary for numeric property values and read-only values.</li>
<li>Inside a property row the field is forced to secondary. You dont pick.</li>
<li>A read-only value is a read-only, disabled field. Never bare text.</li>
</ul>
<div class="sub-heading">
<div class="title">Dont</div>
</div>
<ul class="ds-rules">
<li>Hand-roll a red line under a field. Use the row's error slot.</li>
<li>Put a secondary field in a settings list, or an outline one in a properties section.</li>
<li>Colour or size the icon. The field does it.</li>
</ul>
</main>
</body>
</html>
+140
View File
@@ -0,0 +1,140 @@
<!-- @dsCard group="Foundations" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Colour</title>
<link rel="stylesheet" href="../components.css">
<style>
body { padding: var(--container-padding); }
.group { margin-top: var(--gap-lg); }
.group > .caption { margin-bottom: var(--gap-sm); padding: 0 var(--gap-xxs); }
.swatches { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-md); }
.sw { display: flex; gap: var(--gap-sm); align-items: flex-start; min-width: 0; }
.sw .chip { flex: none; width: 32px; height: 32px; border-radius: var(--radius-sm); border: var(--control-border-width) solid var(--divider); }
.sw .meta { min-width: 0; display: flex; flex-direction: column; gap: var(--gap-xxs); }
.sw .for { font-size: var(--text-xxs); }
.schemes { margin-top: var(--gap-xl); display: flex; flex-direction: column; gap: var(--gap-sm); }
.strip { display: flex; align-items: center; gap: var(--gap-md); padding: var(--gap-sm) var(--gap-md); border-radius: var(--radius-md); border: var(--control-border-width) solid var(--panel-border); }
.strip .name { width: calc(var(--control-height) * 1.6); flex: none; }
.strip .chips { display: flex; flex-wrap: wrap; gap: var(--gap-sm); }
.strip .set { display: flex; gap: var(--gap-xxs); }
.strip .mini { width: 12px; height: 12px; border-radius: var(--radius-xs); border: var(--control-border-width) solid var(--divider); }
</style>
</head>
<body>
<div class="page-heading">
<div class="title">Colour</div>
<div class="subtitle">37 slots per scheme. Carbon is the default. Pick the slot by what the thing is.</div>
</div>
<div class="group">
<div class="caption">Ground & surfaces</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--background)"></div><div class="meta"><span class="mono">--background</span><span class="mono muted">#0d0d0d</span><span class="for muted">Page ground, and a panel's fill</span></div></div>
<div class="sw"><div class="chip" style="background: var(--chrome)"></div><div class="meta"><span class="mono">--chrome</span><span class="mono muted">#010101</span><span class="for muted">Header and footer bars, the gutter between panels</span></div></div>
<div class="sw"><div class="chip" style="background: var(--surface-sunken)"></div><div class="meta"><span class="mono">--surface-sunken</span><span class="mono muted">#0a0a0a</span><span class="for muted">One step below the ground: rails, panel bodies</span></div></div>
<div class="sw"><div class="chip" style="background: var(--card)"></div><div class="meta"><span class="mono">--card</span><span class="mono muted">#1b1b1b</span><span class="for muted">Raised surface: cards, properties sections, dialogs</span></div></div>
<div class="sw"><div class="chip" style="background: var(--muted)"></div><div class="meta"><span class="mono">--muted</span><span class="mono muted">#0f0f0f</span><span class="for muted">De-emphasised fill</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Text</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--foreground)"></div><div class="meta"><span class="mono">--foreground</span><span class="mono muted">#f2f2f2</span><span class="for muted">Default text</span></div></div>
<div class="sw"><div class="chip" style="background: var(--muted-foreground)"></div><div class="meta"><span class="mono">--muted-foreground</span><span class="mono muted">#8f8f8f</span><span class="for muted">Secondary text, hints, units</span></div></div>
<div class="sw"><div class="chip" style="background: var(--row-text)"></div><div class="meta"><span class="mono">--row-text</span><span class="mono muted">#c2c2c2</span><span class="for muted">A row's resting label</span></div></div>
<div class="sw"><div class="chip" style="background: var(--row-hovered)"></div><div class="meta"><span class="mono">--row-hovered</span><span class="mono muted">#222222</span><span class="for muted">A hovered row in lists and menus</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Controls</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--control-fill)"></div><div class="meta"><span class="mono">--control-fill</span><span class="mono muted">#080808</span><span class="for muted">Shared fill of every control</span></div></div>
<div class="sw"><div class="chip" style="background: var(--control-fill-hovered)"></div><div class="meta"><span class="mono">--control-fill-hovered</span><span class="mono muted">#070707</span><span class="for muted">Control fill, hovered</span></div></div>
<div class="sw"><div class="chip" style="background: var(--control-fill-focused)"></div><div class="meta"><span class="mono">--control-fill-focused</span><span class="mono muted">#000000</span><span class="for muted">Control fill, focused</span></div></div>
<div class="sw"><div class="chip" style="background: var(--control-border)"></div><div class="meta"><span class="mono">--control-border</span><span class="mono muted">#2a2a2a</span><span class="for muted">Shared stroke of every control</span></div></div>
<div class="sw"><div class="chip" style="background: var(--secondary)"></div><div class="meta"><span class="mono">--secondary</span><span class="mono muted">#303030</span><span class="for muted">A neutral filled control</span></div></div>
<div class="sw"><div class="chip" style="background: var(--secondary-hovered)"></div><div class="meta"><span class="mono">--secondary-hovered</span><span class="mono muted">#424242</span><span class="for muted">Secondary, hovered</span></div></div>
<div class="sw"><div class="chip" style="background: var(--secondary-foreground)"></div><div class="meta"><span class="mono">--secondary-foreground</span><span class="mono muted">#f2f2f2</span><span class="for muted">Text on secondary</span></div></div>
<div class="sw"><div class="chip" style="background: var(--switch-track-inactive)"></div><div class="meta"><span class="mono">--switch-track-inactive</span><span class="mono muted">#0f0f0f</span><span class="for muted">A switch while off</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Floating</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--popover)"></div><div class="meta"><span class="mono">--popover</span><span class="mono muted">#000000</span><span class="for muted">Menus, select popups, anything floating</span></div></div>
<div class="sw"><div class="chip" style="background: var(--popover-border)"></div><div class="meta"><span class="mono">--popover-border</span><span class="mono muted">#1c1c1c</span><span class="for muted">A popover's edge</span></div></div>
<div class="sw"><div class="chip" style="background: var(--popover-item-hovered)"></div><div class="meta"><span class="mono">--popover-item-hovered</span><span class="mono muted">#1f1f1f</span><span class="for muted">Hover inside select and date popups</span></div></div>
<div class="sw"><div class="chip" style="background: var(--tooltip-background)"></div><div class="meta"><span class="mono">--tooltip-background</span><span class="mono muted">#080808</span><span class="for muted">Tooltips, above everything</span></div></div>
<div class="sw"><div class="chip" style="background: var(--tooltip-border)"></div><div class="meta"><span class="mono">--tooltip-border</span><span class="mono muted">#1f1f1f</span><span class="for muted">A tooltip's edge</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Accent & state</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--primary)"></div><div class="meta"><span class="mono">--primary</span><span class="mono muted">#e8e8e8</span><span class="for muted">The accent. The one thing that's on or chosen</span></div></div>
<div class="sw"><div class="chip" style="background: var(--primary-hovered)"></div><div class="meta"><span class="mono">--primary-hovered</span><span class="mono muted">#fefefe</span><span class="for muted">Primary, hovered</span></div></div>
<div class="sw"><div class="chip" style="background: var(--primary-foreground)"></div><div class="meta"><span class="mono">--primary-foreground</span><span class="mono muted">#000000</span><span class="for muted">Text on primary</span></div></div>
<div class="sw"><div class="chip" style="background: var(--destructive)"></div><div class="meta"><span class="mono">--destructive</span><span class="mono muted">#c43333</span><span class="for muted">Delete, revoke, discard</span></div></div>
<div class="sw"><div class="chip" style="background: var(--ring)"></div><div class="meta"><span class="mono">--ring</span><span class="mono muted">#ffffff</span><span class="for muted">Keyboard focus</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Lines & panels</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--border)"></div><div class="meta"><span class="mono">--border</span><span class="mono muted">#010101</span><span class="for muted">General outline</span></div></div>
<div class="sw"><div class="chip" style="background: var(--divider)"></div><div class="meta"><span class="mono">--divider</span><span class="mono muted">#202020</span><span class="for muted">A seam between things</span></div></div>
<div class="sw"><div class="chip" style="background: var(--panel-border)"></div><div class="meta"><span class="mono">--panel-border</span><span class="mono muted">#222222</span><span class="for muted">A panel's edge, resting</span></div></div>
<div class="sw"><div class="chip" style="background: var(--panel-border-highlighted)"></div><div class="meta"><span class="mono">--panel-border-highlighted</span><span class="mono muted">#3a3a3a</span><span class="for muted">The lit panel's edge</span></div></div>
<div class="sw"><div class="chip" style="background: var(--properties-section-border)"></div><div class="meta"><span class="mono">--properties-section-border</span><span class="mono muted">#272727</span><span class="for muted">A properties section's edge</span></div></div>
</div>
</div>
<div class="group">
<div class="caption">Charts</div>
<div class="swatches">
<div class="sw"><div class="chip" style="background: var(--chart-1)"></div><div class="meta"><span class="mono">--chart-1</span><span class="mono muted">#f45b69</span><span class="for muted">Data visualisation</span></div></div>
<div class="sw"><div class="chip" style="background: var(--chart-2)"></div><div class="meta"><span class="mono">--chart-2</span><span class="mono muted">#86d957</span><span class="for muted">Data visualisation</span></div></div>
<div class="sw"><div class="chip" style="background: var(--chart-3)"></div><div class="meta"><span class="mono">--chart-3</span><span class="mono muted">#62a7ff</span><span class="for muted">Data visualisation</span></div></div>
<div class="sw"><div class="chip" style="background: var(--chart-4)"></div><div class="meta"><span class="mono">--chart-4</span><span class="mono muted">#ffc857</span><span class="for muted">Data visualisation</span></div></div>
<div class="sw"><div class="chip" style="background: var(--chart-5)"></div><div class="meta"><span class="mono">--chart-5</span><span class="mono muted">#ff8c42</span><span class="for muted">Data visualisation</span></div></div>
</div>
</div>
<div class="schemes">
<div class="caption">Other schemes, same slots in the same order</div>
<div class="gu strip" data-scheme="light">
<span class="name medium">Light</span>
<div class="chips">
<div class="set"><div class="mini" style="background: var(--background)" title="--background"></div><div class="mini" style="background: var(--chrome)" title="--chrome"></div><div class="mini" style="background: var(--surface-sunken)" title="--surface-sunken"></div><div class="mini" style="background: var(--card)" title="--card"></div><div class="mini" style="background: var(--muted)" title="--muted"></div></div>
<div class="set"><div class="mini" style="background: var(--foreground)" title="--foreground"></div><div class="mini" style="background: var(--muted-foreground)" title="--muted-foreground"></div><div class="mini" style="background: var(--row-text)" title="--row-text"></div><div class="mini" style="background: var(--row-hovered)" title="--row-hovered"></div></div>
<div class="set"><div class="mini" style="background: var(--control-fill)" title="--control-fill"></div><div class="mini" style="background: var(--control-fill-hovered)" title="--control-fill-hovered"></div><div class="mini" style="background: var(--control-fill-focused)" title="--control-fill-focused"></div><div class="mini" style="background: var(--control-border)" title="--control-border"></div><div class="mini" style="background: var(--secondary)" title="--secondary"></div><div class="mini" style="background: var(--secondary-hovered)" title="--secondary-hovered"></div><div class="mini" style="background: var(--secondary-foreground)" title="--secondary-foreground"></div><div class="mini" style="background: var(--switch-track-inactive)" title="--switch-track-inactive"></div></div>
<div class="set"><div class="mini" style="background: var(--popover)" title="--popover"></div><div class="mini" style="background: var(--popover-border)" title="--popover-border"></div><div class="mini" style="background: var(--popover-item-hovered)" title="--popover-item-hovered"></div><div class="mini" style="background: var(--tooltip-background)" title="--tooltip-background"></div><div class="mini" style="background: var(--tooltip-border)" title="--tooltip-border"></div></div>
<div class="set"><div class="mini" style="background: var(--primary)" title="--primary"></div><div class="mini" style="background: var(--primary-hovered)" title="--primary-hovered"></div><div class="mini" style="background: var(--primary-foreground)" title="--primary-foreground"></div><div class="mini" style="background: var(--destructive)" title="--destructive"></div><div class="mini" style="background: var(--ring)" title="--ring"></div></div>
<div class="set"><div class="mini" style="background: var(--border)" title="--border"></div><div class="mini" style="background: var(--divider)" title="--divider"></div><div class="mini" style="background: var(--panel-border)" title="--panel-border"></div><div class="mini" style="background: var(--panel-border-highlighted)" title="--panel-border-highlighted"></div><div class="mini" style="background: var(--properties-section-border)" title="--properties-section-border"></div></div>
<div class="set"><div class="mini" style="background: var(--chart-1)" title="--chart-1"></div><div class="mini" style="background: var(--chart-2)" title="--chart-2"></div><div class="mini" style="background: var(--chart-3)" title="--chart-3"></div><div class="mini" style="background: var(--chart-4)" title="--chart-4"></div><div class="mini" style="background: var(--chart-5)" title="--chart-5"></div></div>
</div>
</div>
<div class="gu strip" data-scheme="dark">
<span class="name medium">Dark</span>
<div class="chips">
<div class="set"><div class="mini" style="background: var(--background)" title="--background"></div><div class="mini" style="background: var(--chrome)" title="--chrome"></div><div class="mini" style="background: var(--surface-sunken)" title="--surface-sunken"></div><div class="mini" style="background: var(--card)" title="--card"></div><div class="mini" style="background: var(--muted)" title="--muted"></div></div>
<div class="set"><div class="mini" style="background: var(--foreground)" title="--foreground"></div><div class="mini" style="background: var(--muted-foreground)" title="--muted-foreground"></div><div class="mini" style="background: var(--row-text)" title="--row-text"></div><div class="mini" style="background: var(--row-hovered)" title="--row-hovered"></div></div>
<div class="set"><div class="mini" style="background: var(--control-fill)" title="--control-fill"></div><div class="mini" style="background: var(--control-fill-hovered)" title="--control-fill-hovered"></div><div class="mini" style="background: var(--control-fill-focused)" title="--control-fill-focused"></div><div class="mini" style="background: var(--control-border)" title="--control-border"></div><div class="mini" style="background: var(--secondary)" title="--secondary"></div><div class="mini" style="background: var(--secondary-hovered)" title="--secondary-hovered"></div><div class="mini" style="background: var(--secondary-foreground)" title="--secondary-foreground"></div><div class="mini" style="background: var(--switch-track-inactive)" title="--switch-track-inactive"></div></div>
<div class="set"><div class="mini" style="background: var(--popover)" title="--popover"></div><div class="mini" style="background: var(--popover-border)" title="--popover-border"></div><div class="mini" style="background: var(--popover-item-hovered)" title="--popover-item-hovered"></div><div class="mini" style="background: var(--tooltip-background)" title="--tooltip-background"></div><div class="mini" style="background: var(--tooltip-border)" title="--tooltip-border"></div></div>
<div class="set"><div class="mini" style="background: var(--primary)" title="--primary"></div><div class="mini" style="background: var(--primary-hovered)" title="--primary-hovered"></div><div class="mini" style="background: var(--primary-foreground)" title="--primary-foreground"></div><div class="mini" style="background: var(--destructive)" title="--destructive"></div><div class="mini" style="background: var(--ring)" title="--ring"></div></div>
<div class="set"><div class="mini" style="background: var(--border)" title="--border"></div><div class="mini" style="background: var(--divider)" title="--divider"></div><div class="mini" style="background: var(--panel-border)" title="--panel-border"></div><div class="mini" style="background: var(--panel-border-highlighted)" title="--panel-border-highlighted"></div><div class="mini" style="background: var(--properties-section-border)" title="--properties-section-border"></div></div>
<div class="set"><div class="mini" style="background: var(--chart-1)" title="--chart-1"></div><div class="mini" style="background: var(--chart-2)" title="--chart-2"></div><div class="mini" style="background: var(--chart-3)" title="--chart-3"></div><div class="mini" style="background: var(--chart-4)" title="--chart-4"></div><div class="mini" style="background: var(--chart-5)" title="--chart-5"></div></div>
</div>
</div>
</div>
</body>
</html>
+89
View File
@@ -0,0 +1,89 @@
<!-- @dsCard group="Foundations" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Density</title>
<link rel="stylesheet" href="../components.css">
<style>
body { padding: var(--container-padding); }
.block { margin-top: var(--gap-xl); }
.block > .caption { margin-bottom: var(--gap-sm); padding: 0 var(--gap-xxs); }
.tiers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap-md); align-items: start; }
.tier { border: var(--control-border-width) solid var(--panel-border); border-radius: var(--radius-lg); padding: var(--gap-md) 0 0; min-width: 0; display: flex; flex-direction: column; gap: var(--gap-sm); }
.tier > .caption, .tier > .ctl { padding: 0 var(--gap-md); }
.tier > .ctl { display: flex; }
.tier > .ctl > .field, .tier > .ctl > .select { flex: 1; }
.tier .properties-section { margin-top: var(--gap-xxs); }
.rows { display: flex; flex-direction: column; }
.rows .r { display: grid; grid-template-columns: 1.1fr 0.9fr 0.9fr 0.9fr; gap: var(--gap-md); align-items: baseline; padding: var(--gap-sm) var(--gap-xs); }
.rows .r + .r { border-top: var(--control-border-width) solid var(--divider); }
</style>
</head>
<body>
<div class="page-heading">
<div class="title">Density</div>
<div class="subtitle">Three tiers. Pick one for the app, don't tune it.</div>
</div>
<div class="block">
<div class="caption">Same controls, three tiers</div>
<div class="tiers">
<div class="gu tier" data-density="compact">
<div class="caption">Compact</div>
<div class="ctl"><button class="btn primary">Save</button></div>
<div class="ctl"><label class="field"><input placeholder="Name"></label></div>
<div class="ctl"><div class="select"><span>Monthly</span><svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
<div class="properties-section">
<div class="rows">
<div class="property-row"><span class="label">Width</span><div class="value"><label class="field secondary center"><input value="120"><span class="unit">px</span></label></div></div>
</div>
</div>
</div>
<div class="gu tier" data-density="normal">
<div class="caption">Normal</div>
<div class="ctl"><button class="btn primary">Save</button></div>
<div class="ctl"><label class="field"><input placeholder="Name"></label></div>
<div class="ctl"><div class="select"><span>Monthly</span><svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
<div class="properties-section">
<div class="rows">
<div class="property-row"><span class="label">Width</span><div class="value"><label class="field secondary center"><input value="120"><span class="unit">px</span></label></div></div>
</div>
</div>
</div>
<div class="gu tier" data-density="product">
<div class="caption">Product</div>
<div class="ctl"><button class="btn primary">Save</button></div>
<div class="ctl"><label class="field"><input placeholder="Name"></label></div>
<div class="ctl"><div class="select"><span>Monthly</span><svg class="icon chevron" viewBox="0 0 24 24"><path d="m7 15 5 5 5-5" /><path d="m7 9 5-5 5 5" /></svg></div></div>
<div class="properties-section">
<div class="rows">
<div class="property-row"><span class="label">Width</span><div class="value"><label class="field secondary center"><input value="120"><span class="unit">px</span></label></div></div>
</div>
</div>
</div>
</div>
</div>
<div class="block">
<div class="caption">The numbers</div>
<div class="rows">
<div class="r"><span></span><span class="medium">Compact</span><span class="medium">Normal</span><span class="medium">Product</span></div>
<div class="r"><span class="mono muted">font-size</span><span class="mono">10</span><span class="mono">10</span><span class="mono">12</span></div>
<div class="r"><span class="mono muted">control-height</span><span class="mono">23</span><span class="mono">27</span><span class="mono">33</span></div>
<div class="r"><span class="mono muted">chrome-bar-height</span><span class="mono">31</span><span class="mono">37</span><span class="mono">43</span></div>
<div class="r"><span class="muted">For</span><span>Dense desktop tools</span><span>The same, roomier</span><span>Web and product apps</span></div>
<div class="r"><span class="muted">Used by</span><span>Arcs &amp; Angles</span><span>A&amp;A "Comfortable"</span><span>The hub</span></div>
</div>
</div>
</body>
</html>
+77
View File
@@ -0,0 +1,77 @@
<!-- @dsCard group="Foundations" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Shape</title>
<link rel="stylesheet" href="../components.css">
<style>
body { padding: var(--container-padding); }
.block { margin-top: var(--gap-xl); }
.block > .caption { margin-bottom: var(--gap-sm); padding: 0 var(--gap-xxs); }
.radii { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--gap-md); }
.radius { display: flex; flex-direction: column; gap: var(--gap-xs); }
.radius .box { height: calc(var(--control-height) * 1.6); background: var(--card); border: var(--control-border-width) solid var(--control-border); }
.facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-md); }
.facts .card { display: flex; flex-direction: column; gap: var(--gap-xxs); padding: var(--gap-md) var(--gap-lg); }
.demo { border-radius: var(--radius-lg); overflow: hidden; border: var(--control-border-width) solid var(--panel-border); }
.demo .shell { height: calc(var(--control-height) * 5); }
.demo .rail { width: 30%; }
.demo .main { flex: 1; }
.demo .panel { display: flex; flex-direction: column; }
.demo .panel .body { flex: 1; padding: 0 var(--gap-md) var(--gap-md); }
.demo .foot { padding: var(--gap-sm) var(--gap-lg); }
</style>
</head>
<body>
<div class="page-heading">
<div class="title">Shape</div>
<div class="subtitle">Small radii, flat surfaces. Borders and surface steps do the separating.</div>
</div>
<div class="block">
<div class="caption">Radii</div>
<div class="radii">
<div class="radius"><div class="box" style="border-radius: var(--radius-xs)"></div><span class="mono">radius-xs</span><span class="mono muted">2</span></div>
<div class="radius"><div class="box" style="border-radius: var(--radius-sm)"></div><span class="mono">radius-sm</span><span class="mono muted">4</span></div>
<div class="radius"><div class="box" style="border-radius: var(--radius-md)"></div><span class="mono">radius-md</span><span class="mono muted">6 · controls</span></div>
<div class="radius"><div class="box" style="border-radius: var(--radius-lg)"></div><span class="mono">radius-lg</span><span class="mono muted">8</span></div>
<div class="radius"><div class="box" style="border-radius: var(--radius-xl)"></div><span class="mono">radius-xl</span><span class="mono muted">10 · cards</span></div>
<div class="radius"><div class="box" style="border-radius: var(--radius-xxl)"></div><span class="mono">radius-xxl</span><span class="mono muted">12 · dialogs</span></div>
</div>
</div>
<div class="block">
<div class="caption">Panels</div>
<div class="facts">
<div class="card"><span class="mono">panel-radius</span><span class="mono muted">10</span></div>
<div class="card"><span class="mono">panel-gap</span><span class="mono muted">5</span></div>
<div class="card"><span class="mono">panel-border-width</span><span class="mono muted">1.15</span></div>
</div>
</div>
<div class="block">
<div class="caption">Flat, no shadows</div>
<div class="demo">
<div class="chrome-bar"><span class="medium">Chrome</span></div>
<div class="shell">
<div class="panel sunken rail">
<div class="panel-header"><span class="title">Rail</span></div>
<div class="body muted">Sunken panel</div>
</div>
<div class="panel active main">
<div class="panel-header"><span class="title">Workspace</span></div>
<div class="body muted">The lit panel. Only one at a time.</div>
</div>
</div>
</div>
<div class="foot muted">Chrome ground, panels standing on it, panel-gap gutters between.</div>
</div>
</body>
</html>
+80
View File
@@ -0,0 +1,80 @@
<!-- @dsCard group="Foundations" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Spacing</title>
<link rel="stylesheet" href="../components.css">
<style>
body { padding: var(--container-padding); }
.block { margin-top: var(--gap-xl); }
.block > .caption { margin-bottom: var(--gap-sm); padding: 0 var(--gap-xxs); }
.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-md); }
.tier { border: var(--control-border-width) solid var(--panel-border); border-radius: var(--radius-lg); padding: var(--gap-md) var(--gap-lg); }
.tier > .caption { margin-bottom: var(--gap-sm); }
.bar-row { display: flex; align-items: center; gap: var(--gap-md); min-height: var(--line-box); padding: var(--gap-xxs) 0; }
.bar-row .tok { flex: 0 0 48%; white-space: nowrap; }
.bar-row .bar { height: var(--gap-sm); background: var(--primary); border-radius: var(--radius-xs); flex: none; }
.bar-row .px { font-size: var(--text-xxs); }
</style>
</head>
<body>
<div class="page-heading">
<div class="title">Spacing</div>
<div class="subtitle">Gaps come from the density tier. Never a literal.</div>
</div>
<div class="block">
<div class="caption">Gap ladder</div>
<div class="tiers">
<div class="gu tier" data-density="product">
<div class="caption">Product (and normal)</div>
<div class="bar-row"><span class="tok mono muted">gap-xxs</span><div class="bar" style="width: var(--gap-xxs)"></div><span class="px mono muted">3</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xs</span><div class="bar" style="width: var(--gap-xs)"></div><span class="px mono muted">5</span></div>
<div class="bar-row"><span class="tok mono muted">gap-sm</span><div class="bar" style="width: var(--gap-sm)"></div><span class="px mono muted">8</span></div>
<div class="bar-row"><span class="tok mono muted">gap-md</span><div class="bar" style="width: var(--gap-md)"></div><span class="px mono muted">10</span></div>
<div class="bar-row"><span class="tok mono muted">gap-lg</span><div class="bar" style="width: var(--gap-lg)"></div><span class="px mono muted">15</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xl</span><div class="bar" style="width: var(--gap-xl)"></div><span class="px mono muted">20</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xxl</span><div class="bar" style="width: var(--gap-xxl)"></div><span class="px mono muted">30</span></div>
</div>
<div class="gu tier" data-density="compact">
<div class="caption">Compact</div>
<div class="bar-row"><span class="tok mono muted">gap-xxs</span><div class="bar" style="width: var(--gap-xxs)"></div><span class="px mono muted">2</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xs</span><div class="bar" style="width: var(--gap-xs)"></div><span class="px mono muted">4</span></div>
<div class="bar-row"><span class="tok mono muted">gap-sm</span><div class="bar" style="width: var(--gap-sm)"></div><span class="px mono muted">6</span></div>
<div class="bar-row"><span class="tok mono muted">gap-md</span><div class="bar" style="width: var(--gap-md)"></div><span class="px mono muted">8</span></div>
<div class="bar-row"><span class="tok mono muted">gap-lg</span><div class="bar" style="width: var(--gap-lg)"></div><span class="px mono muted">12</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xl</span><div class="bar" style="width: var(--gap-xl)"></div><span class="px mono muted">16</span></div>
<div class="bar-row"><span class="tok mono muted">gap-xxl</span><div class="bar" style="width: var(--gap-xxl)"></div><span class="px mono muted">24</span></div>
</div>
</div>
</div>
<div class="block">
<div class="caption">Heights and containers</div>
<div class="tiers">
<div class="gu tier" data-density="product">
<div class="caption">Product</div>
<div class="bar-row"><span class="tok mono muted">control-height</span><div class="bar" style="width: var(--control-height)"></div><span class="px mono muted">33</span></div>
<div class="bar-row"><span class="tok mono muted">property-row-height</span><div class="bar" style="width: var(--property-row-height)"></div><span class="px mono muted">36</span></div>
<div class="bar-row"><span class="tok mono muted">chrome-bar-height</span><div class="bar" style="width: var(--chrome-bar-height)"></div><span class="px mono muted">43</span></div>
<div class="bar-row"><span class="tok mono muted">container-gap</span><div class="bar" style="width: var(--container-gap)"></div><span class="px mono muted">10</span></div>
<div class="bar-row"><span class="tok mono muted">container-padding</span><div class="bar" style="width: var(--container-padding)"></div><span class="px mono muted">20</span></div>
</div>
<div class="gu tier" data-density="compact">
<div class="caption">Compact</div>
<div class="bar-row"><span class="tok mono muted">control-height</span><div class="bar" style="width: var(--control-height)"></div><span class="px mono muted">23</span></div>
<div class="bar-row"><span class="tok mono muted">property-row-height</span><div class="bar" style="width: var(--property-row-height)"></div><span class="px mono muted">28</span></div>
<div class="bar-row"><span class="tok mono muted">chrome-bar-height</span><div class="bar" style="width: var(--chrome-bar-height)"></div><span class="px mono muted">31</span></div>
<div class="bar-row"><span class="tok mono muted">container-gap</span><div class="bar" style="width: var(--container-gap)"></div><span class="px mono muted">8</span></div>
<div class="bar-row"><span class="tok mono muted">container-padding</span><div class="bar" style="width: var(--container-padding)"></div><span class="px mono muted">16</span></div>
</div>
</div>
</div>
</body>
</html>
+99
View File
@@ -0,0 +1,99 @@
<!-- @dsCard group="Foundations" -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Type</title>
<link rel="stylesheet" href="../components.css">
<style>
body { padding: var(--container-padding); }
.block { margin-top: var(--gap-xl); }
.block > .caption { margin-bottom: var(--gap-sm); padding: 0 var(--gap-xxs); }
.faces { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-md); }
.faces .card { display: flex; flex-direction: column; gap: var(--gap-xs); }
.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-md); }
.tier { border: var(--control-border-width) solid var(--panel-border); border-radius: var(--radius-lg); padding: var(--gap-md) var(--gap-lg); }
.tier > .caption { margin-bottom: var(--gap-sm); }
.step { display: flex; align-items: baseline; gap: var(--gap-md); padding: var(--gap-xs) 0; }
.step + .step { border-top: var(--control-border-width) solid var(--divider); }
.step .tok { flex: none; width: calc(var(--control-height) * 3.6); white-space: nowrap; font-size: var(--font-size); }
.weights { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-md); }
.weights .card { display: flex; flex-direction: column; gap: var(--gap-xs); }
.rows { display: flex; flex-direction: column; }
.rows .r { display: grid; grid-template-columns: 1fr 1.3fr 1fr; gap: var(--gap-md); align-items: baseline; padding: var(--gap-sm) var(--gap-xs); }
.rows .r + .r { border-top: var(--control-border-width) solid var(--divider); }
</style>
</head>
<body>
<div class="page-heading">
<div class="title">Type</div>
<div class="subtitle">Geist for everything, Geist Mono for numbers and readouts.</div>
</div>
<div class="block">
<div class="caption">Families</div>
<div class="faces">
<div class="card">
<span class="text-lg">Geist</span>
<span class="muted">--font-sans. Body, controls, headings.</span>
</div>
<div class="card">
<span class="text-lg mono">Geist Mono 0123</span>
<span class="muted">--font-mono. Readouts, ids, stats.</span>
</div>
</div>
</div>
<div class="block">
<div class="caption">Size ladder</div>
<div class="tiers">
<div class="gu tier" data-density="product">
<div class="caption">Product</div>
<div class="step"><span class="tok mono muted">text-lg 22</span><span class="text-lg">Page title</span></div>
<div class="step"><span class="tok mono muted">text-sm 17</span><span class="text-sm">Section heading</span></div>
<div class="step"><span class="tok mono muted">text-xs 13</span><span class="text-xs">List text</span></div>
<div class="step"><span class="tok mono muted">font-size 12</span><span>Body and controls</span></div>
<div class="step"><span class="tok mono muted">text-xxs 11</span><span class="text-xxs">Captions</span></div>
</div>
<div class="gu tier" data-density="compact">
<div class="caption">Compact</div>
<div class="step"><span class="tok mono muted">text-lg 18</span><span class="text-lg">Page title</span></div>
<div class="step"><span class="tok mono muted">text-sm 14</span><span class="text-sm">Section heading</span></div>
<div class="step"><span class="tok mono muted">text-xs 11</span><span class="text-xs">List text</span></div>
<div class="step"><span class="tok mono muted">font-size 10</span><span>Body and controls</span></div>
<div class="step"><span class="tok mono muted">text-xxs 9</span><span class="text-xxs">Captions</span></div>
</div>
</div>
</div>
<div class="block">
<div class="caption">Weights</div>
<div class="weights">
<div class="card"><span class="text-sm">Aa</span><span class="muted">Body 300</span></div>
<div class="card"><span class="text-sm" style="font-weight: var(--weight-control)">Aa</span><span class="muted">Control 400</span></div>
<div class="card"><span class="text-sm medium">Aa</span><span class="muted">Medium 500</span></div>
<div class="card"><span class="text-sm semibold">Aa</span><span class="muted">Semibold 600</span></div>
</div>
</div>
<div class="block">
<div class="caption">Job to style</div>
<div class="rows">
<div class="r"><span class="muted">Page title</span><span class="text-lg">Products</span><span class="mono muted">text-lg</span></div>
<div class="r"><span class="muted">Subtitle</span><span class="muted">What this page is for</span><span class="mono muted">muted</span></div>
<div class="r"><span class="muted">Section heading</span><span class="text-sm semibold">Billing</span><span class="mono muted">text-sm, 600</span></div>
<div class="r"><span class="muted">Nav group caption</span><span class="caption">Account</span><span class="mono muted">text-xxs, 600, muted</span></div>
<div class="r"><span class="muted">Hint, unit, empty line</span><span class="muted">Nothing here yet</span><span class="mono muted">muted</span></div>
<div class="r"><span class="muted">Mono readout</span><span class="mono muted">60 fps · 12.4 ms</span><span class="mono muted">mono, muted</span></div>
</div>
</div>
</body>
</html>
+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>
+259
View File
@@ -0,0 +1,259 @@
/* garage_ui design tokens. Generated by garage_ui/tool/export_design_tokens_test.dart
- dont edit by hand, rerun the export.
Scheme: data-scheme="carbon" (default) | "dark" | "light"
Density: data-density="product" (default) | "normal" | "compact" */
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap");
:root {
--font-sans: "Geist", system-ui, sans-serif;
--font-mono: "Geist Mono", ui-monospace, monospace;
--weight-body: 300;
--weight-control: 400;
--weight-medium: 500;
--weight-semibold: 600;
--radius-xs: 2px;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 10px;
--radius-xxl: 12px;
--panel-radius: 10px;
--panel-gap: 5px;
--panel-border-width: 1.15px;
--property-split: 0.4;
--property-label-gap: 10px;
--dialog-max-width: 350px;
}
:root, [data-scheme="carbon"] {
--background: #0d0d0d;
--foreground: #f2f2f2;
--chrome: #010101;
--surface-sunken: #0a0a0a;
--card: #1b1b1b;
--muted: #0f0f0f;
--muted-foreground: #8f8f8f;
--row-text: #c2c2c2;
--row-hovered: #222222;
--popover: #000000;
--popover-border: #1c1c1c;
--popover-item-hovered: #1f1f1f;
--tooltip-background: #080808;
--tooltip-border: #1f1f1f;
--primary: #e8e8e8;
--primary-hovered: #fefefe;
--primary-foreground: #000000;
--secondary: #303030;
--secondary-hovered: #424242;
--secondary-foreground: #f2f2f2;
--destructive: #c43333;
--control-fill: #080808;
--control-fill-hovered: #070707;
--control-fill-focused: #000000;
--control-border: #2a2a2a;
--switch-track-inactive: #0f0f0f;
--border: #010101;
--divider: #202020;
--panel-border: #222222;
--panel-border-highlighted: #3a3a3a;
--properties-section-border: #272727;
--ring: #ffffff;
--chart-1: #f45b69;
--chart-2: #86d957;
--chart-3: #62a7ff;
--chart-4: #ffc857;
--chart-5: #ff8c42;
}
[data-scheme="dark"] {
--background: #303030;
--foreground: #e6e6e6;
--chrome: #171717;
--surface-sunken: #272727;
--card: #3d3d3d;
--muted: #1d1d1d;
--muted-foreground: #999999;
--row-text: #c1c1c1;
--row-hovered: #474747;
--popover: #191919;
--popover-border: #2d2d2d;
--popover-item-hovered: #424242;
--tooltip-background: #1d1d1d;
--tooltip-border: #303030;
--primary: #4772b3;
--primary-hovered: #6b8fc4;
--primary-foreground: #ffffff;
--secondary: #565656;
--secondary-hovered: #676767;
--secondary-foreground: #e6e6e6;
--destructive: #a9575f;
--control-fill: #1d1d1d;
--control-fill-hovered: #222222;
--control-fill-focused: #1a1a1a;
--control-border: #353535;
--switch-track-inactive: #1d1d1d;
--border: #171717;
--divider: #454545;
--panel-border: #464646;
--panel-border-highlighted: #606060;
--properties-section-border: #4c4c4c;
--ring: #6592d7;
--chart-1: #ff3352;
--chart-2: #8bdc00;
--chart-3: #2890ff;
--chart-4: #edba18;
--chart-5: #ed5700;
}
[data-scheme="light"] {
--background: #f2f2f4;
--foreground: #1a1a1c;
--chrome: #cdcdd5;
--surface-sunken: #e7e7ea;
--card: #fefeff;
--muted: #d7d7de;
--muted-foreground: #65656d;
--row-text: #3b3b40;
--row-hovered: #fefeff;
--popover: #d1d1d8;
--popover-border: #ededf0;
--popover-item-hovered: #fefeff;
--tooltip-background: #d7d7de;
--tooltip-border: #f2f2f4;
--primary: #3a63a1;
--primary-hovered: #537fc1;
--primary-foreground: #ffffff;
--secondary: #fefeff;
--secondary-hovered: #fefeff;
--secondary-foreground: #1a1a1c;
--destructive: #b3474f;
--control-fill: #d7d7de;
--control-fill-hovered: #e0e0e4;
--control-fill-focused: #d3d3d9;
--control-border: #f9f9fa;
--switch-track-inactive: #d7d7de;
--border: #cdcdd5;
--divider: #d8d8de;
--panel-border: #d7d7dd;
--panel-border-highlighted: #fefeff;
--properties-section-border: #eaeaff;
--ring: #4880d5;
--chart-1: #ff3352;
--chart-2: #8bdc00;
--chart-3: #2890ff;
--chart-4: #edba18;
--chart-5: #ed5700;
}
:root, [data-density="product"] {
--font-size: 12px;
--line-height: 1.25;
--line-box: 15px;
--control-height: 33px;
--control-padding-x: 10px;
--control-padding-y: 9px;
--control-gap: 5px;
--control-border-width: 1px;
--menu-row-height: 33px;
--popup-row-height: 33px;
--explorer-row-height: 29px;
--chrome-bar-height: 43px;
--property-row-height: 36px;
--label-column-height: 31px;
--properties-section-gap: 15px;
--explorer-row-base-padding: 8px;
--explorer-row-end-padding: 12px;
--list-row-indent: 10px;
--container-gap: 10px;
--container-padding: 20px;
--gap-xxs: 3px;
--gap-xs: 5px;
--gap-sm: 8px;
--gap-md: 10px;
--gap-lg: 15px;
--gap-xl: 20px;
--gap-xxl: 30px;
--text-xxs: 11px;
--text-xs: 13px;
--text-sm: 17px;
--text-lg: 22px;
--icon-sm: 12px;
--icon-md: 20px;
--icon-lg: 24px;
}
[data-density="compact"] {
--font-size: 10px;
--line-height: 1.1;
--line-box: 11px;
--control-height: 23px;
--control-padding-x: 10px;
--control-padding-y: 6px;
--control-gap: 4px;
--control-border-width: 1px;
--menu-row-height: 20px;
--popup-row-height: 20px;
--explorer-row-height: 22px;
--chrome-bar-height: 31px;
--property-row-height: 28px;
--label-column-height: 26px;
--properties-section-gap: 6px;
--explorer-row-base-padding: 4px;
--explorer-row-end-padding: 10px;
--list-row-indent: 6px;
--container-gap: 8px;
--container-padding: 16px;
--gap-xxs: 2px;
--gap-xs: 4px;
--gap-sm: 6px;
--gap-md: 8px;
--gap-lg: 12px;
--gap-xl: 16px;
--gap-xxl: 24px;
--text-xxs: 9px;
--text-xs: 11px;
--text-sm: 14px;
--text-lg: 18px;
--icon-sm: 10px;
--icon-md: 20px;
--icon-lg: 24px;
}
[data-density="normal"] {
--font-size: 10px;
--line-height: 1.1;
--line-box: 11px;
--control-height: 27px;
--control-padding-x: 12px;
--control-padding-y: 8px;
--control-gap: 5px;
--control-border-width: 1px;
--menu-row-height: 26px;
--popup-row-height: 26px;
--explorer-row-height: 30px;
--chrome-bar-height: 37px;
--property-row-height: 30px;
--label-column-height: 26px;
--properties-section-gap: 15px;
--explorer-row-base-padding: 8px;
--explorer-row-end-padding: 14px;
--list-row-indent: 12px;
--container-gap: 10px;
--container-padding: 20px;
--gap-xxs: 3px;
--gap-xs: 5px;
--gap-sm: 8px;
--gap-md: 10px;
--gap-lg: 15px;
--gap-xl: 20px;
--gap-xxl: 30px;
--text-xxs: 9px;
--text-xs: 11px;
--text-sm: 14px;
--text-lg: 18px;
--icon-sm: 10px;
--icon-md: 20px;
--icon-lg: 24px;
}
+256
View File
@@ -0,0 +1,256 @@
{
"_generated": "by garage_ui/tool/export_design_tokens_test.dart - dont edit by hand",
"fonts": {
"sans": "Geist",
"mono": "Geist Mono"
},
"weights": {
"body": 300,
"control": 400,
"medium": 500,
"semibold": 600
},
"schemes": {
"carbon": {
"background": "#0d0d0d",
"foreground": "#f2f2f2",
"chrome": "#010101",
"surface-sunken": "#0a0a0a",
"card": "#1b1b1b",
"muted": "#0f0f0f",
"muted-foreground": "#8f8f8f",
"row-text": "#c2c2c2",
"row-hovered": "#222222",
"popover": "#000000",
"popover-border": "#1c1c1c",
"popover-item-hovered": "#1f1f1f",
"tooltip-background": "#080808",
"tooltip-border": "#1f1f1f",
"primary": "#e8e8e8",
"primary-hovered": "#fefefe",
"primary-foreground": "#000000",
"secondary": "#303030",
"secondary-hovered": "#424242",
"secondary-foreground": "#f2f2f2",
"destructive": "#c43333",
"control-fill": "#080808",
"control-fill-hovered": "#070707",
"control-fill-focused": "#000000",
"control-border": "#2a2a2a",
"switch-track-inactive": "#0f0f0f",
"border": "#010101",
"divider": "#202020",
"panel-border": "#222222",
"panel-border-highlighted": "#3a3a3a",
"properties-section-border": "#272727",
"ring": "#ffffff",
"chart-1": "#f45b69",
"chart-2": "#86d957",
"chart-3": "#62a7ff",
"chart-4": "#ffc857",
"chart-5": "#ff8c42"
},
"dark": {
"background": "#303030",
"foreground": "#e6e6e6",
"chrome": "#171717",
"surface-sunken": "#272727",
"card": "#3d3d3d",
"muted": "#1d1d1d",
"muted-foreground": "#999999",
"row-text": "#c1c1c1",
"row-hovered": "#474747",
"popover": "#191919",
"popover-border": "#2d2d2d",
"popover-item-hovered": "#424242",
"tooltip-background": "#1d1d1d",
"tooltip-border": "#303030",
"primary": "#4772b3",
"primary-hovered": "#6b8fc4",
"primary-foreground": "#ffffff",
"secondary": "#565656",
"secondary-hovered": "#676767",
"secondary-foreground": "#e6e6e6",
"destructive": "#a9575f",
"control-fill": "#1d1d1d",
"control-fill-hovered": "#222222",
"control-fill-focused": "#1a1a1a",
"control-border": "#353535",
"switch-track-inactive": "#1d1d1d",
"border": "#171717",
"divider": "#454545",
"panel-border": "#464646",
"panel-border-highlighted": "#606060",
"properties-section-border": "#4c4c4c",
"ring": "#6592d7",
"chart-1": "#ff3352",
"chart-2": "#8bdc00",
"chart-3": "#2890ff",
"chart-4": "#edba18",
"chart-5": "#ed5700"
},
"light": {
"background": "#f2f2f4",
"foreground": "#1a1a1c",
"chrome": "#cdcdd5",
"surface-sunken": "#e7e7ea",
"card": "#fefeff",
"muted": "#d7d7de",
"muted-foreground": "#65656d",
"row-text": "#3b3b40",
"row-hovered": "#fefeff",
"popover": "#d1d1d8",
"popover-border": "#ededf0",
"popover-item-hovered": "#fefeff",
"tooltip-background": "#d7d7de",
"tooltip-border": "#f2f2f4",
"primary": "#3a63a1",
"primary-hovered": "#537fc1",
"primary-foreground": "#ffffff",
"secondary": "#fefeff",
"secondary-hovered": "#fefeff",
"secondary-foreground": "#1a1a1c",
"destructive": "#b3474f",
"control-fill": "#d7d7de",
"control-fill-hovered": "#e0e0e4",
"control-fill-focused": "#d3d3d9",
"control-border": "#f9f9fa",
"switch-track-inactive": "#d7d7de",
"border": "#cdcdd5",
"divider": "#d8d8de",
"panel-border": "#d7d7dd",
"panel-border-highlighted": "#fefeff",
"properties-section-border": "#eaeaff",
"ring": "#4880d5",
"chart-1": "#ff3352",
"chart-2": "#8bdc00",
"chart-3": "#2890ff",
"chart-4": "#edba18",
"chart-5": "#ed5700"
}
},
"densities": {
"compact": {
"font-size": 10.0,
"line-height": 1.1,
"line-box": 11.0,
"control-height": 23.0,
"control-padding-x": 10.0,
"control-padding-y": 6.0,
"control-gap": 4.0,
"control-border-width": 1.0,
"menu-row-height": 20.0,
"popup-row-height": 20.0,
"explorer-row-height": 22.0,
"chrome-bar-height": 31.0,
"property-row-height": 28.0,
"label-column-height": 26.0,
"properties-section-gap": 6.0,
"explorer-row-base-padding": 4.0,
"explorer-row-end-padding": 10.0,
"list-row-indent": 6.0,
"container-gap": 8.0,
"container-padding": 16.0,
"gap-xxs": 2.0,
"gap-xs": 4.0,
"gap-sm": 6.0,
"gap-md": 8.0,
"gap-lg": 12.0,
"gap-xl": 16.0,
"gap-xxl": 24.0,
"text-xxs": 9.0,
"text-xs": 11.0,
"text-sm": 14.0,
"text-lg": 18.0,
"icon-sm": 10.0,
"icon-md": 20.0,
"icon-lg": 24.0
},
"normal": {
"font-size": 10.0,
"line-height": 1.1,
"line-box": 11.0,
"control-height": 27.0,
"control-padding-x": 12.0,
"control-padding-y": 8.0,
"control-gap": 5.0,
"control-border-width": 1.0,
"menu-row-height": 26.0,
"popup-row-height": 26.0,
"explorer-row-height": 30.0,
"chrome-bar-height": 37.0,
"property-row-height": 30.0,
"label-column-height": 26.0,
"properties-section-gap": 15.0,
"explorer-row-base-padding": 8.0,
"explorer-row-end-padding": 14.0,
"list-row-indent": 12.0,
"container-gap": 10.0,
"container-padding": 20.0,
"gap-xxs": 3.0,
"gap-xs": 5.0,
"gap-sm": 8.0,
"gap-md": 10.0,
"gap-lg": 15.0,
"gap-xl": 20.0,
"gap-xxl": 30.0,
"text-xxs": 9.0,
"text-xs": 11.0,
"text-sm": 14.0,
"text-lg": 18.0,
"icon-sm": 10.0,
"icon-md": 20.0,
"icon-lg": 24.0
},
"product": {
"font-size": 12.0,
"line-height": 1.25,
"line-box": 15.0,
"control-height": 33.0,
"control-padding-x": 10.0,
"control-padding-y": 9.0,
"control-gap": 5.0,
"control-border-width": 1.0,
"menu-row-height": 33.0,
"popup-row-height": 33.0,
"explorer-row-height": 29.0,
"chrome-bar-height": 43.0,
"property-row-height": 36.0,
"label-column-height": 31.0,
"properties-section-gap": 15.0,
"explorer-row-base-padding": 8.0,
"explorer-row-end-padding": 12.0,
"list-row-indent": 10.0,
"container-gap": 10.0,
"container-padding": 20.0,
"gap-xxs": 3.0,
"gap-xs": 5.0,
"gap-sm": 8.0,
"gap-md": 10.0,
"gap-lg": 15.0,
"gap-xl": 20.0,
"gap-xxl": 30.0,
"text-xxs": 11.0,
"text-xs": 13.0,
"text-sm": 17.0,
"text-lg": 22.0,
"icon-sm": 12.0,
"icon-md": 20.0,
"icon-lg": 24.0
}
},
"shape": {
"radius-xs": 2.0,
"radius-sm": 4.0,
"radius-md": 6.0,
"radius-lg": 8.0,
"radius-xl": 10.0,
"radius-xxl": 12.0,
"panel-radius": 10.0,
"panel-gap": 5.0,
"panel-border-width": 1.15,
"property-split": 0.4,
"property-label-gap": 10,
"dialog-max-width": 350
}
}