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 +85 -0
- package/README.md +14 -6
- package/mote.components.css +61 -8
- package/mote.css +91 -6
- package/package.json +3 -3
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,
|
|
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
|
|
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.
|
|
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
|
|
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` —
|
|
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
|
-
|
|
|
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 |
|
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.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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
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.
|
|
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,
|
|
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
|
|
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,
|
|
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.
|
|
4
|
-
"description": "A mote of styling for plain HTML — tiny, semantic,
|
|
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
|
-
"
|
|
15
|
+
"class-optional",
|
|
16
16
|
"semantic",
|
|
17
17
|
"framework-agnostic",
|
|
18
18
|
"dark-mode",
|