@apliteni/apliteni-ui 0.23.2 → 0.23.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/overlay.js +28 -44
- package/src/components/shell.js +48 -76
- package/src/styles/table.css +10 -20
- package/src/tokens/accents.css +6 -16
- package/src/tokens/tokens.css +76 -136
package/package.json
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
// Overlay primitives — the page state the kit's modal surfaces share.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// the
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// Internal — not re-exported from src/index.js.
|
|
11
|
-
|
|
12
|
-
// What each overlay paints on, as its stylesheet resolves it today: a drawer at
|
|
13
|
-
// `--z-overlay` (src/styles/drawer.css) and a confirm one above it
|
|
14
|
-
// (src/styles/confirm.css). Absolute values, not ranks, so a sheet that moves and
|
|
15
|
-
// a table that did not is a failed test rather than a keyboard on the wrong layer
|
|
16
|
-
// — stories/overlay-css.test.js holds these two numbers to those two rules.
|
|
1
|
+
// Overlay primitives — the page state the kit's modal surfaces share. Drawer and Confirm
|
|
2
|
+
// are the same problem twice: content over a scrim that owns the keyboard until it is
|
|
3
|
+
// answered. What is inert, which overlay Escape talks to and where Tab may go are
|
|
4
|
+
// properties of the *page*, so they are answered here from one stack per document rather
|
|
5
|
+
// than from either component's own storage. Internal — not re-exported from src/index.js.
|
|
6
|
+
|
|
7
|
+
// What each overlay paints on today: a drawer at `--z-overlay` (styles/drawer.css) and a
|
|
8
|
+
// confirm above it (styles/confirm.css). Absolute values, not ranks, so a sheet that moves
|
|
9
|
+
// and a table that did not is a failed test — stories/overlay-css.test.js holds both.
|
|
17
10
|
export const OVERLAY_LAYER = { drawer: 100, confirm: 101 };
|
|
18
11
|
|
|
19
12
|
const FOCUSABLE = [
|
|
@@ -33,10 +26,9 @@ function pageOf(doc) {
|
|
|
33
26
|
return page;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
// Focusable to the *browser*, not merely matching the selector. A control in a
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// and Tab walks straight out of the modal.
|
|
29
|
+
// Focusable to the *browser*, not merely matching the selector. A control in a closed
|
|
30
|
+
// overlay, an inert subtree or a hidden ancestor is skipped by the real tab order, so a
|
|
31
|
+
// trap that counts it wraps at an element focus never reaches.
|
|
40
32
|
function reachable(el) {
|
|
41
33
|
for (let n = el; n && n.nodeType === 1; n = n.parentElement) {
|
|
42
34
|
if (n.inert || n.hasAttribute('inert') || n.hasAttribute('hidden')) return false;
|
|
@@ -53,10 +45,9 @@ export function focusablesIn(panel) {
|
|
|
53
45
|
return Array.from(panel.querySelectorAll(FOCUSABLE)).filter(reachable);
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
// Hide everything *outside* `root` from AT + the tab order: walk root→body and
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// not — a drawer under an aria-modal alertdialog must not be tabbable.
|
|
48
|
+
// Hide everything *outside* `root` from AT + the tab order: walk root→body and mark each
|
|
49
|
+
// ancestor's other children. Scrim and panel live inside the root so they stay
|
|
50
|
+
// interactive; a drawer under an aria-modal alertdialog is outside it and must not be.
|
|
60
51
|
function mark(page, doc, root) {
|
|
61
52
|
let node = root;
|
|
62
53
|
while (node && node.parentElement && node !== doc.body) {
|
|
@@ -81,10 +72,9 @@ function unmark(page) {
|
|
|
81
72
|
page.marked = [];
|
|
82
73
|
}
|
|
83
74
|
|
|
84
|
-
// Recompute the page from the stack — never replay a snapshot taken when an
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// what to give back outlives the node that hid it.
|
|
75
|
+
// Recompute the page from the stack — never replay a snapshot taken when an overlay
|
|
76
|
+
// opened, because by the time it closes that snapshot describes a page that has moved on.
|
|
77
|
+
// Roots torn down while open drop out here.
|
|
88
78
|
function sync(doc) {
|
|
89
79
|
const page = pageOf(doc);
|
|
90
80
|
for (let i = page.stack.length - 1; i >= 0; i--) {
|
|
@@ -128,12 +118,11 @@ function place(root, panel, dismiss, layer, where) {
|
|
|
128
118
|
sync(doc);
|
|
129
119
|
}
|
|
130
120
|
|
|
131
|
-
// The layer this root actually paints on. In a browser the live z-index is the
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// stands. `auto` must not read as 0; anything unparseable falls through.
|
|
121
|
+
// The layer this root actually paints on. In a browser the live z-index is the truth, so
|
|
122
|
+
// a consumer who moves an overlay in their own stylesheet gets the keyboard on the layer
|
|
123
|
+
// they can see. JSDOM hands back declared text rather than a number, so under test the
|
|
124
|
+
// passed constant stands — `auto` must not read as 0, and anything unparseable falls
|
|
125
|
+
// through.
|
|
137
126
|
function paintedLayer(root, layer) {
|
|
138
127
|
const painted = root.ownerDocument.defaultView?.getComputedStyle(root)?.zIndex;
|
|
139
128
|
const live = painted ? Number(painted) : NaN;
|
|
@@ -152,16 +141,11 @@ export function pushOverlay(root, panel, dismiss, layer) {
|
|
|
152
141
|
}
|
|
153
142
|
|
|
154
143
|
/**
|
|
155
|
-
* Take on a root that arrived already open — markup rendered with `open: true`,
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* position only separates two overlays on the same layer, and there it is the
|
|
161
|
-
* right answer rather than a fallback: at equal stack levels the later root
|
|
162
|
-
* paints on top (CSS 2.2 Appendix E, steps 8 and 9).
|
|
163
|
-
*
|
|
164
|
-
* A root that renders closed is left alone — wiring is not an opening.
|
|
144
|
+
* Take on a root that arrived already open — markup rendered with `open: true`, which
|
|
145
|
+
* nobody called open…() for. Without this its aria-modal is a claim the page contradicts.
|
|
146
|
+
* It goes in by PAINT ORDER, because wiring has no history to order by; document position
|
|
147
|
+
* separates two overlays on the same layer, where the later root paints on top (CSS 2.2
|
|
148
|
+
* Appendix E, steps 8 and 9). A root that renders closed is left alone.
|
|
165
149
|
*/
|
|
166
150
|
export function adoptOverlay(root, panel, dismiss, layer) {
|
|
167
151
|
if (!root.classList.contains('is-open')) return;
|
package/src/components/shell.js
CHANGED
|
@@ -10,9 +10,8 @@ import { sidebarNav, breadcrumbs } from './nav.js';
|
|
|
10
10
|
import { prism } from '../assets/brand.js';
|
|
11
11
|
import { ACCOUNT_NAV, toMenuTuple, initials } from './account-nav.js';
|
|
12
12
|
|
|
13
|
-
// The one account navigation definition lives in account-nav.js
|
|
14
|
-
//
|
|
15
|
-
// here so the published name docs/library.md documents keeps working.
|
|
13
|
+
// The one account navigation definition lives in account-nav.js because topbar.js needs
|
|
14
|
+
// it too; re-exported here so the name docs/library.md publishes keeps working.
|
|
16
15
|
export { ACCOUNT_NAV };
|
|
17
16
|
|
|
18
17
|
const str = (v) => (v == null ? '' : String(v));
|
|
@@ -20,32 +19,24 @@ const isRecord = (v) => typeof v === 'object' && v !== null;
|
|
|
20
19
|
|
|
21
20
|
// ---- the options bag, settled once --------------------------------------
|
|
22
21
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
// accountShell() has always taken its nav as [id, icon, label, href?, target?].
|
|
33
|
-
// Accept that shape and nav.js's object shape side by side, so a consumer's
|
|
34
|
-
// existing tuples and the exported ACCOUNT_NAV both work. A nav that is not a
|
|
35
|
-
// list at all falls back to the default; an entry inside one that is neither
|
|
36
|
-
// shape is dropped, because sideItem() reads `.items` off whatever it is given.
|
|
37
|
-
// An empty list is an answer, not a mistake — it stays empty.
|
|
22
|
+
// A default parameter covers `undefined` and nothing else, so `nav: null` from an
|
|
23
|
+
// /auth/me and a `crumbs` string from somebody reading the migration note both arrived
|
|
24
|
+
// as they were. A shell that throws mid-render takes the page with it, so each option is
|
|
25
|
+
// settled here, before the first sink sees it. Adding one to appShell() means adding it
|
|
26
|
+
// to SHAPES or deciding in the open that it needs nothing.
|
|
27
|
+
|
|
28
|
+
// accountShell()'s tuple nav — [id, icon, label, href?, target?] — and nav.js's object
|
|
29
|
+
// shape are accepted side by side. A nav that is not a list falls back to the default;
|
|
30
|
+
// an entry that is neither shape is dropped. An empty list is an answer and stays.
|
|
38
31
|
const toItems = (nav) => (Array.isArray(nav) ? nav : ACCOUNT_NAV)
|
|
39
32
|
.filter(isRecord)
|
|
40
33
|
.map((n) => (Array.isArray(n)
|
|
41
34
|
? { id: n[0], icon: n[1], label: n[2], href: n[3], target: n[4] }
|
|
42
35
|
: n));
|
|
43
36
|
|
|
44
|
-
// The trail is the caller's, and `crumbs`
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// read as a one-crumb trail — that would draw a plausible page and hide the
|
|
48
|
-
// migration mistake — so it is no trail at all, which is the visible answer. A
|
|
37
|
+
// The trail is the caller's, and `crumbs` changed shape this release: `crumb: 'Payouts'`
|
|
38
|
+
// became `crumbs: [{ label }]`, one letter apart. A non-list is NOT read as a one-crumb
|
|
39
|
+
// trail — that hides the migration mistake — so it is no trail at all, which shows. A
|
|
49
40
|
// crumb with no label would draw an empty cell, so it goes too.
|
|
50
41
|
const toCrumbs = (crumbs) => (Array.isArray(crumbs) ? crumbs : [])
|
|
51
42
|
.filter((c) => isRecord(c) && !Array.isArray(c) && str(c.label) !== '');
|
|
@@ -56,23 +47,16 @@ const toReader = (a) => (isRecord(a) ? { name: str(a.name), email: str(a.email)
|
|
|
56
47
|
|
|
57
48
|
// ---- the topbar, which interpolates where the rail escapes ----------------
|
|
58
49
|
//
|
|
59
|
-
// brand() writes `word` straight into its markup and accountMenu() does the
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// `initials` travels with them because it is derived, and a derived value comes
|
|
71
|
-
// from what the caller passed, not from the entities made of it: `<Ada>` and
|
|
72
|
-
// `<Ada>` do not start with the same character. The entries land in the
|
|
73
|
-
// same sink and toMenuTuple() is what escapes them; it reads item objects, so
|
|
74
|
-
// tuples go through toItems() first. A nav nobody passed stays unpassed, and
|
|
75
|
-
// accountMenu() falls back to its own derived default rather than to nothing.
|
|
50
|
+
// brand() writes `word` straight into its markup and accountMenu() does the same with
|
|
51
|
+
// the reader's name, address and menu entries, so the escaping is here on the one path
|
|
52
|
+
// into productTopbar() rather than in each caller. The caller passes text either way.
|
|
53
|
+
|
|
54
|
+
// The reader the menu draws. Both fields are always written, empty when the caller gave
|
|
55
|
+
// none: accountMenu()'s defaults are a demo identity, so a key dropped here is a key its
|
|
56
|
+
// fixture fills in. `initials` is derived from what the caller passed rather than from
|
|
57
|
+
// the entities made of it — `<Ada>` and `<Ada>` do not start with the same
|
|
58
|
+
// character. toMenuTuple() escapes the entries and reads item objects, so tuples go
|
|
59
|
+
// through toItems() first; a nav nobody passed stays unpassed.
|
|
76
60
|
const toMenuReader = (a) => {
|
|
77
61
|
const { name, email } = toReader(a);
|
|
78
62
|
const rest = isRecord(a) && !Array.isArray(a) ? a : {};
|
|
@@ -89,12 +73,11 @@ const toTopbar = (t) => {
|
|
|
89
73
|
return out;
|
|
90
74
|
};
|
|
91
75
|
|
|
92
|
-
// `maxWidth` lands inside a style attribute, so a length is all this accepts —
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
// drops the column to `none`, the full track.
|
|
76
|
+
// `maxWidth` lands inside a style attribute, so a length is all this accepts — a number
|
|
77
|
+
// and a unit, or `none`. Anything else yields '' and the caller writes no style
|
|
78
|
+
// attribute, falling through to --measure. It must REMOVE the property rather than pass
|
|
79
|
+
// a bad value on: a custom property accepts any token stream, so garbage is a valid
|
|
80
|
+
// declaration that drops the column to `none`, the full track.
|
|
98
81
|
// why: docs/specification.md#widths
|
|
99
82
|
const LENGTH = /^(?:\d+|\d*\.\d+)(?:px|rem|em|ch|%|vw)$/;
|
|
100
83
|
const mainMax = (v) => {
|
|
@@ -108,11 +91,9 @@ const SHAPES = {
|
|
|
108
91
|
nav: toItems, crumbs: toCrumbs, account: toReader, maxWidth: mainMax, topbar: toTopbar,
|
|
109
92
|
};
|
|
110
93
|
|
|
111
|
-
// The text options settle
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// left the brand link with no accessible name at all. Dropping the key is what
|
|
115
|
-
// lets the declared default apply.
|
|
94
|
+
// The text options settle by the same argument. `body: null` from a record with no
|
|
95
|
+
// description drew the word "null" on the page and `word: null` left the brand link with
|
|
96
|
+
// no accessible name; dropping the key is what lets the declared default apply.
|
|
116
97
|
const TEXT = ['word', 'brandHref', 'navLabel', 'title', 'sub', 'body', 'signOutHref', 'active'];
|
|
117
98
|
|
|
118
99
|
function settle(options) {
|
|
@@ -122,23 +103,18 @@ function settle(options) {
|
|
|
122
103
|
return out;
|
|
123
104
|
}
|
|
124
105
|
|
|
125
|
-
// Signing out is a navigation action, so it belongs in the rail nav's footer
|
|
126
|
-
//
|
|
127
|
-
// no session behind it.
|
|
106
|
+
// Signing out is a navigation action, so it belongs in the rail nav's footer slot.
|
|
107
|
+
// Opt-in: rendering it unasked puts a dead link on a page with no session behind it.
|
|
128
108
|
const signOut = (href) =>
|
|
129
109
|
`<a class="ui-nav__item is-danger" href="${esc(href)}" aria-label="Sign out">` +
|
|
130
110
|
`<span class="ui-nav__ic">${icon('logout')}</span>` +
|
|
131
111
|
`<span class="ui-nav__label">Sign out</span></a>`;
|
|
132
112
|
|
|
133
|
-
// Who is signed in. A sibling of the <nav>, not its footer: a
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// The initials carry the name, and the spelled-out half is aria-hidden. The
|
|
139
|
-
// narrow rail folds `.ui-app__who` out of view, so a name that lived only there
|
|
140
|
-
// left the initials on screen with nothing at all in the accessibility tree.
|
|
141
|
-
// Naming the mark instead makes the two agree at every width, and says it once.
|
|
113
|
+
// Who is signed in. A sibling of the <nav>, not its footer: a name and address are not
|
|
114
|
+
// navigation, and inside the landmark a screen reader announces the address as an entry.
|
|
115
|
+
// Empty when nobody is. The initials carry the name and the spelled-out half is
|
|
116
|
+
// aria-hidden, because the narrow rail folds `.ui-app__who` out of view and a name that
|
|
117
|
+
// lived only there left nothing in the accessibility tree.
|
|
142
118
|
function railUser({ name, email }) {
|
|
143
119
|
if (!name && !email) return '';
|
|
144
120
|
const who = [name, email].filter(Boolean).join(', ');
|
|
@@ -179,16 +155,14 @@ export function appShell(options = {}) {
|
|
|
179
155
|
ariaLabel: navLabel,
|
|
180
156
|
footer: signOutHref ? signOut(signOutHref) : '',
|
|
181
157
|
});
|
|
182
|
-
// The topbar already says the product word
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// so the name is written out — the mark itself is aria-hidden.
|
|
158
|
+
// The topbar already says the product word, so the rail head steps aside when there is
|
|
159
|
+
// one. The word is the link's only text and the narrow rail folds it out of view, so
|
|
160
|
+
// the name is written out — the mark itself is aria-hidden.
|
|
186
161
|
const brand = topbar ? '' : `<a class="ui-app__brand" href="${esc(brandHref)}" aria-label="${esc(word)}">`
|
|
187
162
|
+ `${prism(`appb-${++_shellUid}`, 24)}<span>${esc(word)}</span></a>`;
|
|
188
|
-
// A <div>, not an <aside>: <aside> is the `complementary` landmark
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
// already the landmark that names the menu.
|
|
163
|
+
// A <div>, not an <aside>: <aside> is the `complementary` landmark, and this holds the
|
|
164
|
+
// page's primary navigation and the signed-in reader. The <nav> inside it is already
|
|
165
|
+
// the landmark that names the menu.
|
|
192
166
|
const grid = `<div class="ui-app">
|
|
193
167
|
<div class="ui-app__rail">
|
|
194
168
|
${brand}
|
|
@@ -225,11 +199,9 @@ export function accountShell({
|
|
|
225
199
|
// topbar menu are handed one list rather than two readings of `nav`.
|
|
226
200
|
const items = toItems(nav);
|
|
227
201
|
const trail = [{ label: cap }, { label: crumb || title }];
|
|
228
|
-
// The preset hands the topbar the caller's text,
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
// as entities, and escaping nowhere is what the public `topbar` option used
|
|
232
|
-
// to do.
|
|
202
|
+
// The preset hands the topbar the caller's text, as it does the rail. toTopbar()
|
|
203
|
+
// escapes for the menu's raw sinks and runs once inside appShell(); escaping here as
|
|
204
|
+
// well would reach the menu as entities.
|
|
233
205
|
return appShell({
|
|
234
206
|
word,
|
|
235
207
|
nav: items,
|
package/src/styles/table.css
CHANGED
|
@@ -34,14 +34,10 @@
|
|
|
34
34
|
.ui-table tbody tr { transition: background var(--dur-fast) var(--ease); }
|
|
35
35
|
|
|
36
36
|
/* Row hover: an inset, rounded fill so the highlight never sits flush against a
|
|
37
|
-
* container's border (
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* Zebra tables keep the original full-bleed tint (finance ledger), so they're
|
|
42
|
-
* excluded here — their hover rule lives further down.
|
|
43
|
-
* The inset was 6px, which is a tie between --space-1 and --space-2; it rounds
|
|
44
|
-
* up, because what it buys is clearance and clearance rounds away from the edge. */
|
|
37
|
+
* container's border (#71). The separate border model with zero spacing keeps the row
|
|
38
|
+
* separators continuous while letting the row ends round, and table padding — which
|
|
39
|
+
* only applies in that model — insets the rows. Zebra keeps the original full-bleed
|
|
40
|
+
* tint (finance ledger) and is excluded here; its hover rule is below. */
|
|
45
41
|
.ui-table--hover:not(.ui-table--zebra) {
|
|
46
42
|
border-collapse: separate;
|
|
47
43
|
border-spacing: 0;
|
|
@@ -54,9 +50,7 @@
|
|
|
54
50
|
border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }
|
|
55
51
|
|
|
56
52
|
/* Data-table modifiers, composable: .ui-table--dense --zebra --hover.
|
|
57
|
-
* --dense --space-3 across, --space-2 down — one step in from the base
|
|
58
|
-
* table, and both round DOWN, because the modifier exists to fit
|
|
59
|
-
* more rows and rounding up spends the distinction it is for.
|
|
53
|
+
* --dense --space-3 across, --space-2 down — one step in from the base table
|
|
60
54
|
* --zebra row striping; 2px header rule; the hover tint out-ranks the stripe
|
|
61
55
|
* Cell helpers: .ui-table__num (right, tabular), .ui-table__code.
|
|
62
56
|
* why: docs/specification.md#spacing-and-rhythm
|
|
@@ -71,15 +65,11 @@
|
|
|
71
65
|
.ui-table--zebra th { border-bottom-width: 2px; }
|
|
72
66
|
.ui-table--zebra td { border-bottom: 0; }
|
|
73
67
|
.ui-table--zebra tbody tr:nth-child(even) td { background: var(--surface-2); }
|
|
74
|
-
/* The stripe
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* x=0 of the stripe. `DataTable` is that composition — it writes `ui-table
|
|
80
|
-
* ui-table--hover ui-table--zebra` and no `--dense` — so its select-all and per-row
|
|
81
|
-
* checkboxes sat flush against the tint with a measured 0px gap.
|
|
82
|
-
* Header and body are inset together, or the columns stop lining up. */
|
|
68
|
+
/* The stripe stays full-bleed (the #71 inset above is hover only), but it only reads right
|
|
69
|
+
* if the end cells are inset from the tint's own edge, and `.ui-table td` sets
|
|
70
|
+
* `padding-left: 0`. Only `--dense` supplied that inset, so `--zebra` without `--dense` —
|
|
71
|
+
* `DataTable`'s composition — sat its checkboxes at x=0 of the stripe. Head and body are
|
|
72
|
+
* inset together, or the columns stop lining up. */
|
|
83
73
|
.ui-table--zebra th:first-child,
|
|
84
74
|
.ui-table--zebra td:first-child { padding-left: var(--space-3); }
|
|
85
75
|
.ui-table--zebra th:last-child,
|
package/src/tokens/accents.css
CHANGED
|
@@ -62,25 +62,15 @@
|
|
|
62
62
|
--purple: #1156b8;
|
|
63
63
|
--purple-light: #1568d6;
|
|
64
64
|
--purple-mid: #1560c8;
|
|
65
|
-
/* Deepened for WCAG AA on the raised grey panel and its wash
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
which is what this cell ships — an earlier version of this comment quoted
|
|
69
|
-
them at the 0.05 the wash briefly carried, which flattered both inks.
|
|
70
|
-
Light Ocean is the one light accent #96 never deepened, because it happened
|
|
71
|
-
to clear plain white. Same kind of move, smaller: hue and chroma unchanged
|
|
72
|
-
in OKLCH (H 258, C 0.185) and four points of lightness down, where #96's
|
|
73
|
-
two took about ten points and gave up 15.9% of the chroma on Phoenix and
|
|
74
|
-
17.6% on Emerald. See issues #96 and #157. */
|
|
65
|
+
/* Deepened for WCAG AA on the raised grey panel and its wash — hue and chroma held
|
|
66
|
+
in OKLCH (H 258, C 0.185), four points of lightness down. The washed figures are
|
|
67
|
+
read at the 0.10 alpha below, which is what this cell ships. See issues #96, #157. */
|
|
75
68
|
--accent: #005bc8;
|
|
76
69
|
--accent-strong: var(--accent);
|
|
77
70
|
--accent-contrast: #ffffff;
|
|
78
|
-
/* Back to 0.10
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
wash does not have to be the thing that gives. The rgb stays --purple-light
|
|
82
|
-
rather than the accent — in light a glow tints --purple-light, which
|
|
83
|
-
stories/signal-contrast.test.js holds. See issue #157. */
|
|
71
|
+
/* Back to the 0.10 this cell carried before #157: deepening the ink closed the pair,
|
|
72
|
+
so the wash does not have to give. The rgb stays --purple-light rather than the
|
|
73
|
+
accent — in light a glow tints --purple-light, which signal-contrast.test.js holds. */
|
|
84
74
|
--glow-purple: rgba(21, 104, 214, 0.10);
|
|
85
75
|
--grad-from: #1568d6;
|
|
86
76
|
--grad-to: #17a2c0;
|
package/src/tokens/tokens.css
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
/* ============================================================================
|
|
2
|
-
* apliteni-ui — design tokens
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* purple accent, near-black surfaces, green = live, cyan = link/flag.
|
|
7
|
-
* Two themes: dark (default) and light. Toggle with `data-theme` on <html>.
|
|
8
|
-
*
|
|
9
|
-
* Everything downstream references the SEMANTIC tokens (--bg, --surface,
|
|
10
|
-
* --accent, --text…), never the raw ramp. Swap a ramp value once, and every
|
|
11
|
-
* component follows.
|
|
2
|
+
* apliteni-ui — design tokens. Single source of truth for the Apliteni deck theme,
|
|
3
|
+
* with viz/'s colour semantics: purple accent, near-black surfaces, green = live,
|
|
4
|
+
* cyan = link/flag. Two themes, dark (default) and light, toggled with `data-theme`
|
|
5
|
+
* on <html>. Everything downstream references the SEMANTIC tokens, never the ramp.
|
|
12
6
|
* ========================================================================== */
|
|
13
7
|
|
|
14
8
|
:root {
|
|
@@ -44,7 +38,6 @@
|
|
|
44
38
|
/* -- Measure ------------------------------------------------------------
|
|
45
39
|
* --container is the page, gutter to gutter; --measure is the reading column
|
|
46
40
|
* INSIDE a track, beside a sidebar. Different axes, never interchangeable.
|
|
47
|
-
* Below --measure are --panel-* and --prose-*, at the end of this file (#208).
|
|
48
41
|
* why: docs/specification.md#widths */
|
|
49
42
|
--container: 1120px;
|
|
50
43
|
--measure: 860px;
|
|
@@ -79,13 +72,12 @@
|
|
|
79
72
|
--tracking-caps: 0.14em;
|
|
80
73
|
|
|
81
74
|
/* -- Motion -------------------------------------------------------------
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* concern). The reusable effect layer lives in styles/motion.css. */
|
|
75
|
+
* The kit's legacy --dur-* and --ease names alias onto the brand's
|
|
76
|
+
* --duration-* and --easing-* primitives, so every component consumes one
|
|
77
|
+
* vocabulary; the fallbacks keep tokens.css self-sufficient when imported
|
|
78
|
+
* without brand.generated.css. Delays are the kit's own, and the reusable
|
|
79
|
+
* effect layer lives in styles/motion.css.
|
|
80
|
+
* why: CONTRIBUTING.md#brand-tokens-synced-from-design-system */
|
|
89
81
|
--ease: var(--easing-ease-in-out, cubic-bezier(0.4, 0, 0.2, 1));
|
|
90
82
|
--ease-out: var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1));
|
|
91
83
|
--ease-in: var(--easing-ease-in, cubic-bezier(0.4, 0, 1, 1));
|
|
@@ -119,22 +111,16 @@
|
|
|
119
111
|
color-scheme: dark;
|
|
120
112
|
|
|
121
113
|
--purple: #6a2dcc;
|
|
122
|
-
/* The ramp moves up one step so --accent is --purple-light again,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
the two tokens would resolve to one colour and the ramp would carry a
|
|
126
|
-
duplicate step. #bd8cff is the value docs/specification.md#colour-and-contrast reasons about and
|
|
127
|
-
rejects as the accent; it becomes the ramp's top step instead. Light
|
|
128
|
-
Nebula's ramp does not move. See issue #157. */
|
|
114
|
+
/* The ramp moves up one step so --accent is --purple-light again, the shape light
|
|
115
|
+
Nebula and light Ocean already ship; left alone the two tokens would resolve to
|
|
116
|
+
one colour. #bd8cff becomes the ramp's top step instead. See issue #157. */
|
|
129
117
|
--purple-light: #b479ff;
|
|
130
118
|
--purple-mid: #bd8cff;
|
|
131
119
|
--cyan: #20dcf5;
|
|
132
120
|
--green: #98ff8f;
|
|
133
|
-
/* Lighter than it looks like it should be
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
under AA — so danger moved AWAY from the canvas, not toward it. Light moves
|
|
137
|
-
the opposite way for the same reason. See issue #131. */
|
|
121
|
+
/* Lighter than it looks like it should be: --pink is read mostly ON its own 16%
|
|
122
|
+
glow, and that wash lifts the ground faster than the hue lifts the ink, so danger
|
|
123
|
+
moved AWAY from the canvas. Light moves the opposite way. See issue #131. */
|
|
138
124
|
--pink: #e97ca5;
|
|
139
125
|
--amber: #ffcf6a;
|
|
140
126
|
|
|
@@ -144,9 +130,8 @@
|
|
|
144
130
|
--surface-2: #1b1927;
|
|
145
131
|
--surface-3: #2a2739;
|
|
146
132
|
--seg-active-bg: #34314a; /* raised pill — clearly above the inset track in dark */
|
|
147
|
-
/* The raised handle on a control track (switch knob).
|
|
148
|
-
|
|
149
|
-
the accent fill the track takes when the control is on. */
|
|
133
|
+
/* The raised handle on a control track (switch knob). White in both themes and under
|
|
134
|
+
every accent: it reads on the plain track AND on the accent fill of an on control. */
|
|
150
135
|
--control-knob: #ffffff;
|
|
151
136
|
--border: #332f45;
|
|
152
137
|
--border-strong: #453f5c;
|
|
@@ -156,53 +141,40 @@
|
|
|
156
141
|
--dim: #c6c2d6;
|
|
157
142
|
--muted: #948fa8;
|
|
158
143
|
|
|
159
|
-
/* The paint of a control that is off. NOT an opacity: `opacity` is a group
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
faded accent. These three composite predictably instead: the ink is read on
|
|
164
|
-
the surface beside it, and both are opaque. They are aliases, not new
|
|
165
|
-
colours — the ramp gains nothing to keep in sync. A disabled control drops
|
|
166
|
-
its accent, which is most of what makes it read as inert; the ink going
|
|
167
|
-
quiet is the rest. Held by stories/guidelines/accessibility-floor.test.js.
|
|
144
|
+
/* The paint of a control that is off. NOT an opacity: `opacity` is a group property,
|
|
145
|
+
so it fades label and box toward the ground TOGETHER — a disabled primary button
|
|
146
|
+
measured 1.48:1 that way. These three are aliases that composite predictably, the
|
|
147
|
+
ink read on the surface beside it. Held by guidelines/accessibility-floor.test.js.
|
|
168
148
|
why: docs/specification.md#colour-and-contrast */
|
|
169
149
|
--disabled-ink: var(--muted);
|
|
170
150
|
--disabled-surface: var(--surface-2);
|
|
171
151
|
--disabled-border: var(--border);
|
|
172
152
|
|
|
173
|
-
/* Lifted from #9b5dff, which cleared the flat surfaces and failed on its own
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
--purple-light, which is how every other Nebula and Ocean cell is written.
|
|
177
|
-
--glow-purple follows it down the file; --ring is written as var(--accent)
|
|
178
|
-
and so follows it by reference. Held by
|
|
179
|
-
stories/accent-contrast.test.js. See issue #157.
|
|
153
|
+
/* Lifted from #9b5dff, which cleared the flat surfaces and failed on its own wash over
|
|
154
|
+
a card; the value was already in the ramp. --ring is var(--accent) and follows by
|
|
155
|
+
reference. Held by stories/accent-contrast.test.js. #157.
|
|
180
156
|
why: docs/specification.md#colour-and-contrast */
|
|
181
157
|
--accent: #b479ff;
|
|
182
158
|
--accent-strong: #7c3aed; /* button bg — white text clears WCAG AA at 5.70:1 */
|
|
183
159
|
--accent-contrast: #ffffff;
|
|
184
160
|
--ink: #e9e7f0;
|
|
185
161
|
|
|
186
|
-
/* Ink that reads on a SIGNAL colour once that colour becomes a fill — the
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
because LIGHT still needs one.
|
|
162
|
+
/* Ink that reads on a SIGNAL colour once that colour becomes a fill — the glyph in a
|
|
163
|
+
success circle, or on a toast's status circle. One near-black ink clears all five in
|
|
164
|
+
dark; danger keeps its own token because LIGHT still needs one.
|
|
190
165
|
why: docs/specification.md#colour-and-contrast */
|
|
191
166
|
--signal-contrast: #0c0c0c;
|
|
192
167
|
--danger-contrast: var(--signal-contrast);
|
|
193
168
|
|
|
194
169
|
--glow-green: rgba(152, 255, 143, 0.16);
|
|
195
|
-
/* Dropped from 0.18: the wash, not the surface beneath it, is what put --accent
|
|
196
|
-
|
|
197
|
-
it. Re-tinted from the new --accent, which signal-contrast.test.js requires.
|
|
198
|
-
See issue #157. */
|
|
170
|
+
/* Dropped from 0.18: the wash, not the surface beneath it, is what put --accent under
|
|
171
|
+
AA. Re-tinted from the new --accent, which signal-contrast.test.js requires. #157. */
|
|
199
172
|
--glow-purple: rgba(180, 121, 255, 0.12);
|
|
200
173
|
--glow-cyan: rgba(32, 220, 245, 0.14);
|
|
201
174
|
--glow-pink: rgba(233, 124, 165, 0.16);
|
|
202
175
|
|
|
203
|
-
/* Status-chip paint — the ink + fill of a badge / pill. Dark tints with the
|
|
204
|
-
|
|
205
|
-
on white), which is why these are a token pair and not just --green etc. */
|
|
176
|
+
/* Status-chip paint — the ink + fill of a badge / pill. Dark tints with the signal's
|
|
177
|
+
own glow; light needs a solid, deepened pair, which is why these are a token pair. */
|
|
206
178
|
--chip-success-ink: var(--green);
|
|
207
179
|
--chip-success-fill: var(--glow-green);
|
|
208
180
|
--chip-warn-ink: var(--amber);
|
|
@@ -212,14 +184,10 @@
|
|
|
212
184
|
--chip-info-ink: var(--cyan);
|
|
213
185
|
--chip-info-fill: var(--glow-cyan);
|
|
214
186
|
|
|
215
|
-
/* Solid signal surfaces — a status that has stopped being ink and become the
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
the bright signals, so the ink is near-black — it measures 15.87 on --green,
|
|
220
|
-
7.35 on --pink, 13.40 on --amber, 11.75 on --cyan and 6.29 on --muted.
|
|
221
|
-
Neutral needs no chip pair to take part: --muted is already its
|
|
222
|
-
fill-strength form. See issues #131 and #149. */
|
|
187
|
+
/* Solid signal surfaces — a status that has stopped being ink and become the FILL (a
|
|
188
|
+
solid toast). Fill and ink are ONE pair, never two independent picks: the fill is the
|
|
189
|
+
status at this theme's extreme and the ink the pole opposite it, so a single ink
|
|
190
|
+
clears all five. --muted is already its own fill-strength form. See #131, #149. */
|
|
223
191
|
--signal-solid-ink: var(--signal-contrast);
|
|
224
192
|
--signal-solid-success: var(--green);
|
|
225
193
|
--signal-solid-danger: var(--pink);
|
|
@@ -232,19 +200,18 @@
|
|
|
232
200
|
--shadow-lg: 0 18px 50px rgba(0, 0, 0, 0.5);
|
|
233
201
|
/* Tight elevation for the active segmented pill — stays contained in its track. */
|
|
234
202
|
--shadow-seg: 0 1px 2px rgba(0, 0, 0, 0.45), 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
235
|
-
/* Resting elevation for a card. Dark cards are borderless — the surface
|
|
236
|
-
|
|
203
|
+
/* Resting elevation for a card. Dark cards are borderless — the surface groups on
|
|
204
|
+
its own — so they cast nothing. */
|
|
237
205
|
--shadow-card: none;
|
|
238
|
-
/* THE ONLY --ring in the kit — light and every data-accent sub-theme declare
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
stories/guidelines/accessibility-floor.test.js. See issue #218.
|
|
206
|
+
/* THE ONLY --ring in the kit — light and every data-accent sub-theme declare an
|
|
207
|
+
--accent and inherit this line. Opaque because a translucent ring is a glow, and the
|
|
208
|
+
eight rgba() literals it replaced all missed WCAG 1.4.11's 3:1 (#218). Held by
|
|
209
|
+
stories/guidelines/accessibility-floor.test.js.
|
|
243
210
|
why: docs/specification.md#the-focus-ring */
|
|
244
211
|
--ring: 0 0 0 3px var(--accent);
|
|
245
212
|
|
|
246
|
-
/* The ink a cast shadow is made of, and the specular white that reads as
|
|
247
|
-
|
|
213
|
+
/* The ink a cast shadow is made of, and the specular white that reads as gloss on a
|
|
214
|
+
raised surface. Effects mix these instead of writing rgba(). */
|
|
248
215
|
--shadow-ink: #000000;
|
|
249
216
|
--sheen: #ffffff;
|
|
250
217
|
|
|
@@ -252,10 +219,8 @@
|
|
|
252
219
|
--scrim: rgba(0, 0, 0, 0.58);
|
|
253
220
|
|
|
254
221
|
/* Accent gradient stops (hero headline, marketing) — overridden per sub-theme.
|
|
255
|
-
--grad-from is pinned to
|
|
256
|
-
|
|
257
|
-
first stop, a visible change nobody asked for. Every --grad-from in
|
|
258
|
-
src/tokens/accents.css is already written as a literal for the same reason. */
|
|
222
|
+
--grad-from is pinned to a literal rather than tracking --purple-light, which #157
|
|
223
|
+
moved up a step; every --grad-from in accents.css is a literal for the same reason. */
|
|
259
224
|
--grad-from: #9b5dff;
|
|
260
225
|
--grad-to: var(--cyan);
|
|
261
226
|
}
|
|
@@ -271,15 +236,13 @@
|
|
|
271
236
|
--purple-mid: #7a3be0;
|
|
272
237
|
--cyan: #0c8fa8;
|
|
273
238
|
--green: #1c8a2c;
|
|
274
|
-
/* Deeper than the dark pink for the same reason dark's is lighter: the wash
|
|
275
|
-
|
|
276
|
-
3.87:1 on --glow-pink over white and 4.42:1 on plain white — the required
|
|
277
|
-
marker and the form error missed AA on nothing at all. See issue #131. */
|
|
239
|
+
/* Deeper than the dark pink for the same reason dark's is lighter: the wash under it
|
|
240
|
+
is white, so the ink has to come down to meet it. See issue #131. */
|
|
278
241
|
--pink: #b63361;
|
|
279
242
|
--amber: #b5730a;
|
|
280
243
|
|
|
281
|
-
/* A WHITE app:
|
|
282
|
-
|
|
244
|
+
/* A WHITE app: page and cards are both white, and cards are delineated by a hairline
|
|
245
|
+
border + soft shadow (see card.css), not a grey canvas. */
|
|
283
246
|
--bg: #ffffff;
|
|
284
247
|
--bg-elevated: #ffffff;
|
|
285
248
|
--surface: #ffffff;
|
|
@@ -295,9 +258,8 @@
|
|
|
295
258
|
--dim: #3a404b;
|
|
296
259
|
--muted: #5c6270;
|
|
297
260
|
|
|
298
|
-
/* Same three aliases as dark
|
|
299
|
-
|
|
300
|
-
card: a control that is off should sink, not rise. */
|
|
261
|
+
/* Same three aliases as dark. Light moves the disabled surface AWAY from white for the
|
|
262
|
+
same reason dark moves it away from the card: a control that is off should sink. */
|
|
301
263
|
--disabled-ink: var(--muted);
|
|
302
264
|
--disabled-surface: var(--surface-2);
|
|
303
265
|
--disabled-border: var(--border);
|
|
@@ -310,45 +272,33 @@
|
|
|
310
272
|
--signal-contrast: #0c0c0c;
|
|
311
273
|
--danger-contrast: #ffffff;
|
|
312
274
|
|
|
313
|
-
/* Re-tinted from --green
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
0.86 of a deltaE — under the 1.0 that is normally called imperceptible. No
|
|
318
|
-
verdict moves with it: body copy on the success wash goes 9.29 -> 9.15
|
|
319
|
-
against the 4.5 AA bar, and the --green icon on it 3.97 -> 3.91 against the
|
|
320
|
-
3.0 bar WCAG 1.4.11 sets for a graphic. The success chips are untouched —
|
|
321
|
-
light's --chip-success-fill is the solid #dff3e4, not this. Gated for all
|
|
322
|
-
four glows in stories/signal-contrast.test.js. See issue #131. */
|
|
275
|
+
/* Re-tinted from --green; it was rgba(30, 150, 50, .1), a colour that exists nowhere
|
|
276
|
+
else in the kit while the other three light glows are exact tints of their own token.
|
|
277
|
+
The wash over white moves at most two levels on a channel and no verdict moves with
|
|
278
|
+
it. Gated for all four glows in stories/signal-contrast.test.js. */
|
|
323
279
|
--glow-green: rgba(28, 138, 44, 0.1);
|
|
324
280
|
--glow-purple: rgba(106, 45, 204, 0.09);
|
|
325
281
|
--glow-cyan: rgba(12, 143, 168, 0.1);
|
|
326
282
|
--glow-pink: rgba(182, 51, 97, 0.1);
|
|
327
283
|
|
|
328
|
-
/* Solid chip pairs for the white app. Each ink is deepened past its signal
|
|
329
|
-
|
|
330
|
-
e.g. --pink (#b63361) on #fbe0ea is 4.66:1, #b7295f is 4.85:1. */
|
|
284
|
+
/* Solid chip pairs for the white app. Each ink is deepened past its signal hue so 10px
|
|
285
|
+
uppercase text clears WCAG AA on its own pale fill. */
|
|
331
286
|
--chip-success-ink: #1f7a38;
|
|
332
287
|
--chip-success-fill: #dff3e4;
|
|
333
288
|
--chip-warn-ink: #8a5e00;
|
|
334
289
|
--chip-warn-fill: #fbedd2;
|
|
335
290
|
--chip-danger-ink: #b7295f;
|
|
336
291
|
--chip-danger-fill: #fbe0ea;
|
|
337
|
-
/* Info was the one signal family without a pair, so the info badge
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
three pairs already sit in. See issue #131. */
|
|
292
|
+
/* Info was the one signal family without a pair, so the info badge painted --cyan on
|
|
293
|
+
--glow-cyan over white and missed AA. This pair sits in the band the other three
|
|
294
|
+
already occupy. See issue #131. */
|
|
341
295
|
--chip-info-ink: #0a7286;
|
|
342
296
|
--chip-info-fill: #ddeff3;
|
|
343
297
|
|
|
344
|
-
/* Light runs the same rule the other way. The signals are mid-tone here
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
clears every one: #ffffff measures 5.39 on --chip-success-ink, 6.01 on
|
|
349
|
-
--chip-danger-ink, 5.70 on --chip-warn-ink, 5.58 on --chip-info-ink and 6.11
|
|
350
|
-
on --muted. A future status supplies one --signal-solid-* value dark enough
|
|
351
|
-
for white and inherits the ink. See issues #131 and #149. */
|
|
298
|
+
/* Light runs the same rule the other way. The signals are mid-tone here, so NO ink
|
|
299
|
+
rescues them and the fill is what has to move: it takes the deepened form the chip
|
|
300
|
+
pair already carries, and white clears every one. A future status supplies one
|
|
301
|
+
--signal-solid-* value dark enough for white and inherits the ink. See #131, #149. */
|
|
352
302
|
--signal-solid-ink: #ffffff;
|
|
353
303
|
--signal-solid-success: var(--chip-success-ink);
|
|
354
304
|
--signal-solid-danger: var(--chip-danger-ink);
|
|
@@ -361,8 +311,8 @@
|
|
|
361
311
|
--shadow-lg: 0 18px 50px rgba(30, 30, 50, 0.16);
|
|
362
312
|
/* Tight elevation for the active segmented pill — stays contained in its track. */
|
|
363
313
|
--shadow-seg: 0 1px 2px rgba(30, 30, 50, 0.12), 0 2px 4px rgba(30, 30, 50, 0.1);
|
|
364
|
-
/* A white card on a white page needs its own
|
|
365
|
-
|
|
314
|
+
/* A white card on a white page needs its own flatter-than-sm elevation: a hairline
|
|
315
|
+
contact shadow plus a wide soft one (see card.css). */
|
|
366
316
|
--shadow-card: 0 1px 2px rgba(20, 25, 45, 0.04), 0 4px 14px rgba(20, 25, 45, 0.05);
|
|
367
317
|
/* No --ring here. :root's is var(--accent), and light re-points --accent. */
|
|
368
318
|
|
|
@@ -376,42 +326,32 @@
|
|
|
376
326
|
}
|
|
377
327
|
|
|
378
328
|
/* ============================================================================
|
|
379
|
-
* WIDTHS BELOW THE PAGE — the box, and the line
|
|
380
|
-
*
|
|
381
|
-
* --container is the page and --measure the reading column inside it (both
|
|
382
|
-
* above). Underneath them the kit has two scales and one rule for choosing
|
|
383
|
-
* between them: THE UNIT SAYS WHICH SCALE YOU ARE ON.
|
|
329
|
+
* WIDTHS BELOW THE PAGE — the box, and the line. Two scales, and one rule for
|
|
330
|
+
* choosing between them: THE UNIT SAYS WHICH SCALE YOU ARE ON.
|
|
384
331
|
*
|
|
385
332
|
* a box that holds a COMPONENT -> --panel-* (px)
|
|
386
333
|
* a box that holds a LINE -> --prose-* (ch)
|
|
387
334
|
*
|
|
388
|
-
* Both are theme-invariant,
|
|
335
|
+
* Both are theme-invariant, so they sit outside the palette blocks.
|
|
389
336
|
* why: docs/specification.md#boxes-below-the-page
|
|
390
337
|
* ========================================================================== */
|
|
391
338
|
:root {
|
|
392
339
|
/* -- Panel widths -------------------------------------------------------
|
|
393
|
-
* Not a new scale: the drawer has shipped sm/md/lg at
|
|
394
|
-
*
|
|
395
|
-
* wrote one of them out again on their own. --panel-md answers the question
|
|
396
|
-
* #208 asked — the two 420s were three (--confirm-w, --drawer-w md and
|
|
397
|
-
* .ui-auth__card), and they are one measure now. */
|
|
340
|
+
* Not a new scale: the drawer has shipped sm/md/lg at these three values, and
|
|
341
|
+
* confirm, the auth card and the toast each wrote one out again (#208). */
|
|
398
342
|
--panel-sm: 320px;
|
|
399
343
|
--panel-md: 420px;
|
|
400
344
|
--panel-lg: 560px;
|
|
401
345
|
|
|
402
346
|
/* -- Prose measures -----------------------------------------------------
|
|
403
|
-
* ch resolves against the font-size of the element it is DECLARED on, so a
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* The four steps are the reading band (44–72ch), named for what is being
|
|
408
|
-
* read rather than sized s/m/l, so the choice is made by the writer:
|
|
347
|
+
* ch resolves against the font-size of the element it is DECLARED on, so a step
|
|
348
|
+
* goes on the paragraph itself, never on a wrapper holding two type sizes. The
|
|
349
|
+
* four steps are the reading band (44–72ch), named for what is being read:
|
|
409
350
|
* caption one or two sentences under a glyph, centred
|
|
410
351
|
* lede the line under a title
|
|
411
352
|
* body a left-aligned column of copy
|
|
412
353
|
* dense reference prose set below 13px, where the band runs wider
|
|
413
|
-
* --prose-display is NOT a measure
|
|
414
|
-
* it is far under the band on purpose. */
|
|
354
|
+
* --prose-display is NOT a measure — it is where a display headline rags. */
|
|
415
355
|
--prose-display: 14ch;
|
|
416
356
|
--prose-caption: 44ch;
|
|
417
357
|
--prose-lede: 54ch;
|