mote.css 0.2.1 → 0.4.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 +82 -2
- package/README.md +9 -6
- package/mote.components.css +165 -13
- package/mote.css +55 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,83 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-10-20
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
Layout shell (`mote.components.css`), from the ten-app evaluation:
|
|
13
|
+
- `.mote-doc` mobile: nav/main/aside overlapped in one grid cell — the
|
|
14
|
+
collapse media query now resets `grid-row: auto`.
|
|
15
|
+
- `.mote-doc` blowout on wide content: content tracks are `minmax(0, 1fr)`
|
|
16
|
+
(+ `min-width: 0` on items); a wide table scrolls inside `main`, never the page.
|
|
17
|
+
- Rail nav no longer renders as the horizontal pill row: pill styles scoped
|
|
18
|
+
to `body > header nav`; rails get a quiet vertical list with
|
|
19
|
+
`aria-current` states.
|
|
20
|
+
Sticky rails and anchored content clear the site header via
|
|
21
|
+
`--mote-rail-top` (token) and automatic `scroll-padding` when
|
|
22
|
+
`body > header` exists. `body.mote-doc` gains page gutters.
|
|
23
|
+
|
|
24
|
+
Core (`mote.css`):
|
|
25
|
+
- `:user-invalid` kept its error border through hover and focus
|
|
26
|
+
(`:is(input, select, textarea):user-invalid`, placed to win the cascade) —
|
|
27
|
+
previously the field the user must fix showed the accent focus border.
|
|
28
|
+
- `--mote-accent-deep` now brightens in dark mode (`color-mix(accent,
|
|
29
|
+
white 14%)`); dark hovers, badges and notes no longer dim.
|
|
30
|
+
- `input[type="range"]` stripped of text-input chrome; `select[multiple]`
|
|
31
|
+
restored to native listbox appearance.
|
|
32
|
+
- `[role="status"]` / `[role="alert"]` read `--mote-status` (semantic
|
|
33
|
+
colors as fallback) instead of hard-coding.
|
|
34
|
+
- `.mote-note` gains block margin; `.mote-badge` line-height tamed in
|
|
35
|
+
headings; `blockquote` code no longer italic; `.mote-card` first/last-child
|
|
36
|
+
rules moved into `:where()` so page classes win; dialogs get
|
|
37
|
+
`max-width: min(90vw, 44rem)`; `.mote-avatar` sized by
|
|
38
|
+
`--mote-avatar` token; `.mote-skeleton` gets intrinsic size.
|
|
39
|
+
- Print: the light palette is forced on paper — dark fills (zebra, notes,
|
|
40
|
+
open details) no longer print as near-black bands.
|
|
41
|
+
- Focus rings no longer fade in (box-shadow removed from transitions).
|
|
42
|
+
|
|
43
|
+
Components:
|
|
44
|
+
- Popovers and dialogs now animate on exit, not just entrance
|
|
45
|
+
(closed-state deltas for the `allow-discrete` transitions).
|
|
46
|
+
- `details[name]` content padding fixed for multiple children
|
|
47
|
+
(was padding every child; now only the last).
|
|
48
|
+
- Toasts documented with `popover="manual"` (auto popovers light-dismiss);
|
|
49
|
+
samples corrected.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- README and docs CDN snippets use `mote.css@0` (range pin) so they never go
|
|
54
|
+
stale; exact-version pinning documented for reproducible builds.
|
|
55
|
+
- Docs: wide-table wrapper recipe, form-width guidance, document-shell
|
|
56
|
+
real-page notes (gutters, rail-top token), print caveats for exclusive
|
|
57
|
+
accordions, toast stacking guidance; section numbering fixed.
|
|
58
|
+
|
|
59
|
+
## [0.3.0] - 2026-10-19
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- Layout module in `mote.components.css`:
|
|
64
|
+
- `.mote-doc` document shell — opt in with one class on `<body>` and the
|
|
65
|
+
landmarks become the layout via `:has()`: sticky left rail for `nav`,
|
|
66
|
+
right rail for `aside`, `main` flexing between them, footer pinned to the
|
|
67
|
+
viewport bottom on short pages (body-scoped `min-height: 100svh`),
|
|
68
|
+
everything collapsing to a single column under 50rem. No classes on the
|
|
69
|
+
landmarks; `:where()` keeps every rule overridable.
|
|
70
|
+
- `.mote-grid` card grid — auto-fill columns computed from the container;
|
|
71
|
+
no breakpoints, no column utilities.
|
|
72
|
+
- Classless page shell: `body > header` and its nav are styled from landmarks
|
|
73
|
+
alone (sticky translucent bar + nav pills). `.mote-header` / `.mote-nav`
|
|
74
|
+
remain for the one case landmarks cannot express — shells wrapped in a
|
|
75
|
+
`<div>`.
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
- Documentation restructured: every element documented exactly once, with all
|
|
80
|
+
its status variants in place; the Status classes section is now the token
|
|
81
|
+
contract plus a consumer map. Complete status coverage (including muted)
|
|
82
|
+
across notes, badges, toasts, blockquotes, progress, meters, spinners and
|
|
83
|
+
summary markers.
|
|
84
|
+
|
|
8
85
|
## [0.2.1] - 2026-10-16
|
|
9
86
|
|
|
10
87
|
### Fixed
|
|
@@ -17,8 +94,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
17
94
|
- Release CI moved to GitLab CI (`.gitlab-ci.yml`): tag-triggered `npm stage
|
|
18
95
|
publish` with OIDC trusted publishing — no tokens anywhere; a maintainer
|
|
19
96
|
still approves every release with 2FA. The GitHub mirror is distribution-only.
|
|
20
|
-
-
|
|
21
|
-
public
|
|
97
|
+
- Provenance-ready: trusted publishing is configured and the source repository
|
|
98
|
+
is public; whether attestations survive the staged-approval flow gets proven
|
|
99
|
+
by the pipeline's `--provenance` flag at the next release.
|
|
22
100
|
- Package metadata points to the canonical GitLab source; GitHub remains the
|
|
23
101
|
distribution mirror.
|
|
24
102
|
|
|
@@ -86,6 +164,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
86
164
|
- Opt-in helpers: `.mote-container`, `.mote-muted`, `.mote-note` surfaces.
|
|
87
165
|
- Element gallery demo page (`demo.html`).
|
|
88
166
|
|
|
167
|
+
[0.3.0]: https://gitlab.com/abmach/mote/compare/v0.2.1...v0.3.0
|
|
168
|
+
[0.4.0]: https://gitlab.com/abmach/mote/compare/v0.3.0...v0.4.0
|
|
89
169
|
[0.2.1]: https://gitlab.com/abmach/mote/compare/v0.2.0...v0.2.1
|
|
90
170
|
[0.2.0]: https://gitlab.com/abmach/mote/compare/v0.1.0...v0.2.0
|
|
91
171
|
[0.1.0]: https://gitlab.com/abmach/mote/-/tags/v0.1.0
|
package/README.md
CHANGED
|
@@ -35,15 +35,16 @@ Source on [GitLab](https://gitlab.com/abmach/mote) · mirrored on [GitHub](https
|
|
|
35
35
|
**CDN** — jsDelivr, serving the npm package, with auto-minified variants:
|
|
36
36
|
|
|
37
37
|
```html
|
|
38
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0
|
|
38
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0/mote.css">
|
|
39
39
|
<!-- minified variant (dotted filenames minify to <name>.min.css) -->
|
|
40
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0
|
|
40
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0/mote.min.css">
|
|
41
41
|
|
|
42
42
|
<!-- optional components file -->
|
|
43
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0
|
|
43
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0/mote.components.css">
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
`@0` tracks the latest 0.x release, so these snippets never go stale. Pin an exact
|
|
47
|
+
version (`mote.css@0.3.0`) for reproducible production builds.
|
|
47
48
|
|
|
48
49
|
**npm**:
|
|
49
50
|
|
|
@@ -139,12 +140,14 @@ mote is classless by design; these are the only escape hatches, plus status clas
|
|
|
139
140
|
|
|
140
141
|
| Component | Hook |
|
|
141
142
|
|---|---|
|
|
143
|
+
| Document layout (landmark grid) | `.mote-doc` on `<body>` — `:has()` reads your `header`/`nav`/`main`/`aside`/`footer` |
|
|
144
|
+
| Sticky site header / nav pills | `body > header` — classless; `.mote-header`/`.mote-nav` only for shells wrapped in a `<div>` |
|
|
145
|
+
| Breadcrumbs | `nav[aria-label="breadcrumb"]` — semantic, no class |
|
|
146
|
+
| Card grids | `.mote-grid` — auto-fill columns from the container, no breakpoints |
|
|
142
147
|
| Cards | `.mote-card` |
|
|
143
148
|
| Badges | `.mote-badge` + optional status class |
|
|
144
149
|
| Avatars | `.mote-avatar` |
|
|
145
150
|
| Skeleton / spinner | `.mote-skeleton`, `.mote-spinner` |
|
|
146
|
-
| Sticky site header / nav pills | `.mote-header`, `.mote-nav` |
|
|
147
|
-
| Breadcrumbs | `nav[aria-label="breadcrumb"]` — semantic, no class |
|
|
148
151
|
| Tooltips | `[popover].mote-tooltip` + `popovertarget` button |
|
|
149
152
|
| Menus | `menu[popover]` + `popovertarget` button |
|
|
150
153
|
| Toasts | `[popover].mote-toast` + status class |
|
package/mote.components.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* mote.components.css — opt-in components for mote.css.
|
|
3
|
-
* v0.
|
|
3
|
+
* v0.4.0 · MIT
|
|
4
4
|
* https://gitlab.com/abmach/mote · mirror: https://github.com/abmach/mote
|
|
5
5
|
*
|
|
6
6
|
* Requires mote.css loaded first (provides the --mote-* tokens).
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
box-shadow: var(--mote-shadow-sm);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.mote-card > :first-child {
|
|
25
|
+
:where(.mote-card) > :first-child {
|
|
26
26
|
margin-block-start: 0;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
.mote-card > :last-child {
|
|
29
|
+
:where(.mote-card) > :last-child {
|
|
30
30
|
margin-block-end: 0;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
border-radius: 999px;
|
|
44
44
|
font-size: 0.8em;
|
|
45
45
|
font-weight: 500;
|
|
46
|
-
line-height: 1.
|
|
46
|
+
line-height: 1.4;
|
|
47
47
|
background: var(--mote-status-soft, var(--mote-accent-soft));
|
|
48
48
|
color: var(--mote-status, var(--mote-accent-deep));
|
|
49
49
|
user-select: none;
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
* ------------------------------------------------------------------------- */
|
|
55
55
|
|
|
56
56
|
.mote-avatar {
|
|
57
|
-
width: 2.5rem;
|
|
58
|
-
height: 2.5rem;
|
|
57
|
+
width: var(--mote-avatar, 2.5rem);
|
|
58
|
+
height: var(--mote-avatar, 2.5rem);
|
|
59
59
|
border-radius: 999px;
|
|
60
60
|
object-fit: cover;
|
|
61
61
|
border: var(--mote-border-width) solid var(--mote-border);
|
|
@@ -67,6 +67,8 @@
|
|
|
67
67
|
* ------------------------------------------------------------------------- */
|
|
68
68
|
|
|
69
69
|
.mote-skeleton {
|
|
70
|
+
display: inline-block;
|
|
71
|
+
min-height: 1em;
|
|
70
72
|
border-radius: var(--mote-radius-sm);
|
|
71
73
|
background:
|
|
72
74
|
linear-gradient(
|
|
@@ -105,9 +107,15 @@
|
|
|
105
107
|
|
|
106
108
|
/* ---------------------------------------------------------------------------
|
|
107
109
|
* 6. Site header and nav
|
|
110
|
+
*
|
|
111
|
+
* The page shell is styled classlessly: a header that is a direct
|
|
112
|
+
* child of <body> is the site bar, its nav the pill row. The classes
|
|
113
|
+
* exist for the one case landmarks cannot express — a shell wrapped
|
|
114
|
+
* in a <div> (common with frameworks), where body > header cannot
|
|
115
|
+
* see it. All selectors keep :where() specificity so overrides win.
|
|
108
116
|
* ------------------------------------------------------------------------- */
|
|
109
117
|
|
|
110
|
-
.mote-header {
|
|
118
|
+
:where(body > header, .mote-header) {
|
|
111
119
|
position: sticky;
|
|
112
120
|
top: 0;
|
|
113
121
|
z-index: 10;
|
|
@@ -116,7 +124,7 @@
|
|
|
116
124
|
border-block-end: var(--mote-border-width) solid var(--mote-border);
|
|
117
125
|
}
|
|
118
126
|
|
|
119
|
-
.mote-nav ul {
|
|
127
|
+
:where(body > header nav, .mote-nav) ul {
|
|
120
128
|
display: flex;
|
|
121
129
|
flex-wrap: wrap;
|
|
122
130
|
gap: 0.25rem;
|
|
@@ -125,7 +133,7 @@
|
|
|
125
133
|
list-style: none;
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
.mote-nav a {
|
|
136
|
+
:where(body > header nav, .mote-nav) a {
|
|
129
137
|
display: inline-block;
|
|
130
138
|
padding: 0.35rem 0.8rem;
|
|
131
139
|
border-radius: 999px;
|
|
@@ -134,12 +142,12 @@
|
|
|
134
142
|
transition: background var(--mote-transition), color var(--mote-transition);
|
|
135
143
|
}
|
|
136
144
|
|
|
137
|
-
.mote-nav a:hover {
|
|
145
|
+
:where(body > header nav, .mote-nav) a:hover {
|
|
138
146
|
background: var(--mote-accent-soft);
|
|
139
147
|
color: var(--mote-accent-deep);
|
|
140
148
|
}
|
|
141
149
|
|
|
142
|
-
.mote-nav a[aria-current="page"] {
|
|
150
|
+
:where(body > header nav, .mote-nav) a[aria-current="page"] {
|
|
143
151
|
background: var(--mote-accent);
|
|
144
152
|
color: var(--mote-on-accent);
|
|
145
153
|
}
|
|
@@ -261,7 +269,9 @@ menu[popover] :is(button, [role="menuitem"]):focus-visible {
|
|
|
261
269
|
* ------------------------------------------------------------------------- */
|
|
262
270
|
|
|
263
271
|
[popover],
|
|
264
|
-
dialog
|
|
272
|
+
dialog {
|
|
273
|
+
opacity: 0;
|
|
274
|
+
transform: translateY(8px) scale(0.98);
|
|
265
275
|
transition:
|
|
266
276
|
opacity 180ms ease,
|
|
267
277
|
transform 180ms ease,
|
|
@@ -322,7 +332,11 @@ details[name][open] summary {
|
|
|
322
332
|
|
|
323
333
|
details[name] > :not(summary) {
|
|
324
334
|
margin-block: 0;
|
|
325
|
-
padding: 0.15rem 1.2rem 0
|
|
335
|
+
padding: 0.15rem 1.2rem 0;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
details[name] > :not(summary):last-child {
|
|
339
|
+
padding-block-end: 0.9rem;
|
|
326
340
|
}
|
|
327
341
|
|
|
328
342
|
/* ---------------------------------------------------------------------------
|
|
@@ -370,3 +384,141 @@ details[name] > :not(summary) {
|
|
|
370
384
|
}
|
|
371
385
|
}
|
|
372
386
|
}
|
|
387
|
+
|
|
388
|
+
/* ---------------------------------------------------------------------------
|
|
389
|
+
* 12. Layout — document shell and card grids
|
|
390
|
+
*
|
|
391
|
+
* The document layout is opted in with a single class on <body> (or any
|
|
392
|
+
* wrapper): .mote-doc. The landmarks you already write — header, nav,
|
|
393
|
+
* main, aside, footer — become the layout; no classes on the landmarks.
|
|
394
|
+
* A nav becomes a left rail, an aside a right rail, and everything
|
|
395
|
+
* collapses to a single column under 50rem.
|
|
396
|
+
*
|
|
397
|
+
* All :has() guards sit in :where(), so the rules carry zero specificity
|
|
398
|
+
* weight — your own styles on the same landmarks always win.
|
|
399
|
+
* ------------------------------------------------------------------------- */
|
|
400
|
+
|
|
401
|
+
.mote-doc {
|
|
402
|
+
display: grid;
|
|
403
|
+
column-gap: 1.5rem;
|
|
404
|
+
grid-template-rows: auto 1fr auto;
|
|
405
|
+
grid-template-columns: 1fr;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/* On <body>, the middle row stretches the page so the footer pins to the
|
|
409
|
+
viewport bottom even when content is short. :where() keeps this
|
|
410
|
+
at base specificity; wrappers (demo boxes, embeds) stay natural-size. */
|
|
411
|
+
:where(body).mote-doc {
|
|
412
|
+
min-height: 100svh;
|
|
413
|
+
padding-inline: 1.5rem;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* Anchored content must clear the sticky shell header. Applies only
|
|
417
|
+
when the header actually exists. */
|
|
418
|
+
html:where(:has(body > header)) {
|
|
419
|
+
scroll-padding-block-start: 5rem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.mote-doc > header { grid-row: 1; grid-column: 1 / -1; }
|
|
423
|
+
.mote-doc > footer { grid-row: 3; grid-column: 1 / -1; }
|
|
424
|
+
|
|
425
|
+
.mote-doc > main { grid-row: 2; grid-column: 1; min-width: 0; }
|
|
426
|
+
|
|
427
|
+
/* nav = left rail */
|
|
428
|
+
.mote-doc > nav {
|
|
429
|
+
grid-row: 2;
|
|
430
|
+
grid-column: 1;
|
|
431
|
+
min-width: 0;
|
|
432
|
+
align-self: start;
|
|
433
|
+
position: sticky;
|
|
434
|
+
top: var(--mote-rail-top, 1rem);
|
|
435
|
+
max-height: calc(100svh - 2rem);
|
|
436
|
+
overflow: auto;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* Rail links: a quiet vertical list, not the horizontal pill row.
|
|
440
|
+
--mote-rail-top should clear the shell header: 1rem by default;
|
|
441
|
+
set it on :root when a sticky body > header is taller, e.g.
|
|
442
|
+
--mote-rail-top: 4.75rem. */
|
|
443
|
+
.mote-doc > nav ul {
|
|
444
|
+
display: flex;
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
gap: 0.15rem;
|
|
447
|
+
padding: 0;
|
|
448
|
+
margin: 0;
|
|
449
|
+
list-style: none;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.mote-doc > nav a {
|
|
453
|
+
display: block;
|
|
454
|
+
padding: 0.35rem 0.75rem;
|
|
455
|
+
border-radius: var(--mote-radius-sm);
|
|
456
|
+
text-decoration: none;
|
|
457
|
+
color: var(--mote-fg);
|
|
458
|
+
transition: background var(--mote-transition), color var(--mote-transition);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.mote-doc > nav a:hover {
|
|
462
|
+
background: var(--mote-accent-soft);
|
|
463
|
+
color: var(--mote-accent-deep);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.mote-doc > nav a[aria-current="page"] {
|
|
467
|
+
background: var(--mote-accent);
|
|
468
|
+
color: var(--mote-on-accent);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.mote-doc:where(:has(> nav)) {
|
|
472
|
+
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.mote-doc:where(:has(> nav)) > main {
|
|
476
|
+
grid-column: 2;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/* aside = right rail (or the main column when there is no nav) */
|
|
480
|
+
.mote-doc > aside {
|
|
481
|
+
grid-row: 2;
|
|
482
|
+
grid-column: 2;
|
|
483
|
+
min-width: 0;
|
|
484
|
+
align-self: start;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.mote-doc:where(:has(> nav)) > aside {
|
|
488
|
+
grid-column: 3;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.mote-doc:where(:has(> aside)) {
|
|
492
|
+
grid-template-columns: minmax(0, 1fr) minmax(10rem, 14rem);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.mote-doc:where(:has(> nav)):where(:has(> aside)) {
|
|
496
|
+
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr) minmax(10rem, 14rem);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@media (width < 50rem) {
|
|
500
|
+
.mote-doc:where(:has(> nav)),
|
|
501
|
+
.mote-doc:where(:has(> aside)),
|
|
502
|
+
.mote-doc:where(:has(> nav)):where(:has(> aside)) {
|
|
503
|
+
grid-template-columns: 1fr;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.mote-doc > :is(nav, aside, main) {
|
|
507
|
+
grid-column: 1 / -1;
|
|
508
|
+
grid-row: auto;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.mote-doc > nav {
|
|
512
|
+
position: static;
|
|
513
|
+
max-height: none;
|
|
514
|
+
overflow: visible;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Card grids: one class, no breakpoints, no column utilities.
|
|
519
|
+
Columns are computed from the container: as many as fit in 16rem. */
|
|
520
|
+
.mote-grid {
|
|
521
|
+
display: grid;
|
|
522
|
+
gap: 1.25rem;
|
|
523
|
+
grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
|
|
524
|
+
}
|
package/mote.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* mote.css — a mote of styling for plain HTML.
|
|
3
|
-
* v0.
|
|
3
|
+
* v0.4.0 · MIT
|
|
4
4
|
* https://gitlab.com/abmach/mote · mirror: https://github.com/abmach/mote
|
|
5
5
|
*
|
|
6
6
|
* The smallest thing that can carry style:
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
--mote-error: #d98a8a;
|
|
85
85
|
--mote-info: #96b1d1;
|
|
86
86
|
|
|
87
|
+
/* "Deep" is the emphasis direction per theme: darker on light
|
|
88
|
+
surfaces, brighter on dark ones. Never darken in dark mode. */
|
|
89
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), white 14%);
|
|
90
|
+
|
|
87
91
|
--mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
88
92
|
--mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
89
93
|
--mote-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23a49c90' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
@@ -105,6 +109,8 @@
|
|
|
105
109
|
--mote-error: #d98a8a;
|
|
106
110
|
--mote-info: #96b1d1;
|
|
107
111
|
|
|
112
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), white 14%);
|
|
113
|
+
|
|
108
114
|
--mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
109
115
|
--mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
110
116
|
--mote-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23a49c90' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
@@ -245,6 +251,10 @@ blockquote {
|
|
|
245
251
|
font-style: italic;
|
|
246
252
|
}
|
|
247
253
|
|
|
254
|
+
blockquote :where(code, samp, kbd, var) {
|
|
255
|
+
font-style: normal;
|
|
256
|
+
}
|
|
257
|
+
|
|
248
258
|
blockquote > cite {
|
|
249
259
|
display: block;
|
|
250
260
|
margin-block-start: 0.5rem;
|
|
@@ -403,7 +413,7 @@ input, select, textarea {
|
|
|
403
413
|
background: var(--mote-surface);
|
|
404
414
|
border: var(--mote-border-width) solid var(--mote-border);
|
|
405
415
|
border-radius: var(--mote-radius-sm);
|
|
406
|
-
transition: border-color var(--mote-transition)
|
|
416
|
+
transition: border-color var(--mote-transition);
|
|
407
417
|
}
|
|
408
418
|
|
|
409
419
|
input:hover, select:hover, textarea:hover {
|
|
@@ -428,6 +438,13 @@ select {
|
|
|
428
438
|
background-position: right 0.8rem center;
|
|
429
439
|
}
|
|
430
440
|
|
|
441
|
+
/* Multi-selects are listboxes, not dropdowns: restore native chrome */
|
|
442
|
+
select[multiple] {
|
|
443
|
+
appearance: auto;
|
|
444
|
+
padding: 0.5rem;
|
|
445
|
+
background-image: none;
|
|
446
|
+
}
|
|
447
|
+
|
|
431
448
|
[type="checkbox"], [type="radio"] {
|
|
432
449
|
width: 1em;
|
|
433
450
|
height: 1em;
|
|
@@ -447,6 +464,14 @@ select {
|
|
|
447
464
|
border-style: dashed;
|
|
448
465
|
}
|
|
449
466
|
|
|
467
|
+
/* Range inputs are sliders, not text fields: strip text chrome */
|
|
468
|
+
[type="range"] {
|
|
469
|
+
padding: 0;
|
|
470
|
+
border: none;
|
|
471
|
+
background: none;
|
|
472
|
+
accent-color: var(--mote-status, var(--mote-accent));
|
|
473
|
+
}
|
|
474
|
+
|
|
450
475
|
::placeholder {
|
|
451
476
|
color: var(--mote-muted);
|
|
452
477
|
opacity: 0.8;
|
|
@@ -457,7 +482,7 @@ select {
|
|
|
457
482
|
cursor: not-allowed;
|
|
458
483
|
}
|
|
459
484
|
|
|
460
|
-
:user-invalid {
|
|
485
|
+
:is(input, select, textarea):user-invalid {
|
|
461
486
|
border-color: var(--mote-error);
|
|
462
487
|
}
|
|
463
488
|
|
|
@@ -471,7 +496,7 @@ button, [type="button"], [type="submit"], [type="reset"] {
|
|
|
471
496
|
border-radius: var(--mote-radius-sm);
|
|
472
497
|
cursor: pointer;
|
|
473
498
|
transition: background var(--mote-transition), border-color var(--mote-transition),
|
|
474
|
-
|
|
499
|
+
transform var(--mote-transition);
|
|
475
500
|
}
|
|
476
501
|
|
|
477
502
|
button:hover, [type="button"]:hover, [type="submit"]:hover, [type="reset"]:hover {
|
|
@@ -537,6 +562,7 @@ dialog {
|
|
|
537
562
|
background: var(--mote-bg);
|
|
538
563
|
color: var(--mote-fg);
|
|
539
564
|
box-shadow: var(--mote-shadow);
|
|
565
|
+
max-width: min(90vw, 44rem);
|
|
540
566
|
}
|
|
541
567
|
|
|
542
568
|
dialog::backdrop {
|
|
@@ -550,8 +576,8 @@ progress, meter {
|
|
|
550
576
|
}
|
|
551
577
|
|
|
552
578
|
/* Semantic feedback helpers (pure HTML, works with <p>, <div>, anything) */
|
|
553
|
-
[role="alert"] { color: var(--mote-error); }
|
|
554
|
-
[role="status"] { color: var(--mote-info); }
|
|
579
|
+
[role="alert"] { color: var(--mote-status, var(--mote-error)); }
|
|
580
|
+
[role="status"] { color: var(--mote-status, var(--mote-info)); }
|
|
555
581
|
|
|
556
582
|
/* ---------------------------------------------------------------------------
|
|
557
583
|
* 11. Opt-in helpers
|
|
@@ -569,7 +595,7 @@ progress, meter {
|
|
|
569
595
|
.mote-muted { color: var(--mote-muted); }
|
|
570
596
|
|
|
571
597
|
/* ---------------------------------------------------------------------------
|
|
572
|
-
*
|
|
598
|
+
* 12. Status classes
|
|
573
599
|
*
|
|
574
600
|
* Statuses are pure token assignments — they set variables, never visual
|
|
575
601
|
* properties. Base classes (notes, badges, toasts) consume the tokens and
|
|
@@ -597,6 +623,7 @@ progress, meter {
|
|
|
597
623
|
|
|
598
624
|
/* Themed message surfaces — base shape only; status via the token classes */
|
|
599
625
|
.mote-note {
|
|
626
|
+
margin-block: 1rem;
|
|
600
627
|
padding: 0.8rem 1.1rem;
|
|
601
628
|
border-radius: var(--mote-radius-sm);
|
|
602
629
|
color: var(--mote-status, var(--mote-accent-deep));
|
|
@@ -604,7 +631,7 @@ progress, meter {
|
|
|
604
631
|
}
|
|
605
632
|
|
|
606
633
|
/* ---------------------------------------------------------------------------
|
|
607
|
-
*
|
|
634
|
+
* 13. Motion preferences & print
|
|
608
635
|
* ------------------------------------------------------------------------- */
|
|
609
636
|
|
|
610
637
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -617,6 +644,26 @@ progress, meter {
|
|
|
617
644
|
}
|
|
618
645
|
|
|
619
646
|
@media print {
|
|
647
|
+
/* Force the light palette on paper — dark fills (zebra, notes,
|
|
648
|
+
open details) must not print as near-black bands. Placed last:
|
|
649
|
+
source order ties the dark token blocks' specificity. */
|
|
650
|
+
:root,
|
|
651
|
+
:root[data-theme="dark"],
|
|
652
|
+
:root:not([data-theme="light"]) {
|
|
653
|
+
color-scheme: light;
|
|
654
|
+
--mote-bg: #faf9f7;
|
|
655
|
+
--mote-surface: #f1eee7;
|
|
656
|
+
--mote-fg: #2b2924;
|
|
657
|
+
--mote-muted: #716a60;
|
|
658
|
+
--mote-border: #e6e0d4;
|
|
659
|
+
--mote-accent: #6f5486;
|
|
660
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), black 14%);
|
|
661
|
+
--mote-success: #4e7d51;
|
|
662
|
+
--mote-warning: #8a6819;
|
|
663
|
+
--mote-error: #b34d4d;
|
|
664
|
+
--mote-info: #527097;
|
|
665
|
+
}
|
|
666
|
+
|
|
620
667
|
body {
|
|
621
668
|
background: #fff;
|
|
622
669
|
color: #000;
|