asw-v01: modern CSS reset, fix README token docs, add watch.py live rebuild
- Rewrite 00-reset.css with :where()-wrapped zero-specificity rules - Fix README token docs (--asw-* was incorrect, actual tokens use bare --*) - Add watch.py (pyinotify-based auto-rebuild of dist/asw.css on src/ changes)
This commit is contained in:
parent
9651dd5515
commit
9fd9096f0a
5 changed files with 290 additions and 82 deletions
30
README.md
30
README.md
|
|
@ -142,25 +142,21 @@ Metadata selectors for machine-generated content.
|
||||||
|
|
||||||
### CSS custom properties
|
### CSS custom properties
|
||||||
|
|
||||||
All visual tokens are exposed as `--asw-*` custom properties on `:root`:
|
ASW defines a full set of semantic custom properties on `:root`. Key groups:
|
||||||
|
|
||||||
```css
|
| Group | Examples |
|
||||||
--asw-font-sans: 'Segoe UI', system-ui, sans-serif;
|
|---|---|
|
||||||
--asw-font-mono: 'Fira Code', 'Cascadia Code', monospace;
|
| Surfaces | `--surface`, `--surface-1`, `--surface-card`, `--surface-hover` |
|
||||||
--asw-text-1: oklch(15% 0.01 260);
|
| Text | `--text`, `--text-2`, `--text-3`, `--text-dim` |
|
||||||
--asw-text-2: oklch(35% 0.02 260);
|
| Accent | `--accent`, `--accent-hover`, `--on-accent` |
|
||||||
--asw-surface-1: oklch(98% 0.003 260);
|
| Links | `--link`, `--link-hover`, `--link-underline` |
|
||||||
--asw-surface-2: oklch(95% 0.005 260);
|
| Borders | `--border`, `--border-subtle` |
|
||||||
--asw-surface-3: oklch(90% 0.01 260);
|
| Spacing | `--space-1` through `--space-8` |
|
||||||
--asw-brand: oklch(55% 0.25 260);
|
| Typography | `--font-heading`, `--font-mono`, `--font-ui`, `--text-base` |
|
||||||
--asw-link: oklch(45% 0.20 260);
|
| Layout | `--width-prose`, `--sidebar-min`, `--grid-gap` |
|
||||||
--asw-success: oklch(55% 0.20 145);
|
| States | `--ok`, `--warn`, `--error`, `--info`, `--blocked` |
|
||||||
--asw-warning: oklch(65% 0.20 85);
|
|
||||||
--asw-error: oklch(50% 0.25 25);
|
|
||||||
--asw-radius: 6px;
|
|
||||||
```
|
|
||||||
|
|
||||||
See `src/tokens.css` for the full token table.
|
See `src/tokens.css` for the full table (~70 custom properties).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
154
dist/asw.css
vendored
154
dist/asw.css
vendored
|
|
@ -606,26 +606,39 @@
|
||||||
@media (--xl-n-above) { html { font-size: 106%; } }
|
@media (--xl-n-above) { html { font-size: 106%; } }
|
||||||
@media (--xxl-n-above) { html { font-size: 109%; } }
|
@media (--xxl-n-above) { html { font-size: 109%; } }
|
||||||
/**
|
/**
|
||||||
* 00-reset.css
|
* 00-reset.css — Modern CSS reset
|
||||||
* CSS reset and normalization
|
* Normalizes user-agent styles across browsers.
|
||||||
* Ported from: Pico CSS v2.1.1
|
*
|
||||||
|
* Based on: Josh W. Comeau (2021-2025), David Bushell (2025),
|
||||||
|
* Andy Bell / Piccalilli (2023), Elad Shechter (2021).
|
||||||
|
*
|
||||||
|
* All selectors wrapped in :where() to zero specificity —
|
||||||
|
* any ASW layer file can override with a single element selector.
|
||||||
|
*
|
||||||
|
* Design-agnostic. No ASW tokens referenced.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Box-sizing reset */
|
/* ── Box sizing ────────────────────────────────────────────────── */
|
||||||
*,
|
|
||||||
*::before,
|
:where(*, *::before, *::after) {
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::before,
|
/* ── Remove default margin (except dialog which uses margin: auto) ── */
|
||||||
::after {
|
|
||||||
text-decoration: inherit;
|
:where(*:not(dialog)) {
|
||||||
vertical-align: inherit;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Document */
|
/* ── Remove default list styling ────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(ul, ol) {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Document root ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
:where(:root) {
|
:where(:root) {
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
@ -633,35 +646,112 @@
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
|
hanging-punctuation: first allow-end last;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Root font-size — 100% default, responsive scaling in 01-tokens.css */
|
/* ── Body baseline ──────────────────────────────────────────────── */
|
||||||
html {
|
|
||||||
font-size: 100%;
|
:where(body) {
|
||||||
|
min-block-size: 100svb;
|
||||||
|
min-inline-size: 300px;
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body */
|
/* ── Animations and transitions ─────────────────────────────────── */
|
||||||
body {
|
|
||||||
width: 100%;
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
margin: 0;
|
:where(html) {
|
||||||
padding: 0;
|
interpolate-size: allow-keywords;
|
||||||
font-size: var(--text-base); /* 1rem — inherits html responsive scaling */
|
}
|
||||||
font-family: var(--font-ui);
|
|
||||||
background-color: var(--surface);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prose font — same neo-grotesque stack, consistent across all contexts */
|
/* ── Media elements ─────────────────────────────────────────────── */
|
||||||
/* article and [data-layout="prose"] inherit body font — no override needed */
|
|
||||||
|
|
||||||
/* Article as main content landmark */
|
:where(img, picture, video, canvas, svg) {
|
||||||
article[role="main"] {
|
|
||||||
display: block;
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nested lists */
|
/* ── Form controls ──────────────────────────────────────────────── */
|
||||||
:where(dl, ol, ul) :where(dl, ol, ul) {
|
|
||||||
margin: 0;
|
:where(input, button, textarea, select) {
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(button) {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(textarea) {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Anchor reset ───────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(a) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Typography overflow ────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(p, h1, h2, h3, h4, h5, h6, li, figcaption, blockquote) {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Line wrapping quality ──────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(p, li, figcaption, blockquote) {
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(h1, h2, h3, h4, h5, h6) {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Horizontal rule ────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(hr) {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid currentColor;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Table ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(table) {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Code / pre ─────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(pre) {
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Focus visible ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(:focus-visible) {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Remove default fieldset border ──────────────────────────────── */
|
||||||
|
|
||||||
|
:where(fieldset) {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 02-semantic.css
|
* 02-semantic.css
|
||||||
|
|
|
||||||
2
dist/asw.min.css
vendored
2
dist/asw.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,24 +1,37 @@
|
||||||
/**
|
/**
|
||||||
* 00-reset.css
|
* 00-reset.css — Modern CSS reset
|
||||||
* CSS reset and normalization
|
* Normalizes user-agent styles across browsers.
|
||||||
* Ported from: Pico CSS v2.1.1
|
*
|
||||||
|
* Based on: Josh W. Comeau (2021-2025), David Bushell (2025),
|
||||||
|
* Andy Bell / Piccalilli (2023), Elad Shechter (2021).
|
||||||
|
*
|
||||||
|
* All selectors wrapped in :where() to zero specificity —
|
||||||
|
* any ASW layer file can override with a single element selector.
|
||||||
|
*
|
||||||
|
* Design-agnostic. No ASW tokens referenced.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Box-sizing reset */
|
/* ── Box sizing ────────────────────────────────────────────────── */
|
||||||
*,
|
|
||||||
*::before,
|
:where(*, *::before, *::after) {
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::before,
|
/* ── Remove default margin (except dialog which uses margin: auto) ── */
|
||||||
::after {
|
|
||||||
text-decoration: inherit;
|
:where(*:not(dialog)) {
|
||||||
vertical-align: inherit;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Document */
|
/* ── Remove default list styling ────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(ul, ol) {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Document root ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
:where(:root) {
|
:where(:root) {
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
@ -26,33 +39,110 @@
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
|
hanging-punctuation: first allow-end last;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Root font-size — 100% default, responsive scaling in 01-tokens.css */
|
/* ── Body baseline ──────────────────────────────────────────────── */
|
||||||
html {
|
|
||||||
font-size: 100%;
|
:where(body) {
|
||||||
|
min-block-size: 100svb;
|
||||||
|
min-inline-size: 300px;
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body */
|
/* ── Animations and transitions ─────────────────────────────────── */
|
||||||
body {
|
|
||||||
width: 100%;
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
margin: 0;
|
:where(html) {
|
||||||
padding: 0;
|
interpolate-size: allow-keywords;
|
||||||
font-size: var(--text-base); /* 1rem — inherits html responsive scaling */
|
}
|
||||||
font-family: var(--font-ui);
|
|
||||||
background-color: var(--surface);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prose font — same neo-grotesque stack, consistent across all contexts */
|
/* ── Media elements ─────────────────────────────────────────────── */
|
||||||
/* article and [data-layout="prose"] inherit body font — no override needed */
|
|
||||||
|
|
||||||
/* Article as main content landmark */
|
:where(img, picture, video, canvas, svg) {
|
||||||
article[role="main"] {
|
|
||||||
display: block;
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nested lists */
|
/* ── Form controls ──────────────────────────────────────────────── */
|
||||||
:where(dl, ol, ul) :where(dl, ol, ul) {
|
|
||||||
margin: 0;
|
:where(input, button, textarea, select) {
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(button) {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(textarea) {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Anchor reset ───────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(a) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Typography overflow ────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(p, h1, h2, h3, h4, h5, h6, li, figcaption, blockquote) {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Line wrapping quality ──────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(p, li, figcaption, blockquote) {
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(h1, h2, h3, h4, h5, h6) {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Horizontal rule ────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(hr) {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid currentColor;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Table ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(table) {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Code / pre ─────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(pre) {
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Focus visible ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
:where(:focus-visible) {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Remove default fieldset border ──────────────────────────────── */
|
||||||
|
|
||||||
|
:where(fieldset) {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
watch.py
Normal file
32
watch.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Watch src/ for changes and rebuild dist/asw.css."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import pyinotify
|
||||||
|
|
||||||
|
WATCH_DIR = os.path.expanduser("~/projects/asw/src")
|
||||||
|
BUILD_CMD = ["npm", "run", "build"]
|
||||||
|
|
||||||
|
class RebuildHandler(pyinotify.ProcessEvent):
|
||||||
|
def process_default(self, event):
|
||||||
|
if event.name and event.name.endswith(".css"):
|
||||||
|
print(f"[watch] {event.name} changed → rebuilding...")
|
||||||
|
result = subprocess.run(BUILD_CMD, cwd=os.path.dirname(WATCH_DIR),
|
||||||
|
capture_output=True, text=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("[watch] Build OK")
|
||||||
|
else:
|
||||||
|
print(f"[watch] Build FAILED:\n{result.stderr}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
wm = pyinotify.WatchManager()
|
||||||
|
mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE | pyinotify.IN_DELETE
|
||||||
|
handler = RebuildHandler()
|
||||||
|
notifier = pyinotify.Notifier(wm, handler)
|
||||||
|
wm.add_watch(WATCH_DIR, mask, rec=True)
|
||||||
|
print(f"[watch] Watching {WATCH_DIR} for changes. Ctrl+C to stop.")
|
||||||
|
notifier.loop()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue