@stnd/styles 0.10.15 → 0.11.0
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/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/_standard-01-token.scss +4 -1
- package/_standard-11-forms.scss +88 -23
- package/_standard-13-components.scss +50 -3
- package/app.scss +236 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ File-by-file pass, adding WHY-comments and checking for cascade/scope bugs. Fixe
|
|
|
147
147
|
- **`body`'s own dead `--max-width` fallback** (`_standard-07-base.scss`)
|
|
148
148
|
— `max-width: var(--body-max-width, var(--max-width))`, left over from *my own* `--max-width` removal earlier this session (missed this one file at the time). `--body-max-width` always has a value, so the fallback never fired — harmless, but stale. Now matches `.prose`'s equivalent rule (`var(--body-max-width)`, no fallback).
|
|
149
149
|
- **Garbled leftover comment** (`_standard-07-base.scss`) — a `[id]`
|
|
150
|
-
block's comment repeated the same incomplete CSS fragment (`background: var(--color-surface-
|
|
150
|
+
block's comment repeated the same incomplete CSS fragment (`background: var(--color-surface-light-2);`) four times, pure noise. Replaced with an actual explanation of what `scroll-margin-top` there is for (keeps anchor-linked headings clear of the sticky header).
|
|
151
151
|
- **`.negate-body-padding*` docblock described a nonexistent API**
|
|
152
152
|
(`_standard-07-base.scss`) — those three class names, and the usage example, don't match the real implementation (`.no-body-margin`, and far more restrictive: only `<footer>`/`<header>` that are direct children of `<body>` and first/last-of-type). Neither name is used anywhere in `apps/` today, so nothing broke in practice, but the docs would have misled whoever reached for it next. Rewrote to match reality.
|
|
153
153
|
- **Duplicate identical `font-family` on `select`** (`_standard-11-forms.scss`)
|
|
@@ -162,7 +162,7 @@ Every flagged item below got fixed except three, which are real judgment calls a
|
|
|
162
162
|
- `_standard-02-color.scss`: dark-mode purple/pink/brown now reassign to their `--color-dark-*` counterparts (in both the `prefers-color-scheme: dark` block and the `[data-color-mode="dark"]` blocks); added the missing Stage 8 non-OKLCH fallback hex for all three; deleted the dead `0 solid transparent` `--border` line (kept `none` — see deferred items, the *value* wasn't touched); collapsed `--shadow`'s duplicated `--shadow-ambient` layer to one.
|
|
163
163
|
- `_standard-03-typography.scss`: `grid-line-height` mixin now reads `var(--font-header-line-height, ...)` instead of ignoring the token outright — headings actually respond to the frontmatter token now; deleted the dead hardcoded second `.font-interface` block.
|
|
164
164
|
- `_standard-04-grid.scss`: deleted the dead `--grid-gap` copy of `.grid.compact`/`.relaxed`; replaced every `[class^="grid-"]`/`[class|="grid"]` attribute selector with an explicit, order-independent `$grid-selectors` list (confirmed fix against real markup: `apps/stnd.build/modules/core/routes/index.astro`'s `class="cover grid-12"`).
|
|
165
|
-
- `_standard-06-prose.scss`: `.mycelial-graft` now uses real tokens (`--color-muted`, `--color-accent`, `--color-surface-
|
|
165
|
+
- `_standard-06-prose.scss`: `.mycelial-graft` now uses real tokens (`--color-muted`, `--color-accent`, `--color-surface-light-1`); deleted the stale `h1:first-child` comment.
|
|
166
166
|
- `_standard-07-base.scss`: mobile header padding-negation now sets `margin-block-start` (matching the desktop rule and the footer's symmetric block), not `margin-block-end`.
|
|
167
167
|
- `_standard-08-media.scss`: deleted the dead `--gap-body` padding line on `.image-zoom-overlay`; hardcoded `768px` → `#{$small}`.
|
|
168
168
|
- `_standard-09-lists.scss` / `_standard-50-utilities.scss`: removed all 5 dead `--base-gap` lines; `.display-flex` → `.flex` (the real class).
|
package/_standard-01-token.scss
CHANGED
|
@@ -148,7 +148,10 @@
|
|
|
148
148
|
--filter-glass: blur(20px);
|
|
149
149
|
--stroke-width: max(1px, 0.06rem);
|
|
150
150
|
--stroke-width-lg: calc(var(--stroke-width) * 2);
|
|
151
|
-
|
|
151
|
+
/* `--corner` is the documented hook (see Theme Authoring Hooks) — an app or
|
|
152
|
+
theme sets it and this follows. It was documented and never read, so
|
|
153
|
+
`--corner: 6px` in app.scss left --radius at --trim (~1.3px). */
|
|
154
|
+
--radius: var(--corner, var(--trim));
|
|
152
155
|
--radius-sm: 4px;
|
|
153
156
|
|
|
154
157
|
/**
|
package/_standard-11-forms.scss
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* @property input, textarea Zero-class text controls with interface typography, raised shadow, and accent focus rings.
|
|
15
15
|
* @property select Custom styled dropdown with double-chevron indicator and rhythm-aligned padding.
|
|
16
16
|
* @property input[role=switch] Accessible toggle switch with animated sliding thumb.
|
|
17
|
-
* @property --switch-height Primary height sizing knob for toggle switch controls.
|
|
17
|
+
* @property --switch-height Primary height sizing knob for toggle switch controls. Set it anywhere — on the switch, a class, or a container — and width and thumb follow.
|
|
18
18
|
* @property --switch-width Width sizing token for toggle switches (`calc(var(--switch-height) * 2)`).
|
|
19
|
-
* @property --switch-padding Inset padding controlling the toggle switch thumb gap (`
|
|
19
|
+
* @property --switch-padding Inset padding controlling the toggle switch thumb gap (`0px`).
|
|
20
20
|
* @property --slider-value Percentage value driving custom filled range slider tracks.
|
|
21
21
|
*
|
|
22
22
|
* @example html - A switch (role=switch on a checkbox)
|
|
@@ -117,7 +117,10 @@ select {
|
|
|
117
117
|
text-shadow: none !important;
|
|
118
118
|
text-box-trim: trim;
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
/* Room on the right for the chevron drawn below: without it a long value
|
|
121
|
+
ran under the arrow. */
|
|
122
|
+
padding: 0 calc(1em + var(--space-d4)) 0 var(--trim);
|
|
123
|
+
text-overflow: ellipsis;
|
|
121
124
|
|
|
122
125
|
appearance: none;
|
|
123
126
|
/* Match Combobox double-chevron */
|
|
@@ -134,6 +137,28 @@ select {
|
|
|
134
137
|
}
|
|
135
138
|
}
|
|
136
139
|
|
|
140
|
+
/* A colour input is a swatch, not a text field: fill the control with the
|
|
141
|
+
colour instead of inheriting the text-input padding around a tiny chip. */
|
|
142
|
+
input[type="color"] {
|
|
143
|
+
height: var(--space-2);
|
|
144
|
+
padding: var(--space-d8);
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
|
|
147
|
+
&::-webkit-color-swatch-wrapper {
|
|
148
|
+
padding: 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
&::-webkit-color-swatch {
|
|
152
|
+
border: none;
|
|
153
|
+
border-radius: calc(var(--radius-sm) - var(--stroke-width));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
&:disabled {
|
|
157
|
+
opacity: 0.4;
|
|
158
|
+
cursor: default;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
137
162
|
textarea {
|
|
138
163
|
resize: vertical;
|
|
139
164
|
min-height: var(--space-2);
|
|
@@ -250,24 +275,33 @@ input[type="radio"] {
|
|
|
250
275
|
}
|
|
251
276
|
|
|
252
277
|
input[type="checkbox"][role="switch"] {
|
|
253
|
-
--switch-height
|
|
254
|
-
|
|
278
|
+
/* The public knobs (--switch-height, --switch-width, --switch-padding) are
|
|
279
|
+
only READ here, never declared on the element. Declaring them on the
|
|
280
|
+
input itself made them impossible to set from outside: a value on a
|
|
281
|
+
container is shadowed by the element's own declaration, and a value on
|
|
282
|
+
the input loses to this rule's specificity — it only ever worked for an
|
|
283
|
+
inline style, or for a consumer that happened to out-rank this selector
|
|
284
|
+
by source order. The resolved values live in private --_switch-* props,
|
|
285
|
+
computed from whatever the cascade supplies, so a knob set anywhere
|
|
286
|
+
(ancestor, class, inline) takes effect and the others follow it. */
|
|
287
|
+
--_switch-height: var(--switch-height, calc(var(--space) - var(--space-d3)));
|
|
288
|
+
--_switch-width: var(--switch-width, calc(var(--_switch-height) * 2));
|
|
255
289
|
/* Was a fixed 2px — correct-looking at the default ~16px-tall switch
|
|
256
290
|
(an 8px thumb inset by 2px on each side), but any consumer overriding
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
--
|
|
291
|
+
the height alone (a smaller, denser control panel, say) kept that same
|
|
292
|
+
fixed inset and ended up with a visibly undersized thumb, since the
|
|
293
|
+
padding never scaled down with it. Went to 0 (Francis, iterating live:
|
|
294
|
+
the thumb should fill the track, full height, no ring of bare track
|
|
295
|
+
color around it) — the thumb's own diameter now equals the track's
|
|
296
|
+
height exactly. */
|
|
297
|
+
--_switch-padding: var(--switch-padding, 0px);
|
|
264
298
|
|
|
265
299
|
appearance: none;
|
|
266
300
|
box-sizing: border-box; /* Reset content-box from base checkbox rule */
|
|
267
301
|
position: relative;
|
|
268
302
|
top: 0; /* Reset top: 50% from base checkbox rule */
|
|
269
|
-
width: var(--
|
|
270
|
-
height: var(--
|
|
303
|
+
width: var(--_switch-width);
|
|
304
|
+
height: var(--_switch-height);
|
|
271
305
|
border-radius: 9999px;
|
|
272
306
|
border: none;
|
|
273
307
|
cursor: pointer;
|
|
@@ -304,10 +338,10 @@ input[type="checkbox"][role="switch"] {
|
|
|
304
338
|
&::before {
|
|
305
339
|
content: "";
|
|
306
340
|
position: absolute;
|
|
307
|
-
top: var(--
|
|
308
|
-
left: var(--
|
|
309
|
-
width: calc(var(--
|
|
310
|
-
height: calc(var(--
|
|
341
|
+
top: var(--_switch-padding);
|
|
342
|
+
left: var(--_switch-padding);
|
|
343
|
+
width: calc(var(--_switch-height) - var(--_switch-padding) * 2);
|
|
344
|
+
height: calc(var(--_switch-height) - var(--_switch-padding) * 2);
|
|
311
345
|
border-radius: 50%;
|
|
312
346
|
background: var(--color-surface-light-2);
|
|
313
347
|
/* Not --shadow-raised here on purpose — its ambient component
|
|
@@ -332,7 +366,7 @@ input[type="checkbox"][role="switch"] {
|
|
|
332
366
|
background: var(--color-accent);
|
|
333
367
|
&::before {
|
|
334
368
|
background: var(--color-surface-light-2);
|
|
335
|
-
transform: translateX(calc(var(--
|
|
369
|
+
transform: translateX(calc(var(--_switch-width) - var(--_switch-height)));
|
|
336
370
|
}
|
|
337
371
|
}
|
|
338
372
|
|
|
@@ -345,6 +379,37 @@ input[type="checkbox"][role="switch"] {
|
|
|
345
379
|
}
|
|
346
380
|
}
|
|
347
381
|
|
|
382
|
+
/* Progress: a filled track, the way a range is, for work that has a known
|
|
383
|
+
length. Indeterminate (no value) shows the empty track. */
|
|
384
|
+
progress {
|
|
385
|
+
-webkit-appearance: none;
|
|
386
|
+
appearance: none;
|
|
387
|
+
display: block;
|
|
388
|
+
width: 100%;
|
|
389
|
+
height: var(--space-d4);
|
|
390
|
+
border: none;
|
|
391
|
+
border-radius: 9999px;
|
|
392
|
+
overflow: hidden;
|
|
393
|
+
background: var(--color-surface-dark-2);
|
|
394
|
+
color: var(--color-accent);
|
|
395
|
+
|
|
396
|
+
&::-webkit-progress-bar {
|
|
397
|
+
background: var(--color-surface-dark-2);
|
|
398
|
+
border-radius: inherit;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
&::-webkit-progress-value {
|
|
402
|
+
background: var(--color-accent);
|
|
403
|
+
border-radius: inherit;
|
|
404
|
+
transition: width var(--duration-standard) var(--ease-soft);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
&::-moz-progress-bar {
|
|
408
|
+
background: var(--color-accent);
|
|
409
|
+
border-radius: inherit;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
348
413
|
input[type="range"] {
|
|
349
414
|
-webkit-appearance: none;
|
|
350
415
|
/* Webkit/Blink */
|
|
@@ -371,10 +436,10 @@ input[type="range"] {
|
|
|
371
436
|
actually renders. */
|
|
372
437
|
background: linear-gradient(
|
|
373
438
|
to right,
|
|
374
|
-
var(--color-
|
|
375
|
-
var(--color-
|
|
376
|
-
var(--color-
|
|
377
|
-
var(--color-
|
|
439
|
+
var(--color-accent) 0%,
|
|
440
|
+
var(--color-accent) var(--slider-value, 50%),
|
|
441
|
+
var(--color-surface-dark-2) var(--slider-value, 50%),
|
|
442
|
+
var(--color-surface-dark-2) 100%
|
|
378
443
|
);
|
|
379
444
|
}
|
|
380
445
|
|
|
@@ -241,6 +241,53 @@ a.btn:disabled {
|
|
|
241
241
|
cursor: not-allowed !important;
|
|
242
242
|
pointer-events: none !important;
|
|
243
243
|
}
|
|
244
|
+
/* A toggle button that is currently ON (aria-pressed): the accent, the same
|
|
245
|
+
cue whichever variant it wears. Zero-class so any variant or consumer rule
|
|
246
|
+
still wins over it. */
|
|
247
|
+
:where(button):is([aria-pressed="true"], [aria-current]:not([aria-current="false"])) {
|
|
248
|
+
color: var(--color-accent);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Segmented button group — the either/or control (Frames | Editorial, a row
|
|
252
|
+
of tabs). Documented in the header and referenced by the form spacing rules
|
|
253
|
+
for a long time, but never actually defined. The pressed one is marked the
|
|
254
|
+
way assistive tech reads it — aria-pressed / aria-selected / aria-checked /
|
|
255
|
+
aria-current — so the look can never disagree with the state. */
|
|
256
|
+
.btn-group {
|
|
257
|
+
display: inline-flex;
|
|
258
|
+
gap: 1px;
|
|
259
|
+
padding: 1px;
|
|
260
|
+
border-radius: var(--radius);
|
|
261
|
+
background: var(--color-surface-dark-1);
|
|
262
|
+
box-shadow: var(--shadow-inset);
|
|
263
|
+
|
|
264
|
+
> :where(button, a.btn) {
|
|
265
|
+
flex: 1 1 0;
|
|
266
|
+
min-width: 0;
|
|
267
|
+
background-color: transparent;
|
|
268
|
+
box-shadow: none;
|
|
269
|
+
color: var(--color-muted);
|
|
270
|
+
border-radius: calc(var(--radius) - 1px);
|
|
271
|
+
|
|
272
|
+
&:hover {
|
|
273
|
+
background-color: var(--color-surface);
|
|
274
|
+
color: var(--color-foreground);
|
|
275
|
+
box-shadow: none;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
&:is(
|
|
279
|
+
[aria-pressed="true"],
|
|
280
|
+
[aria-selected="true"],
|
|
281
|
+
[aria-checked="true"],
|
|
282
|
+
[aria-current]:not([aria-current="false"])
|
|
283
|
+
) {
|
|
284
|
+
background-color: var(--color-surface-light-2);
|
|
285
|
+
color: var(--color-foreground);
|
|
286
|
+
box-shadow: var(--shadow);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
244
291
|
/*
|
|
245
292
|
:is(:where(button), .btn) svg {
|
|
246
293
|
height: 100%;
|
|
@@ -1056,7 +1103,7 @@ a.skip-link {
|
|
|
1056
1103
|
|
|
1057
1104
|
.std-menu-content {
|
|
1058
1105
|
padding: var(--space-d4);
|
|
1059
|
-
background: var(--color-surface-
|
|
1106
|
+
background: var(--color-surface-light-1);
|
|
1060
1107
|
border: none;
|
|
1061
1108
|
border-radius: var(--radius);
|
|
1062
1109
|
box-shadow: var(--shadow-raised), var(--shadow-glow);
|
|
@@ -1112,12 +1159,12 @@ a.skip-link {
|
|
|
1112
1159
|
text-decoration: none;
|
|
1113
1160
|
|
|
1114
1161
|
&:hover:not(.disabled) {
|
|
1115
|
-
background: var(--color-surface-
|
|
1162
|
+
background: var(--color-surface-light-2);
|
|
1116
1163
|
box-shadow: var(--shadow);
|
|
1117
1164
|
}
|
|
1118
1165
|
|
|
1119
1166
|
&:active:not(.disabled) {
|
|
1120
|
-
background: var(--color-surface-
|
|
1167
|
+
background: var(--color-surface-dark-1);
|
|
1121
1168
|
box-shadow: var(--shadow-inset);
|
|
1122
1169
|
}
|
|
1123
1170
|
|
package/app.scss
CHANGED
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
* @property --window-inset Gap between the window edge and a floating pane.
|
|
19
19
|
* @property --pane-radius Corner of a pane floating inside the window.
|
|
20
20
|
* @property --titlebar-height Band centred on the OS traffic lights.
|
|
21
|
+
*
|
|
22
|
+
* Everything an application window needs that a document does not — this
|
|
23
|
+
* sheet is where it lives, so nothing has to be hunted for or rebuilt in an app:
|
|
24
|
+
*
|
|
25
|
+
* @property .pane A surface floating inside the window: inset, concentric corner, raised.
|
|
26
|
+
* @property .list, .item A column of rows. `.item` hovers, and reads aria-current / aria-selected as "this one".
|
|
27
|
+
* @property .hud A floating translucent chip or panel over the content (a toast, a status readout).
|
|
28
|
+
* @property .divider A hairline between groups.
|
|
29
|
+
* @property .window-controls, .window-close, .window-minimize, .window-zoom The traffic lights.
|
|
30
|
+
* @property .is-dragging, .is-drop-target The two states of a drag and drop.
|
|
31
|
+
* @property [data-reveal-host], [data-reveal] A control that only shows while its host is hovered or focused.
|
|
32
|
+
* @property .muted, .fine Secondary text and small print.
|
|
21
33
|
*/
|
|
22
34
|
:root {
|
|
23
35
|
/* ---- Type ------------------------------------------------------------
|
|
@@ -83,3 +95,227 @@
|
|
|
83
95
|
window top (same capture as --window-radius), band = twice that. */
|
|
84
96
|
--titlebar-height: 41.5px;
|
|
85
97
|
}
|
|
98
|
+
|
|
99
|
+
/* ---- Images -------------------------------------------------------------
|
|
100
|
+
standard.scss zooms an image on press (a lightbox fallback that needs no
|
|
101
|
+
script) — right for a document, wrong for an app, where an image is a
|
|
102
|
+
workflow item you select and drag. Switch that behaviour off here so an
|
|
103
|
+
app never has to fight it with !important of its own. */
|
|
104
|
+
img,
|
|
105
|
+
img:not([data-no-zoom]) {
|
|
106
|
+
cursor: inherit !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
html img:active,
|
|
110
|
+
html:not(.js-image-zoom-enabled) img:active {
|
|
111
|
+
position: static !important;
|
|
112
|
+
z-index: auto !important;
|
|
113
|
+
transform: none !important;
|
|
114
|
+
max-height: none !important;
|
|
115
|
+
max-width: none !important;
|
|
116
|
+
margin: 0 !important;
|
|
117
|
+
top: auto !important;
|
|
118
|
+
left: auto !important;
|
|
119
|
+
cursor: inherit !important;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
html:has(img:active)::before,
|
|
123
|
+
html:not(.js-image-zoom-enabled):has(img:active)::before {
|
|
124
|
+
display: none !important;
|
|
125
|
+
content: none !important;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ---- Panes --------------------------------------------------------------
|
|
129
|
+
A sidebar, an inspector, a tray: a surface that floats inside the window,
|
|
130
|
+
--window-inset from its edge, corner concentric with the window's. Every
|
|
131
|
+
one of them used to spell these five lines out for itself. Layout (width,
|
|
132
|
+
flex, padding) stays with the pane; this is only what makes it a pane. */
|
|
133
|
+
.pane {
|
|
134
|
+
margin: var(--window-inset);
|
|
135
|
+
background: var(--color-surface-light-1);
|
|
136
|
+
border-radius: var(--pane-radius);
|
|
137
|
+
box-shadow: var(--shadow);
|
|
138
|
+
overflow: hidden;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* ---- States ---------------------------------------------------------------
|
|
142
|
+
The "current" state of anything is said with ARIA, never a class of the
|
|
143
|
+
app's own: aria-current (this row, this page), aria-selected (this tab,
|
|
144
|
+
this option), aria-pressed (this toggle), aria-expanded, aria-disabled.
|
|
145
|
+
The kit below styles those, so the look can never disagree with what a
|
|
146
|
+
screen reader is told. Drag and drop has no ARIA of its own; it has two
|
|
147
|
+
classes. */
|
|
148
|
+
.is-dragging {
|
|
149
|
+
opacity: 0.4;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* A region switched off by the state of something else (a row of AI options
|
|
153
|
+
while AI is off). Native controls inside keep their own disabled styling. */
|
|
154
|
+
[aria-disabled="true"]:not(.item, button, a, input, select, textarea) {
|
|
155
|
+
opacity: 0.5;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.is-drop-target {
|
|
159
|
+
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
|
|
160
|
+
box-shadow: inset 0 0 0 var(--stroke-width) var(--color-accent);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Shown only while the host is hovered or holds focus — the quiet controls
|
|
164
|
+
of a row or a photo (remove, grip, reorder) that would clutter it at rest. */
|
|
165
|
+
[data-reveal] {
|
|
166
|
+
opacity: 0;
|
|
167
|
+
transition: opacity var(--duration-fast) var(--ease-soft);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
[data-reveal-host]:is(:hover, :focus-within) [data-reveal],
|
|
171
|
+
[data-reveal]:is(:hover, :focus-visible) {
|
|
172
|
+
opacity: 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ---- Lists ----------------------------------------------------------------
|
|
176
|
+
A column of rows: folders, notes, settings, a queue. */
|
|
177
|
+
.list {
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: column;
|
|
180
|
+
min-width: 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.list.divided > :not(:last-child) {
|
|
184
|
+
border-bottom: var(--border);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* A card whose content is rows: no padding of its own, rows run edge to edge. */
|
|
188
|
+
.card.flush {
|
|
189
|
+
padding: 0;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.item {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: flex-start;
|
|
197
|
+
gap: var(--space-d2);
|
|
198
|
+
min-width: 0;
|
|
199
|
+
padding: var(--space-d5) var(--space-d2);
|
|
200
|
+
border-radius: var(--radius-sm);
|
|
201
|
+
/* Works on a div or a button alike: a row is not a raised control. */
|
|
202
|
+
background: transparent;
|
|
203
|
+
box-shadow: none;
|
|
204
|
+
text-align: left;
|
|
205
|
+
color: var(--color-muted);
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
transition:
|
|
208
|
+
background var(--duration-instant) var(--ease-soft),
|
|
209
|
+
color var(--duration-instant) var(--ease-soft);
|
|
210
|
+
|
|
211
|
+
&:hover {
|
|
212
|
+
background: var(--color-surface);
|
|
213
|
+
color: var(--color-foreground);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
&:is([aria-current]:not([aria-current="false"]), [aria-selected="true"]) {
|
|
217
|
+
background: var(--color-surface-dark-1);
|
|
218
|
+
color: var(--color-foreground);
|
|
219
|
+
font-weight: var(--font-weight-bold);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&:is(:disabled, [aria-disabled="true"]) {
|
|
223
|
+
opacity: 0.5;
|
|
224
|
+
cursor: default;
|
|
225
|
+
pointer-events: none;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* ---- HUD ------------------------------------------------------------------
|
|
230
|
+
A translucent chip over the content: a toast, an export readout, a
|
|
231
|
+
legend. Not a pane — it carries no chrome of the window, it just floats. */
|
|
232
|
+
.hud {
|
|
233
|
+
display: inline-flex;
|
|
234
|
+
align-items: center;
|
|
235
|
+
gap: var(--space-d2);
|
|
236
|
+
padding: var(--space-d4) var(--space-d2);
|
|
237
|
+
border-radius: 999px;
|
|
238
|
+
background: color-mix(in srgb, var(--color-surface-light-1) 88%, transparent);
|
|
239
|
+
backdrop-filter: var(--filter-blur);
|
|
240
|
+
box-shadow: var(--shadow-raised);
|
|
241
|
+
color: var(--color-foreground);
|
|
242
|
+
font-size: var(--scale-d2);
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
|
|
245
|
+
&.panel {
|
|
246
|
+
display: flex;
|
|
247
|
+
border-radius: var(--radius-lg);
|
|
248
|
+
white-space: normal;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.divider {
|
|
253
|
+
flex-shrink: 0;
|
|
254
|
+
height: var(--stroke-width);
|
|
255
|
+
margin: 0;
|
|
256
|
+
border: none;
|
|
257
|
+
background: var(--color-border);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.muted {
|
|
261
|
+
color: var(--color-muted);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.fine {
|
|
265
|
+
font-size: var(--scale-d2);
|
|
266
|
+
color: var(--color-muted);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* ---- Window controls ------------------------------------------------------
|
|
270
|
+
The traffic lights, drawn by the page because the native ones are hidden
|
|
271
|
+
(an overlay title bar). At rest they are three quiet dots; over the group
|
|
272
|
+
they take the system colours and their glyphs. The three colours are
|
|
273
|
+
macOS's own, not the theme's. */
|
|
274
|
+
.window-controls {
|
|
275
|
+
display: flex;
|
|
276
|
+
gap: var(--space-d2);
|
|
277
|
+
|
|
278
|
+
> button {
|
|
279
|
+
all: unset;
|
|
280
|
+
box-sizing: border-box;
|
|
281
|
+
position: relative;
|
|
282
|
+
width: 12px;
|
|
283
|
+
height: 12px;
|
|
284
|
+
border-radius: 50%;
|
|
285
|
+
cursor: default;
|
|
286
|
+
background: var(--color-foreground);
|
|
287
|
+
opacity: 0.35;
|
|
288
|
+
transition:
|
|
289
|
+
opacity var(--duration-instant) var(--ease-soft),
|
|
290
|
+
background-color var(--duration-instant) var(--ease-soft);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
&:has(> button:hover) > button {
|
|
294
|
+
opacity: 1;
|
|
295
|
+
|
|
296
|
+
&::after {
|
|
297
|
+
position: absolute;
|
|
298
|
+
inset: 0;
|
|
299
|
+
display: grid;
|
|
300
|
+
place-items: center;
|
|
301
|
+
color: rgb(0 0 0 / 55%);
|
|
302
|
+
font-family: sans-serif;
|
|
303
|
+
font-size: 8px;
|
|
304
|
+
font-weight: 700;
|
|
305
|
+
line-height: 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
&:has(> button:hover) > .window-close {
|
|
310
|
+
background: #ff5f57;
|
|
311
|
+
&::after { content: "×"; }
|
|
312
|
+
}
|
|
313
|
+
&:has(> button:hover) > .window-minimize {
|
|
314
|
+
background: #febc2e;
|
|
315
|
+
&::after { content: "−"; }
|
|
316
|
+
}
|
|
317
|
+
&:has(> button:hover) > .window-zoom {
|
|
318
|
+
background: #28c840;
|
|
319
|
+
&::after { content: "+"; }
|
|
320
|
+
}
|
|
321
|
+
}
|