Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 20 additions & 34 deletions docs/design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ The visual system both pages share. It lives in `ui/css/tokens.css`.

Values are measured from a reference design kit, not chosen.

Two themes, dark and light. No stylesheet declares a `prefers-color-scheme` rule; see Themes below.

## The three colour layers

Pick from the lowest layer that answers the question.

**Palette.** Twelve hues and six greys, named by hue: `--sy-teal`, `--sy-red`, `--sy-gray4`. Each has a `-hi` partner holding an increased-contrast value. The `prefers-contrast: more` block swaps the palette to the `-hi` set.

Do not name a palette entry in a rule. The layers above point at it.
Do not name a palette entry in a rule.

**Roles.** What a colour is for: `--accent`, `--danger`, `--warning`, `--success`. Each points at a hue.

`--on-accent` is the ink on a filled control: an accent button, a selected row, a tile carrying a user-chosen colour. It is white in both themes. It is not a label level, because the surface under it is a colour rather than a background.
`--on-accent` is the ink on a filled control. It is white in both themes. It is not a label level.

**Semantic.** What a colour is applied to.

Expand All @@ -28,29 +26,25 @@ Do not name a palette entry in a rule. The layers above point at it.
| Backgrounds | `--bg-primary`, `--bg-secondary`, `--bg-tertiary` | The three layered surfaces |
| Backgrounds, elevated | `--bg-elevated-primary`, `--bg-elevated-secondary`, `--bg-elevated-tertiary` | The same three, over a sheet |

Two singletons sit beside them. `--bg-primary-light` is the opt-in light widget card. `--control-knob` is the white of a slider or switch knob, full white in both themes.
`--bg-primary-light` is the opt-in light widget card. `--control-knob` is a slider or switch knob, full white in both themes.

## Themes

The dark theme is the default and is declared on `:root`. The light theme redeclares the palette, the roles and the semantic sets under `html[data-theme="light"]`.

A page opts in by carrying the attribute. Only the Settings page does. The dashboard never sets it and has one appearance.
The dark theme is declared on `:root`. The light theme redeclares the palette, the roles and the semantic sets under `html[data-theme="light"]`. A page opts in by carrying the attribute. Only the Settings page does.

`ui/js/admin-theme.js` writes the attribute before the first paint. It reads the mode from `localStorage` under `sy-theme`: `system`, `light` or `dark`. `system` is resolved in script, not in a media query, so there is one light block rather than two. `ui/js/theme.js` holds the same logic for the running page, and `ui/test/theme.test.mjs` holds the two together.
No stylesheet declares a `prefers-color-scheme` rule. `ui/js/admin-theme.js` writes the attribute before the first paint, from `localStorage` under `sy-theme`: `system`, `light` or `dark`. `system` is resolved in script. `ui/js/theme.js` holds the same logic for the running page and `ui/test/theme.test.mjs` holds the two together.

The choice is per device. It is never written to the config.

Light hues are drawn for a fill. Green at `#34C759` behind text is a badge, not a sentence. A role that ends up as text or as the edge of a control points at the `-hi` entry, which is the accessible variant of the same hue. `--accent` is one of them.
Light hues are drawn for a fill. A role that ends up as text or as the edge of a control points at the `-hi` entry, the accessible variant of the same hue. `--accent` is one of them.

`prefers-contrast: more` raises whichever theme is in force. A light hue needs its `-hi` partner for the same reason a dark one does.

`ui/test/contrast.test.mjs` measures every required pair in four combinations: each theme, raised and not.
`prefers-contrast: more` raises whichever theme is in force. `ui/test/contrast.test.mjs` measures every required pair in four combinations: each theme, raised and not.

## Two limits on the semantic layer

**Label levels below primary do not clear WCAG on this project's cards.** Use `--sy-a11y-dim` for text that has to be read on a card. Use a label level for hierarchy. `ui/test/contrast.test.mjs` fails on a regression.
**Label levels below primary do not clear WCAG on this project's cards.** Use `--sy-a11y-dim` for text that has to be read on a card. Use a label level for hierarchy.

**The dashboard's glass is not built from Fills.** The glass is white at low alpha over a wallpaper. Fills are grey-tinted and assume an opaque surface. Substituting one for the other changes the look.
**The dashboard's glass is not built from Fills.** The glass is white at low alpha over a wallpaper. Fills are grey-tinted and assume an opaque surface.

## The face

Expand All @@ -62,7 +56,7 @@ One stack, everywhere:

`--font-ui` holds it. `ui/test/font-stack.test.mjs` fails on any other spelling, including in the widget guide and the widget template.

Do not name an optical cut. The stack resolves to the system face and picks the cut for the size it is drawn at: Text below 20, Display at 20 and above. Naming a cut pins it.
Do not name an optical cut. The stack picks the cut for the size it is drawn at. Naming a cut pins one.

`monospace` is the exception, for code spans in the admin hints.

Expand Down Expand Up @@ -96,7 +90,7 @@ Set all three tokens together. `ui/test/type-scale.test.mjs` fails on a size wit
}
```

Leading is stored unitless and tracking in `em`, never as pixels. Sizes are multiplied by `var(--sc)` and a reader can zoom, so both have to follow the size they are used with.
Leading is unitless and tracking is in `em`, never pixels. Sizes are multiplied by `var(--sc)` and a reader can zoom, so both have to follow the size in use.

On the admin page:

Expand All @@ -108,13 +102,9 @@ On the admin page:
| Segmented options, dialog body text, small glyph buttons | `footnote` |
| The version line and the mobile tab bar | `caption-2` |

A grouped list's header and footer are `footnote`. Caption 1 sits below the size either is drawn at.

### Steps

A page can run the whole scale at a smaller step. The same eleven styles at smaller sizes, never a size chosen outside the scale.

A document opts in with a class on `<html>`. The step applies only where the layout is not a phone. The admin page carries `type-small`, so its desktop panel runs at the Small step and a settings row is 15. On a phone a settings row is 17.
The same eleven styles at smaller sizes, never a size chosen outside the scale. A page opts in with a class on `<html>`, and the step applies only where the layout is not a phone. The admin page carries `type-small`, so a settings row is 15 on a desktop and 17 on a phone.

| Style | Default | Small |
| --- | --- | --- |
Expand All @@ -127,7 +117,7 @@ A document opts in with a class on `<html>`. The step applies only where the lay

Tracking is republished per step. There is one absolute tracking per style, so the em that reproduces -0.43 at 17 does not reproduce it at 15.

Adding a step is eleven declarations and an entry in `ui/test/type-scale.test.mjs`. Moving a page between steps is one class.
Adding a step is eleven declarations and an entry in `ui/test/type-scale.test.mjs`.

### Off the scale

Expand Down Expand Up @@ -162,9 +152,9 @@ Six rules do not use it, on purpose.

The switch knob is a capsule, not a circle.

On touch a slider is padded out to a 44 target and the paint is clipped back to the track. Clipping to the content box shrinks each corner by the padding on that axis. The padding is vertical only, so the two radii are set separately. A single radius draws ends that taper to a point.
On touch a slider is padded out to a 44 target and the paint is clipped back to the track. Clipping to the content box shrinks each corner by the padding on that axis. The padding is vertical only, so set the two radii separately. A single radius draws ends that taper to a point.

`--sy-radius-group` is separate from `--sy-radius-lg`. Only the grouped list is rounded to 26; the panel, the dialogs and the toast keep 14.
Only the grouped list is rounded to `--sy-radius-group`. The panel, the dialogs and the toast keep `--sy-radius-lg`.

Derive a radius that rounds something whose size varies. An app icon is drawn at 72 with a label and 78 without, and a widget tile scales with the dashboard.

Expand All @@ -176,23 +166,19 @@ The group keeps `overflow: visible` so a dropdown can escape it. The first and l

## Widgets

A widget is a separate document in an iframe. It keeps its own stylesheet and does not load `tokens.css`.

It cannot name a token. Most widget colours sit in canvas fills, SVG attributes and data modules, where a `var()` is not a colour. A widget carries the values instead.
A widget is a separate document in an iframe. It keeps its own stylesheet, does not load `tokens.css`, and cannot name a token. It carries the values instead.

**Colour.** Every colour must be a palette value or be listed in `ui/test/widget-colours.test.mjs` with what it is. The list covers artwork and colours that are not the project's to choose. A second test removes an entry once nothing uses it.
**Colour.** Every colour must be a palette value or be listed in `ui/test/widget-colours.test.mjs` with what it is. A second test removes an entry once nothing uses it.

**The face.** A widget must spell the font stack the same way as everything else.
**The face.** A widget spells the font stack the same way as everything else.

**Size.** Widget text is not on the type scale. The scale stops at 11 and the densest tiles go to 6.5. Sizes are snapped to a step where one is within a point.

## Page-scoped tokens

A surface particular to one page is declared in that page's own `:root`. `--pane` and `--cp` on the admin page, `--glass-bg` and `--dock-bg` on the dashboard. These are not part of the system.

A themed page declares its light values in the same file, under `html[data-theme="light"]`.
A surface particular to one page is declared in that page's own `:root`, and its light values in the same file under `html[data-theme="light"]`. `--pane` and `--cp` on the admin page, `--glass-bg` and `--dock-bg` on the dashboard. These are not part of the system.

The admin page also scopes two families there rather than writing values into rules.
The admin page scopes two more families there.

| Family | Tokens | Use |
| --- | --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion ui/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<title>Stackyard Settings</title>
<script src="/js/mobile-class.js?v=fe95da0d"></script>
<script src="/js/admin-theme.js?v=66565254"></script>
<link rel="stylesheet" href="/css/tokens.css?v=5077138e">
<link rel="stylesheet" href="/css/tokens.css?v=2f8e06aa">
<link rel="stylesheet" href="/css/admin.css?v=3904bb4a">
<link rel="stylesheet" href="/css/widget-config-form.css?v=53b91d5f">
</head>
Expand Down
Loading
Loading