mote.css 0.5.1 → 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,7 +5,68 @@ 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.1] - 2026-10-20
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.
9
70
 
10
71
  ### Fixed
11
72
 
@@ -274,6 +335,7 @@ Components:
274
335
  - Element gallery demo page (`demo.html`).
275
336
 
276
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
277
339
  [0.5.1]: https://gitlab.com/abmach/mote/compare/v0.5.0...v0.5.1
278
340
  [0.5.0]: https://gitlab.com/abmach/mote/compare/v0.4.0...v0.5.0
279
341
  [0.4.0]: https://gitlab.com/abmach/mote/compare/v0.3.0...v0.4.0
package/README.md CHANGED
@@ -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
 
@@ -144,11 +148,15 @@ mote is class-optional by design — elements are styled from tags alone; these
144
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 |
156
+ | Table of contents | `nav[aria-label="toc"]` — semantic, no class |
150
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.1 · 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;
@@ -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;
@@ -215,6 +223,28 @@ nav[aria-label="pagination"] [aria-current="page"]:hover {
215
223
  color: var(--mote-on-accent);
216
224
  }
217
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
+
218
248
  /* Page footer: the quiet sibling of the site header */
219
249
  :where(body > footer, .mote-footer) {
220
250
  margin-block-start: 3rem;
@@ -461,6 +491,7 @@ details[name] > :not(summary):last-child {
461
491
  when the header actually exists. */
462
492
  html:where(:has(body > header)) {
463
493
  scroll-padding-block-start: 5rem;
494
+ --mote-rail-top: 4.75rem;
464
495
  }
465
496
 
466
497
  .mote-doc > header { grid-row: 1; grid-column: 1 / -1; }
@@ -468,6 +499,12 @@ html:where(:has(body > header)) {
468
499
 
469
500
  .mote-doc > main { grid-row: 2; grid-column: 1; min-width: 0; }
470
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
+
471
508
  /* nav = left rail */
472
509
  .mote-doc > nav {
473
510
  grid-row: 2;
@@ -512,6 +549,17 @@ html:where(:has(body > header)) {
512
549
  color: var(--mote-on-accent);
513
550
  }
514
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
+
515
563
  .mote-doc:where(:has(> nav)) {
516
564
  grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
517
565
  }
package/mote.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * mote.css — a mote of styling for plain HTML.
3
- * v0.5.1 · 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:
@@ -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 {
@@ -631,7 +644,7 @@ progress, meter {
631
644
 
632
645
  /* Centered page wrapper: <main class="mote-container"> */
633
646
  .mote-container {
634
- width: min(100% - 3rem, 70rem);
647
+ width: min(100% - 3rem, var(--mote-container-max));
635
648
  margin-inline: auto;
636
649
  }
637
650
 
@@ -649,6 +662,70 @@ progress, meter {
649
662
 
650
663
  .mote-push { margin-inline-start: auto; }
651
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
+
652
729
  /* Visually hidden, screen-reader available */
653
730
  .mote-sr-only {
654
731
  position: absolute;
@@ -740,6 +817,7 @@ progress, meter {
740
817
  font-size: var(--mote-display);
741
818
  line-height: 1.1;
742
819
  letter-spacing: var(--mote-tracking);
820
+ color: var(--mote-status, inherit);
743
821
  text-wrap: balance;
744
822
  }
745
823
 
@@ -784,4 +862,10 @@ progress, meter {
784
862
  a { color: #000; }
785
863
  details { border: none; }
786
864
  dialog { box-shadow: none; }
865
+
866
+ /* Opt-in print helpers */
867
+ .mote-no-print { display: none !important; }
868
+ .mote-print-only { display: block; }
787
869
  }
870
+
871
+ .mote-print-only { display: none; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mote.css",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
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": [