mote.css 0.5.0 → 0.6.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 CHANGED
@@ -5,6 +5,89 @@ 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.6.0] - 2026-10-20
9
+
10
+ ### Fixed
11
+
12
+ - Pagination `<span>` current page: the pill box (display, min-width,
13
+ padding) now targets `[aria-current="page"]` as well as `<a>` — the
14
+ docs-recommended span form renders correctly (was a 6.5px sliver).
15
+ - Pagination: `flex-wrap: wrap` on the link row — 10 pills at 390px
16
+ no longer scroll the page horizontally.
17
+ - Anchor ghosts: `.mote-ghost` now carries its own padding, border-radius,
18
+ text-decoration:none and cursor:pointer — `<a class="mote-ghost">`
19
+ renders as a proper pill (was an underlined unpadded rectangle).
20
+ - Badge links: `a.mote-badge` no longer underlines; `user-select: none`
21
+ dropped from badges so version numbers and tags can be copied.
22
+ - `.mote-display` reads `--mote-status` — themed pages (error, warning)
23
+ tint their giant display text automatically.
24
+ - Default `--mote-rail-top` is now 4.75rem when `body > header` exists
25
+ (the sticky rail clears the sticky header without manual overrides).
26
+ - `.mote-doc > main` gets `overflow-x: clip` — wide tables truly scroll
27
+ inside main, never the page.
28
+ - Sticky header: default `padding-block: 0.7rem` — content no longer hugs
29
+ the border.
30
+ - `.mote-row` zeroes heading margins inside the row — version headers
31
+ and card titles no longer balloon the flex context.
32
+ - Checkbox/radio `width: 1em` hardened against page-level
33
+ `width: 100%` rules.
34
+
35
+ ### Added
36
+
37
+ - `.mote-btn`: solid button recipe on any element (anchors, spans) —
38
+ the full solid chrome including gated status classes; hero CTAs and
39
+ pricing links no longer need page CSS.
40
+ - `.mote-media`: media object (avatar/icon beside body) — flex row,
41
+ last child grows; forum posts, comments, chat.
42
+ - `nav[aria-label="toc"]`: semantic table-of-contents pattern (mirrors
43
+ breadcrumbs and pagination) — right-rail asides, "On this page" nav.
44
+ - `.mote-no-print` / `.mote-print-only`: opt-in print visibility helpers.
45
+ - `aria-invalid="true"` styled alongside `:user-invalid` — programmatic
46
+ validation states (server-side, failed-save) without waiting for user
47
+ interaction.
48
+ - Rail headings: `.mote-doc > nav :is(h2, h3)` at label scale —
49
+ grouped sidebar navigation without page CSS.
50
+ - `.mote-card` reads `--mote-status` for its border — featured/emphasized
51
+ cards from one status class.
52
+ - Docs recipes: card flush-media, numeric table columns with sticky
53
+ headers, sparse grids (auto-fit), canonical search form.
54
+ - `.mote-stack` opt-in class: stretches form fields to 100% while
55
+ keeping checkboxes/radios at native 1em — no page CSS needed.
56
+ - `--mote-container-max` token (default 70rem): narrow the reading
57
+ measure for long-form pages without new classes.
58
+ - Spinner sized in em (1.25em, 0.15em border) — scales with context
59
+ (buttons, table rows, loading text).
60
+ - Docs: display text section with error-code example and status tinting;
61
+ `--mote-avatar` sizing variable documented; `--mote-grid-min` density
62
+ variable documented; `[role="alert"]`/`[role="status"]` semantic
63
+ feedback helpers documented; safe stacked-form recipe and `.mote-stack`;
64
+ Chromium theme-toggle transition workaround.
65
+
66
+ ### Changed
67
+
68
+ - Second ten-app evaluation completed against v0.5.1; all round-1 fixes
69
+ verified holding. Round-2 findings addressed in this release.
70
+
71
+ ### Fixed
72
+
73
+ - Pagination: the current page (`aria-current`) kept its pill state on
74
+ hover — previously the generic link hover overrode the accent pill,
75
+ making "you are here" indistinguishable from hovering any other page.
76
+ The current page now deepens on hover (same accent-mix pattern as solid
77
+ buttons); non-current links keep the soft-tint hover. Docs recommend a
78
+ `<span>` for the current page (you are already there — not a link).
79
+
80
+ ### Added
81
+
82
+ - Logo: a cinematic photograph of a single dust mote floating in warm
83
+ golden sunbeams (docs/img/mote-logo.jpg), integrated into the docs hero.
84
+ - Repositioned from "classless" to "class-optional" throughout — more
85
+ honest now that the library ships ~25 opt-in classes for statuses,
86
+ components, helpers, and layout. The core promise is unchanged:
87
+ zero classes required, semantic elements are the default.
88
+ - Pagination documentation: code sample and live example in the Layout
89
+ section, alongside the breadcrumbs it mirrors.
90
+
8
91
  ## [0.5.0] - 2026-10-20
9
92
 
10
93
  ### Added
@@ -252,6 +335,8 @@ Components:
252
335
  - Element gallery demo page (`demo.html`).
253
336
 
254
337
  [0.3.0]: https://gitlab.com/abmach/mote/compare/v0.2.1...v0.3.0
338
+ [0.6.0]: https://gitlab.com/abmach/mote/compare/v0.5.1...v0.6.0
339
+ [0.5.1]: https://gitlab.com/abmach/mote/compare/v0.5.0...v0.5.1
255
340
  [0.5.0]: https://gitlab.com/abmach/mote/compare/v0.4.0...v0.5.0
256
341
  [0.4.0]: https://gitlab.com/abmach/mote/compare/v0.3.0...v0.4.0
257
342
  [0.3.0]: https://gitlab.com/abmach/mote/compare/v0.2.1...v0.3.0
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # mote.css
2
2
 
3
- > The smallest thing that can carry style — a tiny, semantic, classless CSS layer for plain HTML.
3
+ > The smallest thing that can carry style — a tiny, semantic, class-optional CSS layer for plain HTML.
4
4
 
5
- A mote of styling for plain HTML: **zero dependencies, zero build step, zero classes required**. Write semantic HTML, drop in one file, and it looks like a place you'd want to stay a while — modern, warm, unobtrusive. Inspired by the quiet excellence of [oat](https://github.com/knadh/oat), with its own cozy voice.
5
+ A mote of styling for plain HTML: **zero dependencies, zero build step, zero classes required** — but opt-in classes exist for statuses, components, and layout when you want them. Write semantic HTML, drop in one file, and it looks like a place you'd want to stay a while — modern, warm, unobtrusive. Inspired by the quiet excellence of [oat](https://github.com/knadh/oat), with its own cozy voice.
6
6
 
7
7
  Source on [GitLab](https://gitlab.com/abmach/mote) · mirrored on [GitHub](https://github.com/abmach/mote).
8
8
 
@@ -23,7 +23,7 @@ Source on [GitLab](https://gitlab.com/abmach/mote) · mirrored on [GitHub](https
23
23
 
24
24
  ## Principles
25
25
 
26
- - **Semantic HTML is the API.** mote styles elements by what they *are* — headings, forms, `dialog`, `menu` — not by the classes they wear. Classes exist only where the platform has no semantic carrier (a "card" is not a semantic concept).
26
+ - **Semantic HTML is the API.** mote styles elements by what they *are* — headings, forms, `dialog`, `menu` — not by the classes they wear. Zero classes are required; optional classes exist as opt-in conveniences for statuses, components, and helpers.
27
27
  - **The platform before the library.** Interactive components ride on native features: `popover`, `dialog`, `<details name>`, `:user-invalid`. Where the browser can do it, mote never ships JavaScript to do it worse.
28
28
  - **Tokens all the way down.** Every color, radius, shadow, font and status is a `--mote-*` custom property, and derived tones are computed with `color-mix()`. Status classes set tokens and nothing else — one override re-skins every note, badge and toast. Re-theme by overriding variables, never by fighting selectors.
29
29
  - **Weightless by default.** `mote.css` styles your elements; `mote.components.css` is opt-in and CSS-only. You ship zero bytes of library JavaScript, ever.
@@ -96,6 +96,10 @@ The consequence: theming is a handful of variable overrides, and an inline tweak
96
96
  | `--mote-radius` | `10px` | | Default corner softness |
97
97
  | `--mote-radius-sm` | `6px` | | Controls, inline code |
98
98
  | `--mote-radius-lg` | `16px` | | Dialogs |
99
+ | `--mote-container-max` | `70rem` | | Page wrapper max width (narrow for reading) |
100
+ | `--mote-display` | `clamp(2.75rem, 1.5rem + 4vw, 5rem)` | | Display text scale (opt-in) |
101
+ | `--mote-grid-min` | `16rem` | | Card grid minimum track width |
102
+ | `--mote-avatar` | `2.5rem` | | Avatar size |
99
103
  | `--mote-shadow` / `--mote-shadow-sm` | warm diffuse | | Elevation |
100
104
  | `--mote-transition` | `150ms ease` | | Hover/focus timing |
101
105
 
@@ -125,7 +129,7 @@ Or self-host with `npm install @fontsource-variable/inter @fontsource/commit-mon
125
129
 
126
130
  ## Opt-in helpers
127
131
 
128
- mote is classless by design; these are the only escape hatches, plus status classes that every status-consuming component reads:
132
+ mote is class-optional by design — elements are styled from tags alone; these classes are opt-in conveniences, never a required API:
129
133
 
130
134
  | Class | Purpose |
131
135
  |---|---|
@@ -141,14 +145,18 @@ mote is classless by design; these are the only escape hatches, plus status clas
141
145
  | Component | Hook |
142
146
  |---|---|
143
147
  | 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>` |
148
+ | Sticky site header / nav pills | `body > header` — semantic; `.mote-header`/`.mote-nav` only for shells wrapped in a `<div>` |
145
149
  | Semantic buttons (gated) | `class="mote-success"` etc. on the button itself — themed sections never recolor actions |
146
150
  | Ghost buttons | `.mote-ghost` — transparent fill; status tints from the button's own class (gated like solids) |
151
+ | Solid button on any element | `.mote-btn` — the full solid recipe for anchors, spans; status classes gated |
152
+ | Media objects | `.mote-media` — avatar/icon beside body (flex, last child grows) |
147
153
  | Display text | `.mote-display` — opt-in hero/error-code scale via `--mote-display` |
148
154
  | Content rows | `.mote-row` + `.mote-push` — flex row, right-align helper |
149
155
  | Pagination | `nav[aria-label="pagination"]` — semantic, no class |
150
- | Footer baseline | `body > footer` — classless top-border + padding |
156
+ | Table of contents | `nav[aria-label="toc"]` — semantic, no class |
157
+ | Footer baseline | `body > footer` — semantic top-border + padding |
151
158
  | Screen-reader helper | `.mote-sr-only` (doubles as a skip link when focused) |
159
+ | Print helpers | `.mote-no-print` / `.mote-print-only` |
152
160
  | Status inheritance control | `.mote-default` (force accent) · `.mote-reset` (clear inherited status) |
153
161
  | Breadcrumbs | `nav[aria-label="breadcrumb"]` — semantic, no class |
154
162
  | Card grids | `.mote-grid` — auto-fill columns from the container, no breakpoints |
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * mote.components.css — opt-in components for mote.css.
3
- * v0.5.0 · MIT
3
+ * v0.6.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).
@@ -19,6 +19,7 @@
19
19
  flex-direction: column;
20
20
  padding: 1.25rem 1.5rem;
21
21
  border: var(--mote-border-width) solid var(--mote-border);
22
+ border-color: color-mix(in oklab, var(--mote-status, var(--mote-border)), var(--mote-border) 60%);
22
23
  border-radius: var(--mote-radius-lg);
23
24
  background: color-mix(in oklab, var(--mote-surface) 55%, var(--mote-bg));
24
25
  box-shadow: var(--mote-shadow-sm);
@@ -48,7 +49,11 @@
48
49
  line-height: 1.4;
49
50
  background: var(--mote-status-soft, var(--mote-accent-soft));
50
51
  color: var(--mote-status, var(--mote-accent-deep));
51
- user-select: none;
52
+ }
53
+
54
+ /* Badges as links (tag chips, version pills): kill the underline */
55
+ a:where(.mote-badge) {
56
+ text-decoration: none;
52
57
  }
53
58
 
54
59
  /* ---------------------------------------------------------------------------
@@ -95,9 +100,10 @@
95
100
  .mote-spinner {
96
101
  display: inline-block;
97
102
  vertical-align: -0.25em;
98
- width: 1.25rem;
99
- height: 1.25rem;
100
- border: 2px solid color-mix(in oklab, var(--mote-status, var(--mote-accent)), transparent 65%);
103
+ /* em sizing: scales with context (buttons, table rows, loading text) */
104
+ width: 1.25em;
105
+ height: 1.25em;
106
+ border: 0.15em solid color-mix(in oklab, var(--mote-status, var(--mote-accent)), transparent 65%);
101
107
  border-block-start-color: var(--mote-status, var(--mote-accent));
102
108
  border-radius: 999px;
103
109
  animation: mote-spin 0.7s linear infinite;
@@ -110,7 +116,7 @@
110
116
  /* ---------------------------------------------------------------------------
111
117
  * 6. Site header and nav
112
118
  *
113
- * The page shell is styled classlessly: a header that is a direct
119
+ * The page shell is styled semantically: a header that is a direct
114
120
  * child of <body> is the site bar, its nav the pill row. The classes
115
121
  * exist for the one case landmarks cannot express — a shell wrapped
116
122
  * in a <div> (common with frameworks), where body > header cannot
@@ -124,6 +130,7 @@
124
130
  background: color-mix(in oklab, var(--mote-bg) 82%, transparent);
125
131
  backdrop-filter: blur(10px);
126
132
  border-block-end: var(--mote-border-width) solid var(--mote-border);
133
+ padding-block: 0.7rem;
127
134
  }
128
135
 
129
136
  :where(body > header nav, .mote-nav) ul {
@@ -183,13 +190,14 @@ nav[aria-label="breadcrumb"] [aria-current="page"] {
183
190
  /* Pagination — semantic selector, no class (mirrors breadcrumbs) */
184
191
  nav[aria-label="pagination"] ul {
185
192
  display: flex;
193
+ flex-wrap: wrap;
186
194
  gap: 0.25rem;
187
195
  padding: 0;
188
196
  margin: 0;
189
197
  list-style: none;
190
198
  }
191
199
 
192
- nav[aria-label="pagination"] a {
200
+ nav[aria-label="pagination"] :is(a, [aria-current="page"]) {
193
201
  display: inline-block;
194
202
  min-width: 2.2rem;
195
203
  padding: 0.35rem 0.6rem;
@@ -200,7 +208,7 @@ nav[aria-label="pagination"] a {
200
208
  transition: background var(--mote-transition), color var(--mote-transition);
201
209
  }
202
210
 
203
- nav[aria-label="pagination"] a:hover {
211
+ nav[aria-label="pagination"] a:hover:not([aria-current]) {
204
212
  background: var(--mote-accent-soft);
205
213
  color: var(--mote-accent-deep);
206
214
  }
@@ -210,6 +218,33 @@ nav[aria-label="pagination"] [aria-current="page"] {
210
218
  color: var(--mote-on-accent);
211
219
  }
212
220
 
221
+ nav[aria-label="pagination"] [aria-current="page"]:hover {
222
+ background: color-mix(in oklab, var(--mote-accent), var(--mote-fg) 14%);
223
+ color: var(--mote-on-accent);
224
+ }
225
+
226
+ /* Table of contents — semantic selector (mirrors breadcrumbs/pagination) */
227
+ nav[aria-label="toc"] ul {
228
+ padding-inline-start: 0.8rem;
229
+ margin: 0;
230
+ list-style: none;
231
+ font-size: 0.9em;
232
+ }
233
+
234
+ nav[aria-label="toc"] li {
235
+ margin-block: 0.2rem;
236
+ }
237
+
238
+ nav[aria-label="toc"] a {
239
+ color: var(--mote-muted);
240
+ text-decoration: none;
241
+ transition: color var(--mote-transition);
242
+ }
243
+
244
+ nav[aria-label="toc"] a:hover {
245
+ color: var(--mote-accent-deep);
246
+ }
247
+
213
248
  /* Page footer: the quiet sibling of the site header */
214
249
  :where(body > footer, .mote-footer) {
215
250
  margin-block-start: 3rem;
@@ -456,6 +491,7 @@ details[name] > :not(summary):last-child {
456
491
  when the header actually exists. */
457
492
  html:where(:has(body > header)) {
458
493
  scroll-padding-block-start: 5rem;
494
+ --mote-rail-top: 4.75rem;
459
495
  }
460
496
 
461
497
  .mote-doc > header { grid-row: 1; grid-column: 1 / -1; }
@@ -463,6 +499,12 @@ html:where(:has(body > header)) {
463
499
 
464
500
  .mote-doc > main { grid-row: 2; grid-column: 1; min-width: 0; }
465
501
 
502
+ /* Content stays inside the grid track — wide tables scroll here,
503
+ not the page. */
504
+ .mote-doc > main {
505
+ overflow-x: clip;
506
+ }
507
+
466
508
  /* nav = left rail */
467
509
  .mote-doc > nav {
468
510
  grid-row: 2;
@@ -507,6 +549,17 @@ html:where(:has(body > header)) {
507
549
  color: var(--mote-on-accent);
508
550
  }
509
551
 
552
+ /* Rail headings: small label-scale for grouped navigation */
553
+ .mote-doc > nav :is(h2, h3) {
554
+ margin: 1rem 0 0.3rem;
555
+ padding-inline: 0.75rem;
556
+ font-size: 0.72rem;
557
+ font-weight: 600;
558
+ text-transform: uppercase;
559
+ letter-spacing: 0.05em;
560
+ color: var(--mote-muted);
561
+ }
562
+
510
563
  .mote-doc:where(:has(> nav)) {
511
564
  grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
512
565
  }
package/mote.css CHANGED
@@ -1,10 +1,10 @@
1
1
  /*!
2
2
  * mote.css — a mote of styling for plain HTML.
3
- * v0.5.0 · MIT
3
+ * v0.6.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:
7
- * framework-agnostic, semantic, classless, zero dependencies.
7
+ * framework-agnostic, semantic, class-optional, zero dependencies.
8
8
  *
9
9
  * MIT License. Fonts (Inter, Commit Mono) are NOT bundled.
10
10
  * See the README for one-line self-hosting instructions.
@@ -46,6 +46,9 @@
46
46
  --mote-tracking: -0.011em;
47
47
  /* Display scale — opt-in, for heroes and error pages; body scale untouched */
48
48
  --mote-display: clamp(2.75rem, 1.5rem + 4vw, 5rem);
49
+ /* Reading measure: the comfortable max width for long-form content.
50
+ Override per-page for narrow or wide layouts. */
51
+ --mote-container-max: 70rem;
49
52
 
50
53
  /* Shape & depth */
51
54
  --mote-radius: 10px;
@@ -451,10 +454,16 @@ select[multiple] {
451
454
  width: 1em;
452
455
  height: 1em;
453
456
  padding: 0;
454
- accent-color: var(--mote-accent);
457
+ accent-color: var(--mote-status, var(--mote-accent));
455
458
  vertical-align: baseline;
456
459
  }
457
460
 
461
+ /* Harden checkbox/radio sizing against page-level width: 100% rules
462
+ (the documented stacked-form recipe). :where() keeps it overridable. */
463
+ :is([type="checkbox"], [type="radio"]):where(input) {
464
+ width: 1em;
465
+ }
466
+
458
467
  [type="color"] {
459
468
  padding: 0.2rem;
460
469
  height: 2.4rem;
@@ -484,7 +493,7 @@ select[multiple] {
484
493
  cursor: not-allowed;
485
494
  }
486
495
 
487
- :is(input, select, textarea):user-invalid {
496
+ :is(input, select, textarea):is(:user-invalid, [aria-invalid="true"]) {
488
497
  border-color: var(--mote-error);
489
498
  }
490
499
 
@@ -526,6 +535,10 @@ button:hover, [type="button"]:hover, [type="submit"]:hover, [type="reset"]:hover
526
535
  background: none;
527
536
  border: var(--mote-border-width) solid color-mix(in oklab, var(--mote-accent), transparent 55%);
528
537
  color: var(--mote-accent-deep);
538
+ padding: 0.5rem 1.1rem;
539
+ border-radius: var(--mote-radius-sm);
540
+ text-decoration: none;
541
+ cursor: pointer;
529
542
  }
530
543
 
531
544
  .mote-ghost:hover {
@@ -625,12 +638,13 @@ progress, meter {
625
638
  /* ---------------------------------------------------------------------------
626
639
  * 11. Opt-in helpers
627
640
  *
628
- * mote is classless by design; these are the only three escape hatches.
641
+ * mote is class-optional by design — elements are styled from tags
642
+ * alone; these classes are opt-in conveniences, never a required API.
629
643
  * ------------------------------------------------------------------------- */
630
644
 
631
645
  /* Centered page wrapper: <main class="mote-container"> */
632
646
  .mote-container {
633
- width: min(100% - 3rem, 70rem);
647
+ width: min(100% - 3rem, var(--mote-container-max));
634
648
  margin-inline: auto;
635
649
  }
636
650
 
@@ -648,6 +662,70 @@ progress, meter {
648
662
 
649
663
  .mote-push { margin-inline-start: auto; }
650
664
 
665
+ /* Stacked forms: stretch fields, keep checkboxes/radios compact.
666
+ An opt-in alternative to the page-global width: 100% recipe. */
667
+ .mote-stack :is(input, select, textarea) {
668
+ width: 100%;
669
+ }
670
+
671
+ .mote-stack :is([type="checkbox"], [type="radio"]) {
672
+ width: 1em;
673
+ }
674
+
675
+ /* Solid button on any element — anchors, spans, divs.
676
+ Same recipe as the element-level button styles; status classes
677
+ gated identically (class on the element itself). */
678
+ .mote-btn {
679
+ display: inline-block;
680
+ font: inherit;
681
+ font-weight: 500;
682
+ color: var(--mote-on-accent);
683
+ padding: 0.5rem 1.1rem;
684
+ background: var(--mote-accent);
685
+ border: var(--mote-border-width) solid transparent;
686
+ border-radius: var(--mote-radius-sm);
687
+ text-decoration: none;
688
+ cursor: pointer;
689
+ transition: background var(--mote-transition), border-color var(--mote-transition),
690
+ transform var(--mote-transition);
691
+ }
692
+
693
+ .mote-btn:hover {
694
+ background: color-mix(in oklab, var(--mote-accent), var(--mote-fg) 14%);
695
+ box-shadow: var(--mote-shadow-sm);
696
+ }
697
+
698
+ .mote-btn:active {
699
+ transform: translateY(1px);
700
+ }
701
+
702
+ .mote-btn:where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted):where(:not(.mote-ghost)) {
703
+ background: var(--mote-status);
704
+ color: var(--mote-on-status, var(--mote-bg));
705
+ }
706
+
707
+ .mote-btn:where(.mote-success, .mote-warning, .mote-error, .mote-info, .mote-muted):where(:not(.mote-ghost)):hover {
708
+ background: color-mix(in oklab, var(--mote-status), var(--mote-fg) 14%);
709
+ }
710
+
711
+ /* Media object: avatar/icon beside body (forum posts, comments, chat) */
712
+ .mote-media {
713
+ display: flex;
714
+ align-items: flex-start;
715
+ gap: 1rem;
716
+ }
717
+
718
+ .mote-media > :last-child {
719
+ flex: 1;
720
+ min-width: 0;
721
+ }
722
+
723
+ /* Headings inside rows (version headers, card titles): default heading
724
+ margins balloon the row — zero them inside the flex context. */
725
+ :where(.mote-row) > :is(h1, h2, h3, h4, h5, h6) {
726
+ margin-block: 0;
727
+ }
728
+
651
729
  /* Visually hidden, screen-reader available */
652
730
  .mote-sr-only {
653
731
  position: absolute;
@@ -739,6 +817,7 @@ progress, meter {
739
817
  font-size: var(--mote-display);
740
818
  line-height: 1.1;
741
819
  letter-spacing: var(--mote-tracking);
820
+ color: var(--mote-status, inherit);
742
821
  text-wrap: balance;
743
822
  }
744
823
 
@@ -783,4 +862,10 @@ progress, meter {
783
862
  a { color: #000; }
784
863
  details { border: none; }
785
864
  dialog { box-shadow: none; }
865
+
866
+ /* Opt-in print helpers */
867
+ .mote-no-print { display: none !important; }
868
+ .mote-print-only { display: block; }
786
869
  }
870
+
871
+ .mote-print-only { display: none; }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mote.css",
3
- "version": "0.5.0",
4
- "description": "A mote of styling for plain HTML — tiny, semantic, classless CSS with zero dependencies and no build step.",
3
+ "version": "0.6.0",
4
+ "description": "A mote of styling for plain HTML — tiny, semantic, class-optional CSS with zero dependencies and no build step.",
5
5
  "style": "mote.css",
6
6
  "files": [
7
7
  "mote.css",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "keywords": [
14
14
  "css",
15
- "classless",
15
+ "class-optional",
16
16
  "semantic",
17
17
  "framework-agnostic",
18
18
  "dark-mode",