mote.css 0.0.0-stage → 0.2.1

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 ADDED
@@ -0,0 +1,95 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.1] - 2026-10-16
9
+
10
+ ### Fixed
11
+
12
+ - README: correct minified CDN URL — jsDelivr publishes the minified variant
13
+ of a dotted filename as `mote.min.css`, not `mote.css.min.css` (verified live).
14
+
15
+ ### Changed
16
+
17
+ - Release CI moved to GitLab CI (`.gitlab-ci.yml`): tag-triggered `npm stage
18
+ publish` with OIDC trusted publishing — no tokens anywhere; a maintainer
19
+ still approves every release with 2FA. The GitHub mirror is distribution-only.
20
+ - Publishes now carry npm provenance attestations (the source repository is
21
+ public).
22
+ - Package metadata points to the canonical GitLab source; GitHub remains the
23
+ distribution mirror.
24
+
25
+ ## [0.2.0] - 2026-10-15
26
+
27
+ ### Added
28
+
29
+ - Documentation completeness pass: every documented item now has a working
30
+ live example and its code sample; all status variations are shown —
31
+ notes and toasts in all four statuses, spinner status variants,
32
+ status-tinted blockquote rule and summary marker, plus a live
33
+ token-cascade demo (a card re-themed by two inline variables).
34
+ - `mote.components.css`: opt-in, CSS-only components — cards, badges, avatars,
35
+ skeleton, spinner, sticky header, nav pills, breadcrumbs (semantic selector),
36
+ popover tooltips/menus/toasts, exclusive `<details name>` accordions, and
37
+ popover/dialog entrance animations via `@starting-style` + `allow-discrete`.
38
+ - Status classes (`mote-success`, `mote-warning`, `mote-error`, `mote-info`,
39
+ `mote-muted`) as pure `--mote-status` / `--mote-status-soft` token assignments,
40
+ consumed by notes, badges and toasts with accent fallback — one override re-skins
41
+ every status surface; inline `--mote-status` works too.
42
+ - Live documentation page at `docs/index.html`.
43
+ - Anchored popover placement via CSS anchor positioning (Chrome 125+,
44
+ Safari 26+): inline `anchor-name` + `position-anchor` places a popover
45
+ below its trigger with viewport flip; centered fallback elsewhere.
46
+ - Accordion open/close animation in Chromium via `details::details-content`
47
+ + `interpolate-size` (gated on support; instant toggle elsewhere).
48
+ - Status-token consumption extended to blockquote rules, `progress`/`meter`,
49
+ spinners and `summary` markers.
50
+
51
+ ### Fixed
52
+
53
+ - Accordion end-of-transition snap: content margins do not interpolate, so
54
+ the animated `details::details-content` height target differed from the
55
+ settled layout by exactly the content's bottom margin (16px jump on open).
56
+ The animated box is now kept flush (child margins zeroed inside the
57
+ `@supports` gate, spacing provided by padding, `display: flow-root`
58
+ containment), verified frame-by-frame.
59
+ - Popovers no longer snap to the viewport's top-left corner: mote no longer
60
+ overrides the native `margin: auto` centering.
61
+ - Spinner no longer rides high next to text: baseline-lowered alignment.
62
+
63
+ ### Changed
64
+
65
+ - `.mote-note-*` and `.mote-badge-*` compound status classes replaced by the
66
+ shared status classes (`.mote-note mote-success` instead of
67
+ `.mote-note mote-note-success`).
68
+
69
+ ## [0.1.0] - 2026-10-15
70
+
71
+ ### Added
72
+
73
+ - Initial release: a tiny, semantic, classless CSS layer for plain HTML.
74
+ - Variable-driven token system (`--mote-*`) — palette, typography, shape, depth,
75
+ motion; derived tones computed via `color-mix()`.
76
+ - Automatic dark mode via `prefers-color-scheme` with manual `data-theme`
77
+ overrides on `<html>`.
78
+ - Warm-paper / warm-charcoal palettes with muted plum accent and moss, honey,
79
+ brick, dusty-blue semantic colors — all WCAG AA or better.
80
+ - Element styles: headings and inline text, links, lists, definition lists,
81
+ blockquotes, `hr`, `figure`/`figcaption`, `code`/`pre`/`kbd`/`samp`/`var`,
82
+ tables (with caption, footer, zebra), all form controls and states, buttons,
83
+ `details`/`summary`, `dialog`, `progress`/`meter`, media elements.
84
+ - Focus-visible rings, `:user-invalid` feedback, reduced-motion support,
85
+ minimal print styles.
86
+ - Opt-in helpers: `.mote-container`, `.mote-muted`, `.mote-note` surfaces.
87
+ - Element gallery demo page (`demo.html`).
88
+
89
+ [0.2.1]: https://gitlab.com/abmach/mote/compare/v0.2.0...v0.2.1
90
+ [0.2.0]: https://gitlab.com/abmach/mote/compare/v0.1.0...v0.2.0
91
+ [0.1.0]: https://gitlab.com/abmach/mote/-/tags/v0.1.0
92
+
93
+ ---
94
+
95
+ Copyright (c) 2026 Arthur Biazon Machado. All rights reserved.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arthur Biazon Machado
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,171 @@
1
- # Temporary Holding Version
1
+ # mote.css
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > The smallest thing that can carry style — a tiny, semantic, classless CSS layer for plain HTML.
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.
6
+
7
+ Source on [GitLab](https://gitlab.com/abmach/mote) · mirrored on [GitHub](https://github.com/abmach/mote).
8
+
9
+ ```html
10
+ <!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <link rel="stylesheet" href="mote.css">
14
+ </head>
15
+ <body>
16
+ <main>
17
+ <h1>Hello, cozy.</h1>
18
+ <p>That's the whole setup.</p>
19
+ </main>
20
+ </body>
21
+ </html>
22
+ ```
23
+
24
+ ## Principles
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).
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
+ - **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
+ - **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.
30
+ - **Framework-agnostic.** It's just CSS — plain HTML, Svelte, Vue, React, HTMX, PHP, your CMS, anything.
31
+ - **Cozy, and unobtrusive about it.** Warm neutrals, soft edges, generous rhythm — styling that never shouts. Automatic light/dark, AA+ contrast, visible focus, reduced-motion respected.
32
+
33
+ ## Install
34
+
35
+ **CDN** — jsDelivr, serving the npm package, with auto-minified variants:
36
+
37
+ ```html
38
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0.2.0/mote.css">
39
+ <!-- minified variant (dotted filenames minify to <name>.min.css) -->
40
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0.2.0/mote.min.css">
41
+
42
+ <!-- optional components file -->
43
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mote.css@0.2.0/mote.components.css">
44
+ ```
45
+
46
+ Use `mote.css@0` to track the latest 0.x release. CDN URLs go live once the package is published.
47
+
48
+ **npm**:
49
+
50
+ ```sh
51
+ npm install mote.css
52
+ ```
53
+
54
+ **Download**: [`mote.css`](mote.css) and [`mote.components.css`](mote.components.css) — link them directly.
55
+
56
+ ## Theming
57
+
58
+ Everything is a variable. Override the ones you care about:
59
+
60
+ ```css
61
+ :root {
62
+ --mote-accent: #4a7a5b; /* your brand color */
63
+ --mote-bg: #fffdf5; /* your paper */
64
+ --mote-radius: 6px; /* your edges */
65
+ }
66
+ ```
67
+
68
+ ### The token cascade
69
+
70
+ Values flow in layers, each computed from the last — retune a base token and everything downstream re-derives automatically:
71
+
72
+ 1. **Base palette** — the raw tokens: `--mote-bg`, `--mote-fg`, `--mote-accent`, the four semantics, fonts, radii, shadows. Light values live in `:root`; dark values swap in via `prefers-color-scheme` or `data-theme`.
73
+ 2. **Derived tones** — computed from the base with `color-mix()`: `--mote-accent-deep` (hover deepening), `--mote-accent-soft`, the four `--mote-*-soft` tints, `--mote-focus-ring`, `--mote-border-strong`. They can never go stale — change `--mote-accent` and every tint, ring and hover shade follows.
74
+ 3. **Status indirection** — status classes assign `--mote-status` / `--mote-status-soft` and nothing else. Components read them with accent fallbacks: `color: var(--mote-status, var(--mote-accent-deep))`.
75
+
76
+ The consequence: theming is a handful of variable overrides, and an inline tweak (`style="--mote-status: …"`) slots into the same chain at any level.
77
+
78
+ ### Token reference
79
+
80
+ | Token | Light | Dark | Used for |
81
+ |---|---|---|---|
82
+ | `--mote-bg` | `#faf9f7` | `#1c1a17` | Page background |
83
+ | `--mote-surface` | `#f1eee7` | `#262320` | Code blocks, cards, inputs |
84
+ | `--mote-fg` | `#2b2924` | `#eae6de` | Body text |
85
+ | `--mote-muted` | `#716a60` | `#a49c90` | Secondary text, captions |
86
+ | `--mote-border` | `#e6e0d4` | `#3b362f` | Rules, borders |
87
+ | `--mote-accent` | `#6f5486` | `#b8a1ca` | Links, buttons, focus, markers |
88
+ | `--mote-on-accent` | `#ffffff` | `#241d28` | Text on accent (buttons) |
89
+ | `--mote-success` | `#4e7d51` | `#8ab88c` | Positive semantics |
90
+ | `--mote-warning` | `#8a6819` | `#d4b465` | Cautious semantics |
91
+ | `--mote-error` | `#b34d4d` | `#d98a8a` | Destructive semantics |
92
+ | `--mote-info` | `#527097` | `#96b1d1` | Neutral semantics |
93
+ | `--mote-font` | `"Inter", system-ui, …` | | Content typeface stack |
94
+ | `--mote-font-mono` | `"Commit Mono", ui-monospace, …` | | Code typeface stack |
95
+ | `--mote-radius` | `10px` | | Default corner softness |
96
+ | `--mote-radius-sm` | `6px` | | Controls, inline code |
97
+ | `--mote-radius-lg` | `16px` | | Dialogs |
98
+ | `--mote-shadow` / `--mote-shadow-sm` | warm diffuse | | Elevation |
99
+ | `--mote-transition` | `150ms ease` | | Hover/focus timing |
100
+
101
+ Derived tones (soft tints, focus ring, hover deepening) are computed from these via `color-mix()`, so changing a base token re-themes everything consistently — no redundant palette bookkeeping.
102
+
103
+ ### Dark mode
104
+
105
+ By default, mote follows the user's system preference. To force a theme, set an attribute on `<html>`:
106
+
107
+ ```html
108
+ <html data-theme="dark"> <!-- or "light" -->
109
+ ```
110
+
111
+ The accent hue is deliberately plum: it's the one family that doesn't collide with the success/warning/error/info semantics, so a link never looks like a status.
112
+
113
+ ### Fonts (optional, not bundled)
114
+
115
+ mote's stacks fall back to your system fonts out of the box. For the full experience, add Inter and Commit Mono (both free; Inter is OFL, Commit Mono is OFL):
116
+
117
+ ```html
118
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/inter@5/index.css">
119
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/commit-mono@5/index.css">
120
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/commit-mono@5/700.css">
121
+ ```
122
+
123
+ Or self-host with `npm install @fontsource-variable/inter @fontsource/commit-mono` — or any fonts you like; the stacks are just variables.
124
+
125
+ ## Opt-in helpers
126
+
127
+ mote is classless by design; these are the only escape hatches, plus status classes that every status-consuming component reads:
128
+
129
+ | Class | Purpose |
130
+ |---|---|
131
+ | `.mote-container` | Centered page wrapper: `width: min(100% - 3rem, 70rem)` |
132
+ | `.mote-muted` | Dimmed secondary text (also a neutral status) |
133
+ | `.mote-note` + status class | Tinted message surfaces |
134
+ | `.mote-success` · `.mote-warning` · `.mote-error` · `.mote-info` | Pure status tokens: they only set `--mote-status` / `--mote-status-soft`, so one override re-skins every note, badge and toast |
135
+
136
+ ## Components
137
+
138
+ `mote.components.css` is the optional second file: opt-in components, **CSS-only**, riding on native platform features — popovers, dialogs, exclusive `<details>` groups. No JavaScript anywhere.
139
+
140
+ | Component | Hook |
141
+ |---|---|
142
+ | Cards | `.mote-card` |
143
+ | Badges | `.mote-badge` + optional status class |
144
+ | Avatars | `.mote-avatar` |
145
+ | Skeleton / spinner | `.mote-skeleton`, `.mote-spinner` |
146
+ | Sticky site header / nav pills | `.mote-header`, `.mote-nav` |
147
+ | Breadcrumbs | `nav[aria-label="breadcrumb"]` — semantic, no class |
148
+ | Tooltips | `[popover].mote-tooltip` + `popovertarget` button |
149
+ | Menus | `menu[popover]` + `popovertarget` button |
150
+ | Toasts | `[popover].mote-toast` + status class |
151
+ | Exclusive accordions | `<details name="group">` — the browser closes siblings |
152
+ | Dialog & popover entrance animations | automatic when the file is loaded |
153
+
154
+ Full live documentation with copyable markup for everything above: **[docs/index.html](docs/index.html)**.
155
+
156
+ ## Browser support
157
+
158
+ Modern evergreen browsers (uses `color-mix()`, `:focus-visible`, `accent-color`, `:user-invalid`):
159
+ Chrome/Edge 111+, Firefox 113+, Safari 16.2+. `color-mix()`-dependent niceties degrade gracefully on older browsers.
160
+
161
+ The components file additionally uses Baseline 2024 platform features — `popover`, `<details name>`,
162
+ `@starting-style`, `allow-discrete` — each degrading gracefully: accordions stay independent,
163
+ animations become instant, on older engines.
164
+
165
+ ## Demo
166
+
167
+ Open [`docs/index.html`](docs/index.html) for the full live documentation, or [`demo.html`](demo.html) for a quick gallery of every styled element.
168
+
169
+ ## License
170
+
171
+ [MIT](LICENSE). Fonts referenced by the stacks (Inter, Commit Mono) are SIL OFL 1.1 — see their respective projects.
@@ -0,0 +1,372 @@
1
+ /*!
2
+ * mote.components.css — opt-in components for mote.css.
3
+ * v0.2.1 · MIT
4
+ * https://gitlab.com/abmach/mote · mirror: https://github.com/abmach/mote
5
+ *
6
+ * Requires mote.css loaded first (provides the --mote-* tokens).
7
+ * Everything here is CSS-only and rides on native platform features:
8
+ * popovers, dialogs, exclusive <details> groups. No JavaScript required.
9
+ *
10
+ * MIT License.
11
+ */
12
+
13
+ /* ---------------------------------------------------------------------------
14
+ * 1. Cards
15
+ * ------------------------------------------------------------------------- */
16
+
17
+ .mote-card {
18
+ padding: 1.25rem 1.5rem;
19
+ border: var(--mote-border-width) solid var(--mote-border);
20
+ border-radius: var(--mote-radius-lg);
21
+ background: color-mix(in oklab, var(--mote-surface) 55%, var(--mote-bg));
22
+ box-shadow: var(--mote-shadow-sm);
23
+ }
24
+
25
+ .mote-card > :first-child {
26
+ margin-block-start: 0;
27
+ }
28
+
29
+ .mote-card > :last-child {
30
+ margin-block-end: 0;
31
+ }
32
+
33
+ /* ---------------------------------------------------------------------------
34
+ * 2. Badges — .mote-badge + a status class from mote.css (optional)
35
+ *
36
+ * <span class="mote-badge">accent</span>
37
+ * <span class="mote-badge mote-success">success</span>
38
+ * ------------------------------------------------------------------------- */
39
+
40
+ .mote-badge {
41
+ display: inline-block;
42
+ padding: 0.1rem 0.6rem;
43
+ border-radius: 999px;
44
+ font-size: 0.8em;
45
+ font-weight: 500;
46
+ line-height: 1.8;
47
+ background: var(--mote-status-soft, var(--mote-accent-soft));
48
+ color: var(--mote-status, var(--mote-accent-deep));
49
+ user-select: none;
50
+ }
51
+
52
+ /* ---------------------------------------------------------------------------
53
+ * 3. Avatars
54
+ * ------------------------------------------------------------------------- */
55
+
56
+ .mote-avatar {
57
+ width: 2.5rem;
58
+ height: 2.5rem;
59
+ border-radius: 999px;
60
+ object-fit: cover;
61
+ border: var(--mote-border-width) solid var(--mote-border);
62
+ box-shadow: var(--mote-shadow-sm);
63
+ }
64
+
65
+ /* ---------------------------------------------------------------------------
66
+ * 4. Skeleton loading placeholder
67
+ * ------------------------------------------------------------------------- */
68
+
69
+ .mote-skeleton {
70
+ border-radius: var(--mote-radius-sm);
71
+ background:
72
+ linear-gradient(
73
+ 100deg,
74
+ color-mix(in oklab, var(--mote-fg) 5%, transparent) 40%,
75
+ color-mix(in oklab, var(--mote-fg) 9%, transparent) 50%,
76
+ color-mix(in oklab, var(--mote-fg) 5%, transparent) 60%
77
+ );
78
+ background-size: 200% 100%;
79
+ animation: mote-shimmer 1.6s ease-in-out infinite;
80
+ }
81
+
82
+ @keyframes mote-shimmer {
83
+ from { background-position: 200% 0; }
84
+ to { background-position: -200% 0; }
85
+ }
86
+
87
+ /* ---------------------------------------------------------------------------
88
+ * 5. Spinner
89
+ * ------------------------------------------------------------------------- */
90
+
91
+ .mote-spinner {
92
+ display: inline-block;
93
+ vertical-align: -0.25em;
94
+ width: 1.25rem;
95
+ height: 1.25rem;
96
+ border: 2px solid color-mix(in oklab, var(--mote-status, var(--mote-accent)), transparent 65%);
97
+ border-block-start-color: var(--mote-status, var(--mote-accent));
98
+ border-radius: 999px;
99
+ animation: mote-spin 0.7s linear infinite;
100
+ }
101
+
102
+ @keyframes mote-spin {
103
+ to { rotate: 1turn; }
104
+ }
105
+
106
+ /* ---------------------------------------------------------------------------
107
+ * 6. Site header and nav
108
+ * ------------------------------------------------------------------------- */
109
+
110
+ .mote-header {
111
+ position: sticky;
112
+ top: 0;
113
+ z-index: 10;
114
+ background: color-mix(in oklab, var(--mote-bg) 82%, transparent);
115
+ backdrop-filter: blur(10px);
116
+ border-block-end: var(--mote-border-width) solid var(--mote-border);
117
+ }
118
+
119
+ .mote-nav ul {
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ gap: 0.25rem;
123
+ padding: 0;
124
+ margin: 0;
125
+ list-style: none;
126
+ }
127
+
128
+ .mote-nav a {
129
+ display: inline-block;
130
+ padding: 0.35rem 0.8rem;
131
+ border-radius: 999px;
132
+ color: var(--mote-fg);
133
+ text-decoration: none;
134
+ transition: background var(--mote-transition), color var(--mote-transition);
135
+ }
136
+
137
+ .mote-nav a:hover {
138
+ background: var(--mote-accent-soft);
139
+ color: var(--mote-accent-deep);
140
+ }
141
+
142
+ .mote-nav a[aria-current="page"] {
143
+ background: var(--mote-accent);
144
+ color: var(--mote-on-accent);
145
+ }
146
+
147
+ /* ---------------------------------------------------------------------------
148
+ * 7. Breadcrumbs — semantic selector, no class needed
149
+ * ------------------------------------------------------------------------- */
150
+
151
+ nav[aria-label="breadcrumb"] ol {
152
+ display: flex;
153
+ flex-wrap: wrap;
154
+ gap: 0.5rem;
155
+ padding: 0;
156
+ margin: 0;
157
+ font-size: 0.9em;
158
+ list-style: none;
159
+ color: var(--mote-muted);
160
+ }
161
+
162
+ nav[aria-label="breadcrumb"] li + li::before {
163
+ content: "/";
164
+ margin-inline-end: 0.5rem;
165
+ color: var(--mote-border-strong);
166
+ }
167
+
168
+ nav[aria-label="breadcrumb"] [aria-current="page"] {
169
+ color: var(--mote-fg);
170
+ font-weight: 500;
171
+ }
172
+
173
+ /* ---------------------------------------------------------------------------
174
+ * 8. Popover surfaces — tooltips, menus, toasts
175
+ *
176
+ * Any element with the `popover` attribute gets the mote surface.
177
+ * Toggle it with a `popovertarget` button — zero JavaScript.
178
+ * ------------------------------------------------------------------------- */
179
+
180
+ [popover] {
181
+ padding: 0.8rem 1rem;
182
+ border: var(--mote-border-width) solid var(--mote-border);
183
+ border-radius: var(--mote-radius);
184
+ background: var(--mote-bg);
185
+ color: var(--mote-fg);
186
+ box-shadow: var(--mote-shadow);
187
+ }
188
+
189
+ /* Anchored popovers (Chrome 125+, Safari 26+): by default the browser
190
+ centers popovers in the viewport. To place one next to its trigger,
191
+ give the trigger an anchor name and the popover a matching
192
+ position-anchor (unique names per pair) — mote positions it below
193
+ the trigger and flips it when it would overflow the viewport.
194
+
195
+ <button popovertarget="menu" style="anchor-name: --menu-a">Options</button>
196
+ <menu popover id="menu" style="position-anchor: --menu-a">…</menu>
197
+
198
+ Engines without support keep the centered fallback. */
199
+ @supports (anchor-name: --mote-a) {
200
+ [popover][style*="position-anchor"] {
201
+ position-area: block-end;
202
+ justify-self: anchor-center;
203
+ position-try-fallbacks: flip-block, flip-inline;
204
+ margin: 0.45rem;
205
+ }
206
+ }
207
+
208
+ /* Menus: <menu popover> + buttons — semantic, no classes */
209
+ menu[popover] {
210
+ min-width: 11rem;
211
+ padding: 0.35rem;
212
+ }
213
+
214
+ menu[popover] li {
215
+ margin: 0;
216
+ list-style: none;
217
+ }
218
+
219
+ menu[popover] :is(button, [role="menuitem"]) {
220
+ display: block;
221
+ width: 100%;
222
+ padding: 0.5rem 0.75rem;
223
+ border: none;
224
+ border-radius: var(--mote-radius-sm);
225
+ background: none;
226
+ color: var(--mote-fg);
227
+ font: inherit;
228
+ text-align: start;
229
+ cursor: pointer;
230
+ }
231
+
232
+ menu[popover] :is(button, [role="menuitem"]):hover,
233
+ menu[popover] :is(button, [role="menuitem"]):focus-visible {
234
+ background: var(--mote-accent-soft);
235
+ color: var(--mote-accent-deep);
236
+ }
237
+
238
+ /* Tooltips: add .mote-tooltip to the popover element */
239
+ [popover].mote-tooltip {
240
+ max-width: 18rem;
241
+ padding: 0.4rem 0.7rem;
242
+ font-size: 0.85em;
243
+ border-radius: var(--mote-radius-sm);
244
+ box-shadow: var(--mote-shadow-sm);
245
+ }
246
+
247
+ /* Toasts: popover + .mote-toast + optional status class, shown with el.showPopover() */
248
+ [popover].mote-toast {
249
+ position: fixed;
250
+ inset: auto 1.25rem 1.25rem auto;
251
+ max-width: 22rem;
252
+ font-size: 0.9em;
253
+ border-block-start-width: 3px;
254
+ border-block-start-color: var(--mote-status, var(--mote-accent));
255
+ }
256
+
257
+ /* ---------------------------------------------------------------------------
258
+ * 9. Open/close animations for popovers and dialogs
259
+ * (transition-behavior + @starting-style — Baseline 2024, degrades to
260
+ * instant open/close on older browsers)
261
+ * ------------------------------------------------------------------------- */
262
+
263
+ [popover],
264
+ dialog[open] {
265
+ transition:
266
+ opacity 180ms ease,
267
+ transform 180ms ease,
268
+ display 180ms allow-discrete,
269
+ overlay 180ms allow-discrete;
270
+ }
271
+
272
+ [popover]:popover-open,
273
+ dialog[open] {
274
+ opacity: 1;
275
+ transform: none;
276
+ }
277
+
278
+ @starting-style {
279
+ [popover]:popover-open,
280
+ dialog[open] {
281
+ opacity: 0;
282
+ transform: translateY(8px) scale(0.98);
283
+ }
284
+ }
285
+
286
+ dialog::backdrop {
287
+ opacity: 1;
288
+ transition: opacity 180ms allow-discrete;
289
+ }
290
+
291
+ @starting-style {
292
+ dialog::backdrop {
293
+ opacity: 0;
294
+ }
295
+ }
296
+
297
+ /* ---------------------------------------------------------------------------
298
+ * 10. Exclusive accordions — <details name="group">
299
+ * Siblings sharing a name auto-close each other; one open at a time.
300
+ * ------------------------------------------------------------------------- */
301
+
302
+ details[name] {
303
+ padding: 0;
304
+ border: none;
305
+ border-block-end: var(--mote-border-width) solid var(--mote-border);
306
+ border-radius: 0;
307
+ background: none;
308
+ }
309
+
310
+ details[name]:last-of-type {
311
+ border-block-end: none;
312
+ }
313
+
314
+ details[name] summary {
315
+ padding: 0.9rem 1.2rem;
316
+ transition: color var(--mote-transition);
317
+ }
318
+
319
+ details[name][open] summary {
320
+ color: var(--mote-accent-deep);
321
+ }
322
+
323
+ details[name] > :not(summary) {
324
+ margin-block: 0;
325
+ padding: 0.15rem 1.2rem 0.9rem;
326
+ }
327
+
328
+ /* ---------------------------------------------------------------------------
329
+ * 11. Accordion open/close animation
330
+ *
331
+ * Chromium's ::details-content pseudo-element + interpolate-size allow
332
+ * height to animate to `auto`. Gated on support — other engines keep
333
+ * the instant toggle.
334
+ * ------------------------------------------------------------------------- */
335
+
336
+ @supports selector(details::details-content) and (interpolate-size: allow-keywords) {
337
+ :root {
338
+ interpolate-size: allow-keywords;
339
+ }
340
+
341
+ /* Margins do not interpolate: a first/last child margin makes the
342
+ animated block-size target differ from the settled layout, which
343
+ snaps at the transition's end. Keep the animated box flush;
344
+ spacing comes from padding (which lives inside it) instead. */
345
+ details::details-content > :first-child { margin-block-start: 0; }
346
+ details::details-content > :last-child { margin-block-end: 0; }
347
+
348
+ details::details-content {
349
+ /* flow-root contains arbitrary content margins (overflow: clip
350
+ does not create a BFC), keeping the animated box flush. */
351
+ display: flow-root;
352
+ block-size: 0;
353
+ overflow: clip;
354
+ opacity: 0;
355
+ transition:
356
+ block-size 260ms ease,
357
+ opacity 260ms ease,
358
+ content-visibility 260ms allow-discrete;
359
+ }
360
+
361
+ details[open]::details-content {
362
+ block-size: auto;
363
+ opacity: 1;
364
+ }
365
+
366
+ @starting-style {
367
+ details[open]::details-content {
368
+ block-size: 0;
369
+ opacity: 0;
370
+ }
371
+ }
372
+ }
package/mote.css ADDED
@@ -0,0 +1,627 @@
1
+ /*!
2
+ * mote.css — a mote of styling for plain HTML.
3
+ * v0.2.1 · MIT
4
+ * https://gitlab.com/abmach/mote · mirror: https://github.com/abmach/mote
5
+ *
6
+ * The smallest thing that can carry style:
7
+ * framework-agnostic, semantic, classless, zero dependencies.
8
+ *
9
+ * MIT License. Fonts (Inter, Commit Mono) are NOT bundled.
10
+ * See the README for one-line self-hosting instructions.
11
+ */
12
+
13
+ /* ---------------------------------------------------------------------------
14
+ * 1. Design tokens
15
+ *
16
+ * Every color, radius, shadow and font in mote flows from these variables.
17
+ * Override any of them to theme your site — no selectors, no rebuild, no Sass.
18
+ *
19
+ * Theme switching:
20
+ * • Automatic: honors `prefers-color-scheme` when no data-theme is set.
21
+ * • Manual: set `data-theme="light"` or `data-theme="dark"` on <html>.
22
+ * ------------------------------------------------------------------------- */
23
+
24
+ :root {
25
+ color-scheme: light;
26
+
27
+ /* Palette — warm paper + muted plum */
28
+ --mote-bg: #faf9f7;
29
+ --mote-surface: #f1eee7;
30
+ --mote-fg: #2b2924;
31
+ --mote-muted: #716a60;
32
+ --mote-border: #e6e0d4;
33
+ --mote-accent: #6f5486;
34
+ --mote-on-accent: #ffffff;
35
+ --mote-success: #4e7d51;
36
+ --mote-warning: #8a6819;
37
+ --mote-error: #b34d4d;
38
+ --mote-info: #527097;
39
+
40
+ /* Typography */
41
+ --mote-font: "Inter", "Inter Variable", system-ui, -apple-system, "Segoe UI",
42
+ Roboto, "Helvetica Neue", Arial, sans-serif;
43
+ --mote-font-mono: "Commit Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
44
+ "Liberation Mono", monospace;
45
+ --mote-line-height: 1.65;
46
+ --mote-tracking: -0.011em;
47
+
48
+ /* Shape & depth */
49
+ --mote-radius: 10px;
50
+ --mote-radius-sm: 6px;
51
+ --mote-radius-lg: 16px;
52
+ --mote-border-width: 1px;
53
+ --mote-shadow: 0 1px 2px rgba(43, 41, 36, 0.05), 0 4px 16px rgba(43, 41, 36, 0.06);
54
+ --mote-shadow-sm: 0 1px 2px rgba(43, 41, 36, 0.08);
55
+ --mote-transition: 150ms ease;
56
+
57
+ /* Derived values — computed, never hand-tuned twice */
58
+ --mote-accent-deep: color-mix(in oklab, var(--mote-accent), black 14%);
59
+ --mote-accent-soft: color-mix(in oklab, var(--mote-accent) 9%, transparent);
60
+ --mote-selection: color-mix(in oklab, var(--mote-accent) 18%, transparent);
61
+ --mote-focus-ring: 0 0 0 3px color-mix(in oklab, var(--mote-accent) 32%, transparent);
62
+ --mote-success-soft: color-mix(in oklab, var(--mote-success) 10%, transparent);
63
+ --mote-warning-soft: color-mix(in oklab, var(--mote-warning) 12%, transparent);
64
+ --mote-error-soft: color-mix(in oklab, var(--mote-error) 10%, transparent);
65
+ --mote-info-soft: color-mix(in oklab, var(--mote-info) 10%, transparent);
66
+ --mote-border-strong: color-mix(in oklab, var(--mote-border) 55%, var(--mote-fg));
67
+ --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='%23716a60' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
68
+ }
69
+
70
+ @media (prefers-color-scheme: dark) {
71
+ :root:not([data-theme="light"]) {
72
+ color-scheme: dark;
73
+
74
+ /* Palette — warm charcoal + light plum */
75
+ --mote-bg: #1c1a17;
76
+ --mote-surface: #262320;
77
+ --mote-fg: #eae6de;
78
+ --mote-muted: #a49c90;
79
+ --mote-border: #3b362f;
80
+ --mote-accent: #b8a1ca;
81
+ --mote-on-accent: #241d28;
82
+ --mote-success: #8ab88c;
83
+ --mote-warning: #d4b465;
84
+ --mote-error: #d98a8a;
85
+ --mote-info: #96b1d1;
86
+
87
+ --mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
88
+ --mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
89
+ --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");
90
+ }
91
+ }
92
+
93
+ :root[data-theme="dark"] {
94
+ color-scheme: dark;
95
+
96
+ --mote-bg: #1c1a17;
97
+ --mote-surface: #262320;
98
+ --mote-fg: #eae6de;
99
+ --mote-muted: #a49c90;
100
+ --mote-border: #3b362f;
101
+ --mote-accent: #b8a1ca;
102
+ --mote-on-accent: #241d28;
103
+ --mote-success: #8ab88c;
104
+ --mote-warning: #d4b465;
105
+ --mote-error: #d98a8a;
106
+ --mote-info: #96b1d1;
107
+
108
+ --mote-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 4px 16px rgba(0, 0, 0, 0.3);
109
+ --mote-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
110
+ --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");
111
+ }
112
+
113
+ /* ---------------------------------------------------------------------------
114
+ * 2. Base
115
+ * ------------------------------------------------------------------------- */
116
+
117
+ *,
118
+ *::before,
119
+ *::after {
120
+ box-sizing: border-box;
121
+ }
122
+
123
+ html {
124
+ -webkit-text-size-adjust: 100%;
125
+ text-size-adjust: 100%;
126
+ }
127
+
128
+ body {
129
+ margin: 0;
130
+ font-family: var(--mote-font);
131
+ font-size: 1rem;
132
+ line-height: var(--mote-line-height);
133
+ color: var(--mote-fg);
134
+ background: var(--mote-bg);
135
+ -webkit-font-smoothing: antialiased;
136
+ }
137
+
138
+ ::selection {
139
+ background: var(--mote-selection);
140
+ }
141
+
142
+ :focus-visible {
143
+ outline: none;
144
+ box-shadow: var(--mote-focus-ring);
145
+ border-radius: var(--mote-radius-sm);
146
+ }
147
+
148
+ /* ---------------------------------------------------------------------------
149
+ * 3. Typography
150
+ * ------------------------------------------------------------------------- */
151
+
152
+ h1, h2, h3, h4, h5, h6 {
153
+ margin: 2rem 0 0.8rem;
154
+ line-height: 1.25;
155
+ font-weight: 600;
156
+ text-wrap: balance;
157
+ }
158
+
159
+ h1 {
160
+ margin-block-start: 1rem;
161
+ font-size: clamp(1.85rem, 1.3rem + 2.2vw, 2.6rem);
162
+ letter-spacing: var(--mote-tracking);
163
+ }
164
+
165
+ h2 {
166
+ font-size: clamp(1.45rem, 1.15rem + 1.2vw, 1.85rem);
167
+ letter-spacing: var(--mote-tracking);
168
+ }
169
+
170
+ h3 { font-size: 1.3rem; }
171
+ h4 { font-size: 1.15rem; }
172
+ h5 { font-size: 1rem; }
173
+ h6 {
174
+ font-size: 1rem;
175
+ color: var(--mote-muted);
176
+ }
177
+
178
+ p, ul, ol, dl, blockquote, figure, table, pre, fieldset, details {
179
+ margin-block: 1rem;
180
+ }
181
+
182
+ :is(h1, h2, h3, h4, h5, h6) + :is(p, ul, ol, dl, blockquote, figure, table, pre) {
183
+ margin-block-start: 0.6rem;
184
+ }
185
+
186
+ small { font-size: 0.85em; }
187
+
188
+ sub, sup {
189
+ font-size: 0.7em;
190
+ line-height: 0;
191
+ position: relative;
192
+ vertical-align: baseline;
193
+ }
194
+
195
+ sup { top: -0.5em; }
196
+ sub { bottom: -0.25em; }
197
+
198
+ /* ---------------------------------------------------------------------------
199
+ * 4. Inline elements
200
+ * ------------------------------------------------------------------------- */
201
+
202
+ a {
203
+ color: var(--mote-accent);
204
+ text-decoration: underline;
205
+ text-decoration-color: color-mix(in oklab, var(--mote-accent), transparent 55%);
206
+ text-decoration-thickness: 1px;
207
+ text-underline-offset: 0.15em;
208
+ transition: color var(--mote-transition), text-decoration-color var(--mote-transition);
209
+ }
210
+
211
+ a:hover {
212
+ color: var(--mote-accent-deep);
213
+ text-decoration-color: currentColor;
214
+ }
215
+
216
+ strong, b { font-weight: 650; }
217
+
218
+ abbr[title] {
219
+ text-decoration: underline dotted var(--mote-muted);
220
+ text-underline-offset: 0.15em;
221
+ cursor: help;
222
+ }
223
+
224
+ mark {
225
+ padding: 0.1em 0.3em;
226
+ border-radius: var(--mote-radius-sm);
227
+ background: var(--mote-warning-soft);
228
+ color: var(--mote-fg);
229
+ }
230
+
231
+ del { color: var(--mote-error); }
232
+ ins { color: var(--mote-success); }
233
+
234
+ q { font-style: italic; }
235
+ cite { font-style: normal; color: var(--mote-muted); }
236
+
237
+ /* ---------------------------------------------------------------------------
238
+ * 5. Block content
239
+ * ------------------------------------------------------------------------- */
240
+
241
+ blockquote {
242
+ padding: 0.2rem 0 0.2rem 1.2rem;
243
+ border-inline-start: 3px solid color-mix(in oklab, var(--mote-status, var(--mote-accent)), transparent 45%);
244
+ color: var(--mote-muted);
245
+ font-style: italic;
246
+ }
247
+
248
+ blockquote > cite {
249
+ display: block;
250
+ margin-block-start: 0.5rem;
251
+ font-size: 0.85em;
252
+ text-align: end;
253
+ }
254
+
255
+ hr {
256
+ margin-block: 2.5rem;
257
+ border: none;
258
+ border-block-start: 1px solid var(--mote-border);
259
+ }
260
+
261
+ ul, ol {
262
+ padding-inline-start: 1.6rem;
263
+ }
264
+
265
+ li { margin-block: 0.3rem; }
266
+
267
+ li::marker {
268
+ color: var(--mote-muted);
269
+ }
270
+
271
+ dl {
272
+ display: grid;
273
+ grid-template-columns: auto 1fr;
274
+ gap: 0.4rem 1.5rem;
275
+ }
276
+
277
+ dt { font-weight: 600; }
278
+
279
+ dd {
280
+ margin: 0;
281
+ color: var(--mote-muted);
282
+ }
283
+
284
+ figure {
285
+ margin-inline: 0;
286
+ }
287
+
288
+ figcaption {
289
+ margin-block-start: 0.5rem;
290
+ font-size: 0.85em;
291
+ color: var(--mote-muted);
292
+ text-align: center;
293
+ }
294
+
295
+ /* ---------------------------------------------------------------------------
296
+ * 6. Code & keyboard
297
+ * ------------------------------------------------------------------------- */
298
+
299
+ code, kbd, samp, var {
300
+ font-family: var(--mote-font-mono);
301
+ font-size: 0.875em;
302
+ }
303
+
304
+ code, samp {
305
+ padding: 0.15em 0.4em;
306
+ border-radius: var(--mote-radius-sm);
307
+ background: var(--mote-surface);
308
+ border: var(--mote-border-width) solid var(--mote-border);
309
+ }
310
+
311
+ kbd {
312
+ padding: 0.15em 0.45em;
313
+ border-radius: var(--mote-radius-sm);
314
+ background: var(--mote-surface);
315
+ border: var(--mote-border-width) solid var(--mote-border-strong);
316
+ border-block-end-width: 2px;
317
+ font-size: 0.8em;
318
+ }
319
+
320
+ pre {
321
+ padding: 1rem 1.2rem;
322
+ overflow-x: auto;
323
+ border-radius: var(--mote-radius);
324
+ background: var(--mote-surface);
325
+ border: var(--mote-border-width) solid var(--mote-border);
326
+ tab-size: 4;
327
+ }
328
+
329
+ pre code, pre samp {
330
+ padding: 0;
331
+ background: none;
332
+ border: none;
333
+ font-size: 0.875rem;
334
+ line-height: 1.6;
335
+ }
336
+
337
+ var {
338
+ font-style: italic;
339
+ color: var(--mote-accent-deep);
340
+ }
341
+
342
+ /* ---------------------------------------------------------------------------
343
+ * 7. Tables
344
+ * ------------------------------------------------------------------------- */
345
+
346
+ table {
347
+ width: 100%;
348
+ border-collapse: collapse;
349
+ font-size: 0.95em;
350
+ }
351
+
352
+ caption {
353
+ margin-block-end: 0.6rem;
354
+ font-size: 0.85em;
355
+ color: var(--mote-muted);
356
+ text-align: start;
357
+ }
358
+
359
+ th, td {
360
+ padding: 0.6rem 0.8rem;
361
+ border-block-end: var(--mote-border-width) solid var(--mote-border);
362
+ text-align: start;
363
+ vertical-align: top;
364
+ }
365
+
366
+ thead th {
367
+ border-block: 2px solid var(--mote-border-strong);
368
+ border-block-end-width: 1px;
369
+ font-weight: 600;
370
+ }
371
+
372
+ tfoot td {
373
+ border-block: 2px solid var(--mote-border-strong);
374
+ border-block-start-width: 1px;
375
+ }
376
+
377
+ tbody tr:nth-child(even) {
378
+ background: color-mix(in oklab, var(--mote-surface) 55%, transparent);
379
+ }
380
+
381
+ /* ---------------------------------------------------------------------------
382
+ * 8. Forms & buttons
383
+ * ------------------------------------------------------------------------- */
384
+
385
+ label {
386
+ display: inline-block;
387
+ margin-block-end: 0.4rem;
388
+ font-weight: 500;
389
+ }
390
+
391
+ fieldset {
392
+ padding: 1rem 1.2rem;
393
+ border: var(--mote-border-width) solid var(--mote-border);
394
+ border-radius: var(--mote-radius);
395
+ }
396
+
397
+ legend { padding-inline: 0.4rem; }
398
+
399
+ input, select, textarea {
400
+ font: inherit;
401
+ color: inherit;
402
+ padding: 0.5rem 0.75rem;
403
+ background: var(--mote-surface);
404
+ border: var(--mote-border-width) solid var(--mote-border);
405
+ border-radius: var(--mote-radius-sm);
406
+ transition: border-color var(--mote-transition), box-shadow var(--mote-transition);
407
+ }
408
+
409
+ input:hover, select:hover, textarea:hover {
410
+ border-color: var(--mote-border-strong);
411
+ }
412
+
413
+ input:focus-visible, select:focus-visible, textarea:focus-visible {
414
+ border-color: color-mix(in oklab, var(--mote-accent), var(--mote-border) 30%);
415
+ box-shadow: var(--mote-focus-ring);
416
+ }
417
+
418
+ textarea {
419
+ min-height: 8rem;
420
+ resize: vertical;
421
+ }
422
+
423
+ select {
424
+ appearance: none;
425
+ padding-inline-end: 2.4rem;
426
+ background-image: var(--mote-chevron);
427
+ background-repeat: no-repeat;
428
+ background-position: right 0.8rem center;
429
+ }
430
+
431
+ [type="checkbox"], [type="radio"] {
432
+ width: 1em;
433
+ height: 1em;
434
+ padding: 0;
435
+ accent-color: var(--mote-accent);
436
+ vertical-align: baseline;
437
+ }
438
+
439
+ [type="color"] {
440
+ padding: 0.2rem;
441
+ height: 2.4rem;
442
+ }
443
+
444
+ [type="file"] {
445
+ padding: 0.4rem;
446
+ background: none;
447
+ border-style: dashed;
448
+ }
449
+
450
+ ::placeholder {
451
+ color: var(--mote-muted);
452
+ opacity: 0.8;
453
+ }
454
+
455
+ :disabled {
456
+ opacity: 0.55;
457
+ cursor: not-allowed;
458
+ }
459
+
460
+ :user-invalid {
461
+ border-color: var(--mote-error);
462
+ }
463
+
464
+ button, [type="button"], [type="submit"], [type="reset"] {
465
+ font: inherit;
466
+ font-weight: 500;
467
+ color: var(--mote-on-accent);
468
+ padding: 0.5rem 1.1rem;
469
+ background: var(--mote-accent);
470
+ border: var(--mote-border-width) solid transparent;
471
+ border-radius: var(--mote-radius-sm);
472
+ cursor: pointer;
473
+ transition: background var(--mote-transition), border-color var(--mote-transition),
474
+ box-shadow var(--mote-transition), transform var(--mote-transition);
475
+ }
476
+
477
+ button:hover, [type="button"]:hover, [type="submit"]:hover, [type="reset"]:hover {
478
+ background: var(--mote-accent-deep);
479
+ box-shadow: var(--mote-shadow-sm);
480
+ }
481
+
482
+ button:active, [type="button"]:active, [type="submit"]:active, [type="reset"]:active {
483
+ transform: translateY(1px);
484
+ }
485
+
486
+ /* ---------------------------------------------------------------------------
487
+ * 9. Media
488
+ * ------------------------------------------------------------------------- */
489
+
490
+ img, video, canvas, svg, iframe {
491
+ max-width: 100%;
492
+ height: auto;
493
+ }
494
+
495
+ img, video {
496
+ border-radius: var(--mote-radius);
497
+ }
498
+
499
+ audio {
500
+ width: 100%;
501
+ }
502
+
503
+ iframe {
504
+ border: var(--mote-border-width) solid var(--mote-border);
505
+ border-radius: var(--mote-radius);
506
+ }
507
+
508
+ /* ---------------------------------------------------------------------------
509
+ * 10. Interactive elements
510
+ * ------------------------------------------------------------------------- */
511
+
512
+ details {
513
+ padding: 0.8rem 1.2rem;
514
+ border: var(--mote-border-width) solid var(--mote-border);
515
+ border-radius: var(--mote-radius);
516
+ background: color-mix(in oklab, var(--mote-surface) 45%, transparent);
517
+ }
518
+
519
+ details[open] {
520
+ background: var(--mote-surface);
521
+ }
522
+
523
+ summary {
524
+ cursor: pointer;
525
+ font-weight: 500;
526
+ list-style: revert;
527
+ }
528
+
529
+ summary::marker {
530
+ color: var(--mote-status, var(--mote-accent));
531
+ }
532
+
533
+ dialog {
534
+ padding: 1.6rem 2rem;
535
+ border: var(--mote-border-width) solid var(--mote-border);
536
+ border-radius: var(--mote-radius-lg);
537
+ background: var(--mote-bg);
538
+ color: var(--mote-fg);
539
+ box-shadow: var(--mote-shadow);
540
+ }
541
+
542
+ dialog::backdrop {
543
+ background: color-mix(in oklab, var(--mote-fg) 32%, transparent);
544
+ backdrop-filter: blur(2px);
545
+ }
546
+
547
+ progress, meter {
548
+ height: 0.9rem;
549
+ accent-color: var(--mote-status, var(--mote-accent));
550
+ }
551
+
552
+ /* Semantic feedback helpers (pure HTML, works with <p>, <div>, anything) */
553
+ [role="alert"] { color: var(--mote-error); }
554
+ [role="status"] { color: var(--mote-info); }
555
+
556
+ /* ---------------------------------------------------------------------------
557
+ * 11. Opt-in helpers
558
+ *
559
+ * mote is classless by design; these are the only three escape hatches.
560
+ * ------------------------------------------------------------------------- */
561
+
562
+ /* Centered page wrapper: <main class="mote-container"> */
563
+ .mote-container {
564
+ width: min(100% - 3rem, 70rem);
565
+ margin-inline: auto;
566
+ }
567
+
568
+ /* Dimmed text: <small class="mote-muted"> */
569
+ .mote-muted { color: var(--mote-muted); }
570
+
571
+ /* ---------------------------------------------------------------------------
572
+ * 11. Status classes
573
+ *
574
+ * Statuses are pure token assignments — they set variables, never visual
575
+ * properties. Base classes (notes, badges, toasts) consume the tokens and
576
+ * fall back to the accent color when no status class is present.
577
+ *
578
+ * <div class="mote-note mote-success">…</div>
579
+ * <span class="mote-badge mote-error">404</span>
580
+ *
581
+ * Restyle a status once and every component follows; or set the variables
582
+ * inline: style="--mote-status: rebeccapurple".
583
+ * ------------------------------------------------------------------------- */
584
+
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); }
589
+
590
+ /* .mote-muted does double duty: dimmed text where nothing consumes the
591
+ tokens, and a neutral status where something does. */
592
+ .mote-muted {
593
+ color: var(--mote-muted);
594
+ --mote-status: var(--mote-muted);
595
+ --mote-status-soft: color-mix(in oklab, var(--mote-fg) 7%, transparent);
596
+ }
597
+
598
+ /* Themed message surfaces — base shape only; status via the token classes */
599
+ .mote-note {
600
+ padding: 0.8rem 1.1rem;
601
+ border-radius: var(--mote-radius-sm);
602
+ color: var(--mote-status, var(--mote-accent-deep));
603
+ background: var(--mote-status-soft, var(--mote-accent-soft));
604
+ }
605
+
606
+ /* ---------------------------------------------------------------------------
607
+ * 12. Motion preferences & print
608
+ * ------------------------------------------------------------------------- */
609
+
610
+ @media (prefers-reduced-motion: reduce) {
611
+ *, *::before, *::after {
612
+ transition-duration: 0.01ms !important;
613
+ animation-duration: 0.01ms !important;
614
+ animation-iteration-count: 1 !important;
615
+ scroll-behavior: auto !important;
616
+ }
617
+ }
618
+
619
+ @media print {
620
+ body {
621
+ background: #fff;
622
+ color: #000;
623
+ }
624
+ a { color: #000; }
625
+ details { border: none; }
626
+ dialog { box-shadow: none; }
627
+ }
package/package.json CHANGED
@@ -1,6 +1,30 @@
1
1
  {
2
2
  "name": "mote.css",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.1",
4
+ "description": "A mote of styling for plain HTML — tiny, semantic, classless CSS with zero dependencies and no build step.",
5
+ "style": "mote.css",
6
+ "files": [
7
+ "mote.css",
8
+ "mote.components.css",
9
+ "README.md",
10
+ "LICENSE",
11
+ "CHANGELOG.md"
12
+ ],
13
+ "keywords": [
14
+ "css",
15
+ "classless",
16
+ "semantic",
17
+ "framework-agnostic",
18
+ "dark-mode",
19
+ "custom-properties",
20
+ "design-system",
21
+ "cozy"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://gitlab.com/abmach/mote.git"
26
+ },
27
+ "author": "Arthur Biazon Machado",
28
+ "license": "MIT",
29
+ "homepage": "https://gitlab.com/abmach/mote#readme"
30
+ }