mote.css 0.3.0 → 0.5.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 +141 -4
- package/README.md +13 -4
- package/mote.components.css +111 -20
- package/mote.css +172 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,144 @@ 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.5.0] - 2026-10-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Semantic buttons: solid buttons take a status class (`.mote-success`,
|
|
13
|
+
`.mote-warning`, `.mote-error`, `.mote-info`, `.mote-muted`) — gated, so
|
|
14
|
+
the class must be on the button itself. Themed sections (error notes,
|
|
15
|
+
toasts, page-level status) never recolor the actions inside them; the
|
|
16
|
+
intent of an action is declared at the action site. Text color on
|
|
17
|
+
semantic solids is `--mote-on-status` (set by the status classes to the
|
|
18
|
+
page color, AA-verified in both themes).
|
|
19
|
+
- Ghost buttons (`.mote-ghost`): transparent fill, status-tinted border
|
|
20
|
+
and text. Gated like solids — the status class on the element itself
|
|
21
|
+
provides the tint, never the surrounding context.
|
|
22
|
+
- `.mote-default` and `.mote-reset`: control status inheritance for
|
|
23
|
+
the non-gated consumers (badges, notes, spinners, …). Default forces the
|
|
24
|
+
accent down the tree; reset clears the inherited status so each
|
|
25
|
+
element's own classes decide.
|
|
26
|
+
- `.mote-row` + `.mote-push`: flex row helper + right-align helper —
|
|
27
|
+
for headers, footers, toolbars, chip lists, form rows.
|
|
28
|
+
- `.mote-display` + `--mote-display` token: opt-in display/hero scale.
|
|
29
|
+
- `--mote-grid-min` variable on `.mote-grid`: density without a class API.
|
|
30
|
+
- `.mote-sr-only`: visually-hidden helper; doubles as a skip link when
|
|
31
|
+
focused.
|
|
32
|
+
- Semantic pagination: `nav[aria-label="pagination"]` — flex row of
|
|
33
|
+
pills with `aria-current` state, no class (mirrors breadcrumbs).
|
|
34
|
+
- Footer baseline: `body > footer` gets a top border and block padding
|
|
35
|
+
(the quiet sibling of the site header).
|
|
36
|
+
- `.mote-card` is now `display: flex; flex-direction: column` —
|
|
37
|
+
CTA-in-bottom-of-card aligns with `margin-block-start: auto` for free.
|
|
38
|
+
- Theme-aware hover deepening for buttons: `color-mix(accent/status,
|
|
39
|
+
fg 14%)` — darkens in light themes, brightens in dark ones.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Docs: buttons and statuses section (gating rationale, ghost inheritance),
|
|
44
|
+
`.mote-default`/`.mote-reset` guidance, new component table entries.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
Layout shell (`mote.components.css`), from the ten-app evaluation:
|
|
49
|
+
- `.mote-doc` mobile: nav/main/aside overlapped in one grid cell — the
|
|
50
|
+
collapse media query now resets `grid-row: auto`.
|
|
51
|
+
- `.mote-doc` blowout on wide content: content tracks are `minmax(0, 1fr)`
|
|
52
|
+
(+ `min-width: 0` on items); a wide table scrolls inside `main`, never the page.
|
|
53
|
+
- Rail nav no longer renders as the horizontal pill row: pill styles scoped
|
|
54
|
+
to `body > header nav`; rails get a quiet vertical list with
|
|
55
|
+
`aria-current` states.
|
|
56
|
+
Sticky rails and anchored content clear the site header via
|
|
57
|
+
`--mote-rail-top` (token) and automatic `scroll-padding` when
|
|
58
|
+
`body > header` exists. `body.mote-doc` gains page gutters.
|
|
59
|
+
|
|
60
|
+
Core (`mote.css`):
|
|
61
|
+
- `:user-invalid` kept its error border through hover and focus
|
|
62
|
+
(`:is(input, select, textarea):user-invalid`, placed to win the cascade) —
|
|
63
|
+
previously the field the user must fix showed the accent focus border.
|
|
64
|
+
- `--mote-accent-deep` now brightens in dark mode (`color-mix(accent,
|
|
65
|
+
white 14%)`); dark hovers, badges and notes no longer dim.
|
|
66
|
+
- `input[type="range"]` stripped of text-input chrome; `select[multiple]`
|
|
67
|
+
restored to native listbox appearance.
|
|
68
|
+
- `[role="status"]` / `[role="alert"]` read `--mote-status` (semantic
|
|
69
|
+
colors as fallback) instead of hard-coding.
|
|
70
|
+
- `.mote-note` gains block margin; `.mote-badge` line-height tamed in
|
|
71
|
+
headings; `blockquote` code no longer italic; `.mote-card` first/last-child
|
|
72
|
+
rules moved into `:where()` so page classes win; dialogs get
|
|
73
|
+
`max-width: min(90vw, 44rem)`; `.mote-avatar` sized by
|
|
74
|
+
`--mote-avatar` token; `.mote-skeleton` gets intrinsic size.
|
|
75
|
+
- Print: the light palette is forced on paper — dark fills (zebra, notes,
|
|
76
|
+
open details) no longer print as near-black bands.
|
|
77
|
+
- Focus rings no longer fade in (box-shadow removed from transitions).
|
|
78
|
+
|
|
79
|
+
Components:
|
|
80
|
+
- Popovers and dialogs now animate on exit, not just entrance
|
|
81
|
+
(closed-state deltas for the `allow-discrete` transitions).
|
|
82
|
+
- `details[name]` content padding fixed for multiple children
|
|
83
|
+
(was padding every child; now only the last).
|
|
84
|
+
- Toasts documented with `popover="manual"` (auto popovers light-dismiss);
|
|
85
|
+
samples corrected.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- README and docs CDN snippets use `mote.css@0` (range pin) so they never go
|
|
90
|
+
stale; exact-version pinning documented for reproducible builds.
|
|
91
|
+
- Docs: wide-table wrapper recipe, form-width guidance, document-shell
|
|
92
|
+
real-page notes (gutters, rail-top token), print caveats for exclusive
|
|
93
|
+
accordions, toast stacking guidance; section numbering fixed.
|
|
94
|
+
|
|
95
|
+
## [0.4.0] - 2026-10-20
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
|
|
99
|
+
Layout shell (`mote.components.css`), from the ten-app evaluation:
|
|
100
|
+
- `.mote-doc` mobile: nav/main/aside overlapped in one grid cell — the
|
|
101
|
+
collapse media query now resets `grid-row: auto`.
|
|
102
|
+
- `.mote-doc` blowout on wide content: content tracks are `minmax(0, 1fr)`
|
|
103
|
+
(+ `min-width: 0` on items); a wide table scrolls inside `main`, never the page.
|
|
104
|
+
- Rail nav no longer renders as the horizontal pill row: pill styles scoped
|
|
105
|
+
to `body > header nav`; rails get a quiet vertical list with
|
|
106
|
+
`aria-current` states.
|
|
107
|
+
Sticky rails and anchored content clear the site header via
|
|
108
|
+
`--mote-rail-top` (token) and automatic `scroll-padding` when
|
|
109
|
+
`body > header` exists. `body.mote-doc` gains page gutters.
|
|
110
|
+
|
|
111
|
+
Core (`mote.css`):
|
|
112
|
+
- `:user-invalid` kept its error border through hover and focus
|
|
113
|
+
(`:is(input, select, textarea):user-invalid`, placed to win the cascade) —
|
|
114
|
+
previously the field the user must fix showed the accent focus border.
|
|
115
|
+
- `--mote-accent-deep` now brightens in dark mode (`color-mix(accent,
|
|
116
|
+
white 14%)`); dark hovers, badges and notes no longer dim.
|
|
117
|
+
- `input[type="range"]` stripped of text-input chrome; `select[multiple]`
|
|
118
|
+
restored to native listbox appearance.
|
|
119
|
+
- `[role="status"]` / `[role="alert"]` read `--mote-status` (semantic
|
|
120
|
+
colors as fallback) instead of hard-coding.
|
|
121
|
+
- `.mote-note` gains block margin; `.mote-badge` line-height tamed in
|
|
122
|
+
headings; `blockquote` code no longer italic; `.mote-card` first/last-child
|
|
123
|
+
rules moved into `:where()` so page classes win; dialogs get
|
|
124
|
+
`max-width: min(90vw, 44rem)`; `.mote-avatar` sized by
|
|
125
|
+
`--mote-avatar` token; `.mote-skeleton` gets intrinsic size.
|
|
126
|
+
- Print: the light palette is forced on paper — dark fills (zebra, notes,
|
|
127
|
+
open details) no longer print as near-black bands.
|
|
128
|
+
- Focus rings no longer fade in (box-shadow removed from transitions).
|
|
129
|
+
|
|
130
|
+
Components:
|
|
131
|
+
- Popovers and dialogs now animate on exit, not just entrance
|
|
132
|
+
(closed-state deltas for the `allow-discrete` transitions).
|
|
133
|
+
- `details[name]` content padding fixed for multiple children
|
|
134
|
+
(was padding every child; now only the last).
|
|
135
|
+
- Toasts documented with `popover="manual"` (auto popovers light-dismiss);
|
|
136
|
+
samples corrected.
|
|
137
|
+
|
|
138
|
+
### Changed
|
|
139
|
+
|
|
140
|
+
- README and docs CDN snippets use `mote.css@0` (range pin) so they never go
|
|
141
|
+
stale; exact-version pinning documented for reproducible builds.
|
|
142
|
+
- Docs: wide-table wrapper recipe, form-width guidance, document-shell
|
|
143
|
+
real-page notes (gutters, rail-top token), print caveats for exclusive
|
|
144
|
+
accordions, toast stacking guidance; section numbering fixed.
|
|
145
|
+
|
|
8
146
|
## [0.3.0] - 2026-10-19
|
|
9
147
|
|
|
10
148
|
### Added
|
|
@@ -113,11 +251,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
113
251
|
- Opt-in helpers: `.mote-container`, `.mote-muted`, `.mote-note` surfaces.
|
|
114
252
|
- Element gallery demo page (`demo.html`).
|
|
115
253
|
|
|
254
|
+
[0.3.0]: https://gitlab.com/abmach/mote/compare/v0.2.1...v0.3.0
|
|
255
|
+
[0.5.0]: https://gitlab.com/abmach/mote/compare/v0.4.0...v0.5.0
|
|
256
|
+
[0.4.0]: https://gitlab.com/abmach/mote/compare/v0.3.0...v0.4.0
|
|
116
257
|
[0.3.0]: https://gitlab.com/abmach/mote/compare/v0.2.1...v0.3.0
|
|
117
258
|
[0.2.1]: https://gitlab.com/abmach/mote/compare/v0.2.0...v0.2.1
|
|
118
259
|
[0.2.0]: https://gitlab.com/abmach/mote/compare/v0.1.0...v0.2.0
|
|
119
260
|
[0.1.0]: https://gitlab.com/abmach/mote/-/tags/v0.1.0
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
Copyright (c) 2026 Arthur Biazon Machado. All rights reserved.
|
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
|
|
|
@@ -141,6 +142,14 @@ mote is classless by design; these are the only escape hatches, plus status clas
|
|
|
141
142
|
|---|---|
|
|
142
143
|
| Document layout (landmark grid) | `.mote-doc` on `<body>` — `:has()` reads your `header`/`nav`/`main`/`aside`/`footer` |
|
|
143
144
|
| Sticky site header / nav pills | `body > header` — classless; `.mote-header`/`.mote-nav` only for shells wrapped in a `<div>` |
|
|
145
|
+
| Semantic buttons (gated) | `class="mote-success"` etc. on the button itself — themed sections never recolor actions |
|
|
146
|
+
| Ghost buttons | `.mote-ghost` — transparent fill; status tints from the button's own class (gated like solids) |
|
|
147
|
+
| Display text | `.mote-display` — opt-in hero/error-code scale via `--mote-display` |
|
|
148
|
+
| Content rows | `.mote-row` + `.mote-push` — flex row, right-align helper |
|
|
149
|
+
| Pagination | `nav[aria-label="pagination"]` — semantic, no class |
|
|
150
|
+
| Footer baseline | `body > footer` — classless top-border + padding |
|
|
151
|
+
| Screen-reader helper | `.mote-sr-only` (doubles as a skip link when focused) |
|
|
152
|
+
| Status inheritance control | `.mote-default` (force accent) · `.mote-reset` (clear inherited status) |
|
|
144
153
|
| Breadcrumbs | `nav[aria-label="breadcrumb"]` — semantic, no class |
|
|
145
154
|
| Card grids | `.mote-grid` — auto-fill columns from the container, no breakpoints |
|
|
146
155
|
| Cards | `.mote-card` |
|
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.5.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).
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* ------------------------------------------------------------------------- */
|
|
16
16
|
|
|
17
17
|
.mote-card {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
18
20
|
padding: 1.25rem 1.5rem;
|
|
19
21
|
border: var(--mote-border-width) solid var(--mote-border);
|
|
20
22
|
border-radius: var(--mote-radius-lg);
|
|
@@ -22,11 +24,11 @@
|
|
|
22
24
|
box-shadow: var(--mote-shadow-sm);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
.mote-card > :first-child {
|
|
27
|
+
:where(.mote-card) > :first-child {
|
|
26
28
|
margin-block-start: 0;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
.mote-card > :last-child {
|
|
31
|
+
:where(.mote-card) > :last-child {
|
|
30
32
|
margin-block-end: 0;
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
border-radius: 999px;
|
|
44
46
|
font-size: 0.8em;
|
|
45
47
|
font-weight: 500;
|
|
46
|
-
line-height: 1.
|
|
48
|
+
line-height: 1.4;
|
|
47
49
|
background: var(--mote-status-soft, var(--mote-accent-soft));
|
|
48
50
|
color: var(--mote-status, var(--mote-accent-deep));
|
|
49
51
|
user-select: none;
|
|
@@ -54,8 +56,8 @@
|
|
|
54
56
|
* ------------------------------------------------------------------------- */
|
|
55
57
|
|
|
56
58
|
.mote-avatar {
|
|
57
|
-
width: 2.5rem;
|
|
58
|
-
height: 2.5rem;
|
|
59
|
+
width: var(--mote-avatar, 2.5rem);
|
|
60
|
+
height: var(--mote-avatar, 2.5rem);
|
|
59
61
|
border-radius: 999px;
|
|
60
62
|
object-fit: cover;
|
|
61
63
|
border: var(--mote-border-width) solid var(--mote-border);
|
|
@@ -67,6 +69,8 @@
|
|
|
67
69
|
* ------------------------------------------------------------------------- */
|
|
68
70
|
|
|
69
71
|
.mote-skeleton {
|
|
72
|
+
display: inline-block;
|
|
73
|
+
min-height: 1em;
|
|
70
74
|
border-radius: var(--mote-radius-sm);
|
|
71
75
|
background:
|
|
72
76
|
linear-gradient(
|
|
@@ -122,7 +126,7 @@
|
|
|
122
126
|
border-block-end: var(--mote-border-width) solid var(--mote-border);
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
:where(body > header nav,
|
|
129
|
+
:where(body > header nav, .mote-nav) ul {
|
|
126
130
|
display: flex;
|
|
127
131
|
flex-wrap: wrap;
|
|
128
132
|
gap: 0.25rem;
|
|
@@ -131,7 +135,7 @@
|
|
|
131
135
|
list-style: none;
|
|
132
136
|
}
|
|
133
137
|
|
|
134
|
-
:where(body > header nav,
|
|
138
|
+
:where(body > header nav, .mote-nav) a {
|
|
135
139
|
display: inline-block;
|
|
136
140
|
padding: 0.35rem 0.8rem;
|
|
137
141
|
border-radius: 999px;
|
|
@@ -140,12 +144,12 @@
|
|
|
140
144
|
transition: background var(--mote-transition), color var(--mote-transition);
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
:where(body > header nav,
|
|
147
|
+
:where(body > header nav, .mote-nav) a:hover {
|
|
144
148
|
background: var(--mote-accent-soft);
|
|
145
149
|
color: var(--mote-accent-deep);
|
|
146
150
|
}
|
|
147
151
|
|
|
148
|
-
:where(body > header nav,
|
|
152
|
+
:where(body > header nav, .mote-nav) a[aria-current="page"] {
|
|
149
153
|
background: var(--mote-accent);
|
|
150
154
|
color: var(--mote-on-accent);
|
|
151
155
|
}
|
|
@@ -176,6 +180,43 @@ nav[aria-label="breadcrumb"] [aria-current="page"] {
|
|
|
176
180
|
font-weight: 500;
|
|
177
181
|
}
|
|
178
182
|
|
|
183
|
+
/* Pagination — semantic selector, no class (mirrors breadcrumbs) */
|
|
184
|
+
nav[aria-label="pagination"] ul {
|
|
185
|
+
display: flex;
|
|
186
|
+
gap: 0.25rem;
|
|
187
|
+
padding: 0;
|
|
188
|
+
margin: 0;
|
|
189
|
+
list-style: none;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
nav[aria-label="pagination"] a {
|
|
193
|
+
display: inline-block;
|
|
194
|
+
min-width: 2.2rem;
|
|
195
|
+
padding: 0.35rem 0.6rem;
|
|
196
|
+
border-radius: var(--mote-radius-sm);
|
|
197
|
+
text-align: center;
|
|
198
|
+
text-decoration: none;
|
|
199
|
+
color: var(--mote-fg);
|
|
200
|
+
transition: background var(--mote-transition), color var(--mote-transition);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
nav[aria-label="pagination"] a:hover {
|
|
204
|
+
background: var(--mote-accent-soft);
|
|
205
|
+
color: var(--mote-accent-deep);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
nav[aria-label="pagination"] [aria-current="page"] {
|
|
209
|
+
background: var(--mote-accent);
|
|
210
|
+
color: var(--mote-on-accent);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Page footer: the quiet sibling of the site header */
|
|
214
|
+
:where(body > footer, .mote-footer) {
|
|
215
|
+
margin-block-start: 3rem;
|
|
216
|
+
padding-block: 1.5rem;
|
|
217
|
+
border-block-start: var(--mote-border-width) solid var(--mote-border);
|
|
218
|
+
}
|
|
219
|
+
|
|
179
220
|
/* ---------------------------------------------------------------------------
|
|
180
221
|
* 8. Popover surfaces — tooltips, menus, toasts
|
|
181
222
|
*
|
|
@@ -267,7 +308,9 @@ menu[popover] :is(button, [role="menuitem"]):focus-visible {
|
|
|
267
308
|
* ------------------------------------------------------------------------- */
|
|
268
309
|
|
|
269
310
|
[popover],
|
|
270
|
-
dialog
|
|
311
|
+
dialog {
|
|
312
|
+
opacity: 0;
|
|
313
|
+
transform: translateY(8px) scale(0.98);
|
|
271
314
|
transition:
|
|
272
315
|
opacity 180ms ease,
|
|
273
316
|
transform 180ms ease,
|
|
@@ -328,7 +371,11 @@ details[name][open] summary {
|
|
|
328
371
|
|
|
329
372
|
details[name] > :not(summary) {
|
|
330
373
|
margin-block: 0;
|
|
331
|
-
padding: 0.15rem 1.2rem 0
|
|
374
|
+
padding: 0.15rem 1.2rem 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
details[name] > :not(summary):last-child {
|
|
378
|
+
padding-block-end: 0.9rem;
|
|
332
379
|
}
|
|
333
380
|
|
|
334
381
|
/* ---------------------------------------------------------------------------
|
|
@@ -397,31 +444,71 @@ details[name] > :not(summary) {
|
|
|
397
444
|
grid-template-columns: 1fr;
|
|
398
445
|
}
|
|
399
446
|
|
|
400
|
-
/* On <body>, the middle row stretches the page so the footer pins to
|
|
401
|
-
|
|
447
|
+
/* On <body>, the middle row stretches the page so the footer pins to the
|
|
448
|
+
viewport bottom even when content is short. :where() keeps this
|
|
402
449
|
at base specificity; wrappers (demo boxes, embeds) stay natural-size. */
|
|
403
450
|
:where(body).mote-doc {
|
|
404
451
|
min-height: 100svh;
|
|
452
|
+
padding-inline: 1.5rem;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Anchored content must clear the sticky shell header. Applies only
|
|
456
|
+
when the header actually exists. */
|
|
457
|
+
html:where(:has(body > header)) {
|
|
458
|
+
scroll-padding-block-start: 5rem;
|
|
405
459
|
}
|
|
406
460
|
|
|
407
461
|
.mote-doc > header { grid-row: 1; grid-column: 1 / -1; }
|
|
408
462
|
.mote-doc > footer { grid-row: 3; grid-column: 1 / -1; }
|
|
409
463
|
|
|
410
|
-
.mote-doc > main { grid-row: 2; grid-column: 1; }
|
|
464
|
+
.mote-doc > main { grid-row: 2; grid-column: 1; min-width: 0; }
|
|
411
465
|
|
|
412
466
|
/* nav = left rail */
|
|
413
467
|
.mote-doc > nav {
|
|
414
468
|
grid-row: 2;
|
|
415
469
|
grid-column: 1;
|
|
470
|
+
min-width: 0;
|
|
416
471
|
align-self: start;
|
|
417
472
|
position: sticky;
|
|
418
|
-
top: 1rem;
|
|
473
|
+
top: var(--mote-rail-top, 1rem);
|
|
419
474
|
max-height: calc(100svh - 2rem);
|
|
420
475
|
overflow: auto;
|
|
421
476
|
}
|
|
422
477
|
|
|
478
|
+
/* Rail links: a quiet vertical list, not the horizontal pill row.
|
|
479
|
+
--mote-rail-top should clear the shell header: 1rem by default;
|
|
480
|
+
set it on :root when a sticky body > header is taller, e.g.
|
|
481
|
+
--mote-rail-top: 4.75rem. */
|
|
482
|
+
.mote-doc > nav ul {
|
|
483
|
+
display: flex;
|
|
484
|
+
flex-direction: column;
|
|
485
|
+
gap: 0.15rem;
|
|
486
|
+
padding: 0;
|
|
487
|
+
margin: 0;
|
|
488
|
+
list-style: none;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.mote-doc > nav a {
|
|
492
|
+
display: block;
|
|
493
|
+
padding: 0.35rem 0.75rem;
|
|
494
|
+
border-radius: var(--mote-radius-sm);
|
|
495
|
+
text-decoration: none;
|
|
496
|
+
color: var(--mote-fg);
|
|
497
|
+
transition: background var(--mote-transition), color var(--mote-transition);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.mote-doc > nav a:hover {
|
|
501
|
+
background: var(--mote-accent-soft);
|
|
502
|
+
color: var(--mote-accent-deep);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.mote-doc > nav a[aria-current="page"] {
|
|
506
|
+
background: var(--mote-accent);
|
|
507
|
+
color: var(--mote-on-accent);
|
|
508
|
+
}
|
|
509
|
+
|
|
423
510
|
.mote-doc:where(:has(> nav)) {
|
|
424
|
-
grid-template-columns: minmax(12rem, 16rem) 1fr;
|
|
511
|
+
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
|
|
425
512
|
}
|
|
426
513
|
|
|
427
514
|
.mote-doc:where(:has(> nav)) > main {
|
|
@@ -432,6 +519,7 @@ details[name] > :not(summary) {
|
|
|
432
519
|
.mote-doc > aside {
|
|
433
520
|
grid-row: 2;
|
|
434
521
|
grid-column: 2;
|
|
522
|
+
min-width: 0;
|
|
435
523
|
align-self: start;
|
|
436
524
|
}
|
|
437
525
|
|
|
@@ -440,11 +528,11 @@ details[name] > :not(summary) {
|
|
|
440
528
|
}
|
|
441
529
|
|
|
442
530
|
.mote-doc:where(:has(> aside)) {
|
|
443
|
-
grid-template-columns: 1fr minmax(10rem, 14rem);
|
|
531
|
+
grid-template-columns: minmax(0, 1fr) minmax(10rem, 14rem);
|
|
444
532
|
}
|
|
445
533
|
|
|
446
534
|
.mote-doc:where(:has(> nav)):where(:has(> aside)) {
|
|
447
|
-
grid-template-columns: minmax(12rem, 16rem) 1fr minmax(10rem, 14rem);
|
|
535
|
+
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr) minmax(10rem, 14rem);
|
|
448
536
|
}
|
|
449
537
|
|
|
450
538
|
@media (width < 50rem) {
|
|
@@ -456,6 +544,7 @@ details[name] > :not(summary) {
|
|
|
456
544
|
|
|
457
545
|
.mote-doc > :is(nav, aside, main) {
|
|
458
546
|
grid-column: 1 / -1;
|
|
547
|
+
grid-row: auto;
|
|
459
548
|
}
|
|
460
549
|
|
|
461
550
|
.mote-doc > nav {
|
|
@@ -470,5 +559,7 @@ details[name] > :not(summary) {
|
|
|
470
559
|
.mote-grid {
|
|
471
560
|
display: grid;
|
|
472
561
|
gap: 1.25rem;
|
|
473
|
-
|
|
562
|
+
/* Density is a variable, not a class API: --mote-grid-min: 22rem
|
|
563
|
+
gives 3-wide pricing; 26rem gives 2-column content. */
|
|
564
|
+
grid-template-columns: repeat(auto-fill, minmax(min(var(--mote-grid-min, 16rem), 100%), 1fr));
|
|
474
565
|
}
|
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.5.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:
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"Liberation Mono", monospace;
|
|
45
45
|
--mote-line-height: 1.65;
|
|
46
46
|
--mote-tracking: -0.011em;
|
|
47
|
+
/* Display scale — opt-in, for heroes and error pages; body scale untouched */
|
|
48
|
+
--mote-display: clamp(2.75rem, 1.5rem + 4vw, 5rem);
|
|
47
49
|
|
|
48
50
|
/* Shape & depth */
|
|
49
51
|
--mote-radius: 10px;
|
|
@@ -84,6 +86,10 @@
|
|
|
84
86
|
--mote-error: #d98a8a;
|
|
85
87
|
--mote-info: #96b1d1;
|
|
86
88
|
|
|
89
|
+
/* "Deep" is the emphasis direction per theme: darker on light
|
|
90
|
+
surfaces, brighter on dark ones. Never darken in dark mode. */
|
|
91
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), white 14%);
|
|
92
|
+
|
|
87
93
|
--mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
88
94
|
--mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
89
95
|
--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 +111,8 @@
|
|
|
105
111
|
--mote-error: #d98a8a;
|
|
106
112
|
--mote-info: #96b1d1;
|
|
107
113
|
|
|
114
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), white 14%);
|
|
115
|
+
|
|
108
116
|
--mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
109
117
|
--mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
110
118
|
--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 +253,10 @@ blockquote {
|
|
|
245
253
|
font-style: italic;
|
|
246
254
|
}
|
|
247
255
|
|
|
256
|
+
blockquote :where(code, samp, kbd, var) {
|
|
257
|
+
font-style: normal;
|
|
258
|
+
}
|
|
259
|
+
|
|
248
260
|
blockquote > cite {
|
|
249
261
|
display: block;
|
|
250
262
|
margin-block-start: 0.5rem;
|
|
@@ -403,7 +415,7 @@ input, select, textarea {
|
|
|
403
415
|
background: var(--mote-surface);
|
|
404
416
|
border: var(--mote-border-width) solid var(--mote-border);
|
|
405
417
|
border-radius: var(--mote-radius-sm);
|
|
406
|
-
transition: border-color var(--mote-transition)
|
|
418
|
+
transition: border-color var(--mote-transition);
|
|
407
419
|
}
|
|
408
420
|
|
|
409
421
|
input:hover, select:hover, textarea:hover {
|
|
@@ -428,6 +440,13 @@ select {
|
|
|
428
440
|
background-position: right 0.8rem center;
|
|
429
441
|
}
|
|
430
442
|
|
|
443
|
+
/* Multi-selects are listboxes, not dropdowns: restore native chrome */
|
|
444
|
+
select[multiple] {
|
|
445
|
+
appearance: auto;
|
|
446
|
+
padding: 0.5rem;
|
|
447
|
+
background-image: none;
|
|
448
|
+
}
|
|
449
|
+
|
|
431
450
|
[type="checkbox"], [type="radio"] {
|
|
432
451
|
width: 1em;
|
|
433
452
|
height: 1em;
|
|
@@ -447,6 +466,14 @@ select {
|
|
|
447
466
|
border-style: dashed;
|
|
448
467
|
}
|
|
449
468
|
|
|
469
|
+
/* Range inputs are sliders, not text fields: strip text chrome */
|
|
470
|
+
[type="range"] {
|
|
471
|
+
padding: 0;
|
|
472
|
+
border: none;
|
|
473
|
+
background: none;
|
|
474
|
+
accent-color: var(--mote-status, var(--mote-accent));
|
|
475
|
+
}
|
|
476
|
+
|
|
450
477
|
::placeholder {
|
|
451
478
|
color: var(--mote-muted);
|
|
452
479
|
opacity: 0.8;
|
|
@@ -457,7 +484,7 @@ select {
|
|
|
457
484
|
cursor: not-allowed;
|
|
458
485
|
}
|
|
459
486
|
|
|
460
|
-
:user-invalid {
|
|
487
|
+
:is(input, select, textarea):user-invalid {
|
|
461
488
|
border-color: var(--mote-error);
|
|
462
489
|
}
|
|
463
490
|
|
|
@@ -471,14 +498,55 @@ button, [type="button"], [type="submit"], [type="reset"] {
|
|
|
471
498
|
border-radius: var(--mote-radius-sm);
|
|
472
499
|
cursor: pointer;
|
|
473
500
|
transition: background var(--mote-transition), border-color var(--mote-transition),
|
|
474
|
-
|
|
501
|
+
transform var(--mote-transition);
|
|
475
502
|
}
|
|
476
503
|
|
|
477
504
|
button:hover, [type="button"]:hover, [type="submit"]:hover, [type="reset"]:hover {
|
|
478
|
-
|
|
505
|
+
/* Theme-aware deepening: fg is dark in light themes, light in dark ones. */
|
|
506
|
+
background: color-mix(in oklab, var(--mote-accent), var(--mote-fg) 14%);
|
|
479
507
|
box-shadow: var(--mote-shadow-sm);
|
|
480
508
|
}
|
|
481
509
|
|
|
510
|
+
/* Semantic solids are gated: the status class must sit on the button
|
|
511
|
+
itself, so a themed section (body.mote-error, a note, a toast) never
|
|
512
|
+
recolors the actions inside it. The token read below is therefore
|
|
513
|
+
the button's own, never an inherited one. */
|
|
514
|
+
:is(button, [type="button"], [type="submit"], [type="reset"]):where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted):where(:not(.mote-ghost)) {
|
|
515
|
+
background: var(--mote-status);
|
|
516
|
+
color: var(--mote-on-status, var(--mote-bg));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
:is(button, [type="button"], [type="submit"], [type="reset"]):where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted):where(:not(.mote-ghost)):hover {
|
|
520
|
+
background: color-mix(in oklab, var(--mote-status), var(--mote-fg) 14%);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/* Ghost buttons: quiet, transparent fill, accent-tinted. Like solids,
|
|
524
|
+
status colors come from the button's own class, never from context. */
|
|
525
|
+
.mote-ghost {
|
|
526
|
+
background: none;
|
|
527
|
+
border: var(--mote-border-width) solid color-mix(in oklab, var(--mote-accent), transparent 55%);
|
|
528
|
+
color: var(--mote-accent-deep);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.mote-ghost:hover {
|
|
532
|
+
background: var(--mote-accent-soft);
|
|
533
|
+
color: var(--mote-accent-deep);
|
|
534
|
+
box-shadow: none;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* Gated ghost+status: the status class on the element itself tints the
|
|
538
|
+
border and text (same gating as the solid buttons). Works on buttons
|
|
539
|
+
and anchors alike — .mote-ghost is element-agnostic. */
|
|
540
|
+
.mote-ghost:where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted) {
|
|
541
|
+
border-color: color-mix(in oklab, var(--mote-status), transparent 55%);
|
|
542
|
+
color: var(--mote-status);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.mote-ghost:where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted):hover {
|
|
546
|
+
background: var(--mote-status-soft);
|
|
547
|
+
color: var(--mote-status);
|
|
548
|
+
}
|
|
549
|
+
|
|
482
550
|
button:active, [type="button"]:active, [type="submit"]:active, [type="reset"]:active {
|
|
483
551
|
transform: translateY(1px);
|
|
484
552
|
}
|
|
@@ -537,6 +605,7 @@ dialog {
|
|
|
537
605
|
background: var(--mote-bg);
|
|
538
606
|
color: var(--mote-fg);
|
|
539
607
|
box-shadow: var(--mote-shadow);
|
|
608
|
+
max-width: min(90vw, 44rem);
|
|
540
609
|
}
|
|
541
610
|
|
|
542
611
|
dialog::backdrop {
|
|
@@ -550,8 +619,8 @@ progress, meter {
|
|
|
550
619
|
}
|
|
551
620
|
|
|
552
621
|
/* Semantic feedback helpers (pure HTML, works with <p>, <div>, anything) */
|
|
553
|
-
[role="alert"] { color: var(--mote-error); }
|
|
554
|
-
[role="status"] { color: var(--mote-info); }
|
|
622
|
+
[role="alert"] { color: var(--mote-status, var(--mote-error)); }
|
|
623
|
+
[role="status"] { color: var(--mote-status, var(--mote-info)); }
|
|
555
624
|
|
|
556
625
|
/* ---------------------------------------------------------------------------
|
|
557
626
|
* 11. Opt-in helpers
|
|
@@ -568,8 +637,49 @@ progress, meter {
|
|
|
568
637
|
/* Dimmed text: <small class="mote-muted"> */
|
|
569
638
|
.mote-muted { color: var(--mote-muted); }
|
|
570
639
|
|
|
640
|
+
/* Row of things: headers, footers, toolbars, chip lists.
|
|
641
|
+
.mote-push on a child right-aligns it (and everything after). */
|
|
642
|
+
.mote-row {
|
|
643
|
+
display: flex;
|
|
644
|
+
align-items: center;
|
|
645
|
+
gap: 1rem;
|
|
646
|
+
flex-wrap: wrap;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.mote-push { margin-inline-start: auto; }
|
|
650
|
+
|
|
651
|
+
/* Visually hidden, screen-reader available */
|
|
652
|
+
.mote-sr-only {
|
|
653
|
+
position: absolute;
|
|
654
|
+
width: 1px;
|
|
655
|
+
height: 1px;
|
|
656
|
+
padding: 0;
|
|
657
|
+
margin: -1px;
|
|
658
|
+
overflow: hidden;
|
|
659
|
+
clip-path: inset(50%);
|
|
660
|
+
white-space: nowrap;
|
|
661
|
+
border: 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* Skip link: sr-only until focused, then a fixed pill */
|
|
665
|
+
.mote-sr-only:focus-visible {
|
|
666
|
+
position: fixed;
|
|
667
|
+
top: 1rem;
|
|
668
|
+
left: 1rem;
|
|
669
|
+
width: auto;
|
|
670
|
+
height: auto;
|
|
671
|
+
margin: 0;
|
|
672
|
+
padding: 0.5rem 1rem;
|
|
673
|
+
clip-path: none;
|
|
674
|
+
z-index: 20;
|
|
675
|
+
background: var(--mote-accent);
|
|
676
|
+
color: var(--mote-on-accent);
|
|
677
|
+
border-radius: var(--mote-radius-sm);
|
|
678
|
+
text-decoration: none;
|
|
679
|
+
}
|
|
680
|
+
|
|
571
681
|
/* ---------------------------------------------------------------------------
|
|
572
|
-
*
|
|
682
|
+
* 12. Status classes
|
|
573
683
|
*
|
|
574
684
|
* Statuses are pure token assignments — they set variables, never visual
|
|
575
685
|
* properties. Base classes (notes, badges, toasts) consume the tokens and
|
|
@@ -582,10 +692,10 @@ progress, meter {
|
|
|
582
692
|
* inline: style="--mote-status: rebeccapurple".
|
|
583
693
|
* ------------------------------------------------------------------------- */
|
|
584
694
|
|
|
585
|
-
.mote-success { --mote-status: var(--mote-success); --mote-status-soft: var(--mote-success-soft); }
|
|
586
|
-
.mote-warning { --mote-status: var(--mote-warning); --mote-status-soft: var(--mote-warning-soft); }
|
|
587
|
-
.mote-error { --mote-status: var(--mote-error); --mote-status-soft: var(--mote-error-soft); }
|
|
588
|
-
.mote-info { --mote-status: var(--mote-info); --mote-status-soft: var(--mote-info-soft); }
|
|
695
|
+
.mote-success { --mote-status: var(--mote-success); --mote-status-soft: var(--mote-success-soft); --mote-on-status: var(--mote-bg); }
|
|
696
|
+
.mote-warning { --mote-status: var(--mote-warning); --mote-status-soft: var(--mote-warning-soft); --mote-on-status: var(--mote-bg); }
|
|
697
|
+
.mote-error { --mote-status: var(--mote-error); --mote-status-soft: var(--mote-error-soft); --mote-on-status: var(--mote-bg); }
|
|
698
|
+
.mote-info { --mote-status: var(--mote-info); --mote-status-soft: var(--mote-info-soft); --mote-on-status: var(--mote-bg); }
|
|
589
699
|
|
|
590
700
|
/* .mote-muted does double duty: dimmed text where nothing consumes the
|
|
591
701
|
tokens, and a neutral status where something does. */
|
|
@@ -593,18 +703,47 @@ progress, meter {
|
|
|
593
703
|
color: var(--mote-muted);
|
|
594
704
|
--mote-status: var(--mote-muted);
|
|
595
705
|
--mote-status-soft: color-mix(in oklab, var(--mote-fg) 7%, transparent);
|
|
706
|
+
--mote-on-status: var(--mote-bg);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/* .mote-default: the accent as an explicit status — "keep this (and its
|
|
710
|
+
descendants) plum" inside a themed section. Unlike the semantics, this
|
|
711
|
+
IS meant to be applied contextually. */
|
|
712
|
+
.mote-default {
|
|
713
|
+
--mote-status: var(--mote-accent);
|
|
714
|
+
--mote-status-soft: var(--mote-accent-soft);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/* .mote-reset: clear an inherited status for a subtree — consumers inside
|
|
718
|
+
fall back to their own defaults; their own classes re-apply locally.
|
|
719
|
+
Where .mote-default forces the accent down the tree, .mote-reset
|
|
720
|
+
removes the theme and lets each element speak for itself. */
|
|
721
|
+
.mote-reset {
|
|
722
|
+
--mote-status: initial;
|
|
723
|
+
--mote-status-soft: initial;
|
|
724
|
+
--mote-on-status: initial;
|
|
596
725
|
}
|
|
597
726
|
|
|
598
727
|
/* Themed message surfaces — base shape only; status via the token classes */
|
|
599
728
|
.mote-note {
|
|
729
|
+
margin-block: 1rem;
|
|
600
730
|
padding: 0.8rem 1.1rem;
|
|
601
731
|
border-radius: var(--mote-radius-sm);
|
|
602
732
|
color: var(--mote-status, var(--mote-accent-deep));
|
|
603
733
|
background: var(--mote-status-soft, var(--mote-accent-soft));
|
|
604
734
|
}
|
|
605
735
|
|
|
736
|
+
/* Display text: opt-in hero / error-code scale. Widen per use with
|
|
737
|
+
style="--mote-display: clamp(6rem, 24vw, 12rem)". */
|
|
738
|
+
.mote-display {
|
|
739
|
+
font-size: var(--mote-display);
|
|
740
|
+
line-height: 1.1;
|
|
741
|
+
letter-spacing: var(--mote-tracking);
|
|
742
|
+
text-wrap: balance;
|
|
743
|
+
}
|
|
744
|
+
|
|
606
745
|
/* ---------------------------------------------------------------------------
|
|
607
|
-
*
|
|
746
|
+
* 13. Motion preferences & print
|
|
608
747
|
* ------------------------------------------------------------------------- */
|
|
609
748
|
|
|
610
749
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -617,6 +756,26 @@ progress, meter {
|
|
|
617
756
|
}
|
|
618
757
|
|
|
619
758
|
@media print {
|
|
759
|
+
/* Force the light palette on paper — dark fills (zebra, notes,
|
|
760
|
+
open details) must not print as near-black bands. Placed last:
|
|
761
|
+
source order ties the dark token blocks' specificity. */
|
|
762
|
+
:root,
|
|
763
|
+
:root[data-theme="dark"],
|
|
764
|
+
:root:not([data-theme="light"]) {
|
|
765
|
+
color-scheme: light;
|
|
766
|
+
--mote-bg: #faf9f7;
|
|
767
|
+
--mote-surface: #f1eee7;
|
|
768
|
+
--mote-fg: #2b2924;
|
|
769
|
+
--mote-muted: #716a60;
|
|
770
|
+
--mote-border: #e6e0d4;
|
|
771
|
+
--mote-accent: #6f5486;
|
|
772
|
+
--mote-accent-deep: color-mix(in oklab, var(--mote-accent), black 14%);
|
|
773
|
+
--mote-success: #4e7d51;
|
|
774
|
+
--mote-warning: #8a6819;
|
|
775
|
+
--mote-error: #b34d4d;
|
|
776
|
+
--mote-info: #527097;
|
|
777
|
+
}
|
|
778
|
+
|
|
620
779
|
body {
|
|
621
780
|
background: #fff;
|
|
622
781
|
color: #000;
|