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:
co-authored by
Claude Opus 5.5
parent
572c09039f
commit
b5e5ed5b75
@@ -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.
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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 & Angles</span><span>A&A "Comfortable"</span><span>The hub</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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 & 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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
// Dumps the real theme values out to design_system/, so the design system
|
||||||
|
// never has to be typed out by hand (and never drifts from the package).
|
||||||
|
//
|
||||||
|
// cd garage_ui && flutter test tool/export_design_tokens_test.dart
|
||||||
|
//
|
||||||
|
// its a "test" only because thats the easiest way to get a flutter binding
|
||||||
|
// without an app. it writes files, it doesnt assert anything interesting.
|
||||||
|
|
||||||
|
import "dart:convert";
|
||||||
|
import "dart:io";
|
||||||
|
|
||||||
|
import "package:flutter/widgets.dart";
|
||||||
|
import "package:flutter_test/flutter_test.dart";
|
||||||
|
import "package:garage_ui/theme.dart";
|
||||||
|
|
||||||
|
String _hex(Color c) {
|
||||||
|
final argb = c.toARGB32();
|
||||||
|
final a = (argb >> 24) & 0xff;
|
||||||
|
final rgb = (argb & 0xffffff).toRadixString(16).padLeft(6, "0");
|
||||||
|
if (a == 0xff) return "#$rgb";
|
||||||
|
return "#$rgb${a.toRadixString(16).padLeft(2, "0")}";
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> _colours(ColourScheme s) => {
|
||||||
|
"background": _hex(s.background),
|
||||||
|
"foreground": _hex(s.foreground),
|
||||||
|
"chrome": _hex(s.chrome),
|
||||||
|
"surface-sunken": _hex(s.surfaceSunken),
|
||||||
|
"card": _hex(s.card),
|
||||||
|
"muted": _hex(s.muted),
|
||||||
|
"muted-foreground": _hex(s.mutedForeground),
|
||||||
|
"row-text": _hex(s.rowText),
|
||||||
|
"row-hovered": _hex(s.rowHovered),
|
||||||
|
"popover": _hex(s.popover),
|
||||||
|
"popover-border": _hex(s.popoverBorder),
|
||||||
|
"popover-item-hovered": _hex(s.popoverItemHovered),
|
||||||
|
"tooltip-background": _hex(s.tooltipBackground),
|
||||||
|
"tooltip-border": _hex(s.tooltipBorder),
|
||||||
|
"primary": _hex(s.primary),
|
||||||
|
"primary-hovered": _hex(s.primaryHovered),
|
||||||
|
"primary-foreground": _hex(s.primaryForeground),
|
||||||
|
"secondary": _hex(s.secondary),
|
||||||
|
"secondary-hovered": _hex(s.secondaryHovered),
|
||||||
|
"secondary-foreground": _hex(s.secondaryForeground),
|
||||||
|
"destructive": _hex(s.destructive),
|
||||||
|
"control-fill": _hex(s.controlFill),
|
||||||
|
"control-fill-hovered": _hex(s.controlFillHovered),
|
||||||
|
"control-fill-focused": _hex(s.controlFillFocused),
|
||||||
|
"control-border": _hex(s.controlBorder),
|
||||||
|
"switch-track-inactive": _hex(s.switchTrackInactive),
|
||||||
|
"border": _hex(s.border),
|
||||||
|
"divider": _hex(s.divider),
|
||||||
|
"panel-border": _hex(s.panelBorder),
|
||||||
|
"panel-border-highlighted": _hex(s.panelBorderHighlighted),
|
||||||
|
"properties-section-border": _hex(s.propertiesSectionBorder),
|
||||||
|
"ring": _hex(s.ring),
|
||||||
|
"chart-1": _hex(s.chart1),
|
||||||
|
"chart-2": _hex(s.chart2),
|
||||||
|
"chart-3": _hex(s.chart3),
|
||||||
|
"chart-4": _hex(s.chart4),
|
||||||
|
"chart-5": _hex(s.chart5),
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, num> _density(Density d) {
|
||||||
|
final t = ThemeData(colorScheme: GarageSchemes.carbon, density: d);
|
||||||
|
return {
|
||||||
|
"font-size": d.fontSize,
|
||||||
|
"line-height": d.lineHeight,
|
||||||
|
"line-box": d.lineBox,
|
||||||
|
"control-height": d.controlHeight,
|
||||||
|
"control-padding-x": d.buttonPaddingX,
|
||||||
|
"control-padding-y": d.controlPaddingY,
|
||||||
|
"control-gap": d.controlGap,
|
||||||
|
"control-border-width": d.controlBorderWidth,
|
||||||
|
"menu-row-height": d.menuRowHeight,
|
||||||
|
"popup-row-height": d.popupRowHeight,
|
||||||
|
"explorer-row-height": d.explorerRowHeight,
|
||||||
|
"chrome-bar-height": d.chromeBarHeight,
|
||||||
|
"property-row-height": d.propertyRowHeight,
|
||||||
|
// the label + subtitle stack a SettingsRow is sized off
|
||||||
|
"label-column-height": d.labelColumnHeight,
|
||||||
|
"properties-section-gap": d.propertiesSectionGap,
|
||||||
|
"explorer-row-base-padding": d.explorerRowBasePadding,
|
||||||
|
"explorer-row-end-padding": d.explorerRowEndPadding,
|
||||||
|
"list-row-indent": d.listRowIndent,
|
||||||
|
"container-gap": d.containerGap,
|
||||||
|
"container-padding": d.containerPadding,
|
||||||
|
"gap-xxs": d.gapXxs,
|
||||||
|
"gap-xs": d.gapXs,
|
||||||
|
"gap-sm": d.gapSm,
|
||||||
|
"gap-md": d.gapMd,
|
||||||
|
"gap-lg": d.gapLg,
|
||||||
|
"gap-xl": d.gapXl,
|
||||||
|
"gap-xxl": d.gapXxl,
|
||||||
|
"text-xxs": d.textXxs,
|
||||||
|
"text-xs": d.textXs,
|
||||||
|
"text-sm": d.textSm,
|
||||||
|
"text-lg": d.textLg,
|
||||||
|
"icon-sm": t.iconTheme.small.size ?? d.iconSize,
|
||||||
|
"icon-md": t.iconTheme.medium.size ?? 20,
|
||||||
|
"icon-lg": t.iconTheme.large.size ?? 24,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
String _num(num n) => n == n.roundToDouble() ? n.round().toString() : n.toString();
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test("export design tokens", () {
|
||||||
|
final out = Directory("../design_system");
|
||||||
|
out.createSync(recursive: true);
|
||||||
|
|
||||||
|
final t = ThemeData(colorScheme: GarageSchemes.carbon);
|
||||||
|
|
||||||
|
final schemes = {
|
||||||
|
"carbon": _colours(GarageSchemes.carbon),
|
||||||
|
"dark": _colours(GarageSchemes.dark),
|
||||||
|
"light": _colours(GarageSchemes.light),
|
||||||
|
};
|
||||||
|
final densities = {
|
||||||
|
"compact": _density(const Density.compact()),
|
||||||
|
"normal": _density(const Density.normal()),
|
||||||
|
"product": _density(const Density.product()),
|
||||||
|
};
|
||||||
|
final shape = {
|
||||||
|
"radius-xs": t.radiusXs,
|
||||||
|
"radius-sm": t.radiusSm,
|
||||||
|
"radius-md": t.radiusMd,
|
||||||
|
"radius-lg": t.radiusLg,
|
||||||
|
"radius-xl": t.radiusXl,
|
||||||
|
"radius-xxl": t.radiusXxl,
|
||||||
|
"panel-radius": t.panelRadius,
|
||||||
|
"panel-gap": t.panelGap,
|
||||||
|
"panel-border-width": 1.15,
|
||||||
|
"property-split": 0.4,
|
||||||
|
"property-label-gap": 10,
|
||||||
|
"dialog-max-width": 350,
|
||||||
|
};
|
||||||
|
|
||||||
|
File("${out.path}/tokens.json").writeAsStringSync(
|
||||||
|
const JsonEncoder.withIndent(" ").convert({
|
||||||
|
"_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": schemes,
|
||||||
|
"densities": densities,
|
||||||
|
"shape": shape,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final css = StringBuffer()
|
||||||
|
..writeln("/* garage_ui design tokens. Generated by garage_ui/tool/export_design_tokens_test.dart")
|
||||||
|
..writeln(" - dont edit by hand, rerun the export.")
|
||||||
|
..writeln(" Scheme: data-scheme=\"carbon\" (default) | \"dark\" | \"light\"")
|
||||||
|
..writeln(" Density: data-density=\"product\" (default) | \"normal\" | \"compact\" */")
|
||||||
|
..writeln()
|
||||||
|
..writeln("@import url(\"https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap\");")
|
||||||
|
..writeln();
|
||||||
|
|
||||||
|
void block(String selector, Map<String, String> vars) {
|
||||||
|
css.writeln("$selector {");
|
||||||
|
vars.forEach((k, v) => css.writeln(" --$k: $v;"));
|
||||||
|
css.writeln("}");
|
||||||
|
css.writeln();
|
||||||
|
}
|
||||||
|
|
||||||
|
block(":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",
|
||||||
|
for (final e in shape.entries)
|
||||||
|
e.key: (e.key == "property-split") ? _num(e.value) : "${_num(e.value)}px",
|
||||||
|
});
|
||||||
|
|
||||||
|
schemes.forEach((name, vars) {
|
||||||
|
final sel = name == "carbon" ? ":root, [data-scheme=\"carbon\"]" : "[data-scheme=\"$name\"]";
|
||||||
|
block(sel, vars);
|
||||||
|
});
|
||||||
|
|
||||||
|
// the default block has to come FIRST. `:root` and `[data-density=x]` are
|
||||||
|
// the same specificity, so whichever is later wins - with product last,
|
||||||
|
// <html data-density="compact"> quietly rendered product
|
||||||
|
final densityOrder = ["product", "compact", "normal"];
|
||||||
|
for (final name in densityOrder) {
|
||||||
|
final vars = densities[name]!;
|
||||||
|
final sel = name == "product" ? ":root, [data-density=\"product\"]" : "[data-density=\"$name\"]";
|
||||||
|
block(sel, {
|
||||||
|
for (final e in vars.entries)
|
||||||
|
e.key: e.key == "line-height" ? _num(e.value) : "${_num(e.value)}px",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
File("${out.path}/tokens.css").writeAsStringSync(css.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user