Compare commits

...

6 commits

Author SHA1 Message Date
910b0e42a6
refactor: restructure repo into src/ site/ dist/ vendor/ packs/
Separate framework source from website:
- src/layers/ + src/main.css: CSS framework source (was assets/css/)
- site/: Hugo website (content/, layouts/, hugo.toml)
- dist/: built output (asw.css, asw.min.css)
- vendor/open-props/: vendored dependency with version tracking
- Hugo module mounts: dist/ → static, site runs from site/

Build: hugo --source site/ passes (105 pages).
npm run build produces dist/asw.css.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:12:42 +02:00
5bf233348d
feat: add OpenSpec specs and changes for ASW restructure
Specs: repo-structure, 10 framework layer specs, packs, site.
Changes: repo-restructure (10 tasks), css-refactor (12 tasks),
legacy-import (proposal + triage categories).

Supersede docs/css-refactor-plan.md in favor of OpenSpec change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 14:58:39 +02:00
ecfbed0374
feat: add page content type for site infrastructure
Distinct from articles/essays/notes — pages have no publish lifecycle,
no date, no TOC, no prev/next. <main> landmark, not <article>.
Groundwork: layouts/page/single.html + content/pages/about.md stub.
2026-04-11 13:37:52 +02:00
15a6db9c0e
refactor: rename content types to semantic taxonomy
- vault → notes (PKM-exported content)
- posts → articles (short-form, no TOC)
- papers → essays (long-form, with TOC)
- type: post → type: article (posts are just short articles)
- layouts/paper → layouts/essay
- 08a-paper.css → 08a-essay.css
- CSS: fix redundant li resets, remove role="main" from article,
  replace <small> prev/next labels, add console layout
- Update hugo.toml menus, internal URLs, front matter throughout
- Add docs/context.md, docs/css-refactor-plan.md
2026-04-11 13:36:58 +02:00
1408a52e8b
docs: add css/html cleanup todo note
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 02:18:12 +02:00
b42e4942fa
css: move sidebar/TOC component styles out of layout, replace <small> with <h3>
- sidebar and TOC typography/colors moved from 08-layout.css to 03-components.css
- nav[data-nav="sidebar"] and aside[data-toc] layout-only rules remain in 08-layout.css
- <small> section labels replaced with <h3> in sidebar and TOC (semantic + accessible)
- dead selectors removed: nav[data-nav="sidebar"] nav a → nav[data-nav="sidebar"] a,
  nav[data-nav="toc"] (Hugo never outputs data-nav="toc")
- webkit scrollbar pseudo-elements removed (scrollbar-width/color sufficient)
- sidebar/TOC sticky top values split: sidebar top space-4, TOC top space-8 + padding-top
- max-height magic number (--size-px-10) replaced with token expression
- layer convention established: 03=component identity, 04=modifiers, 08=placement only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 02:14:42 +02:00
96 changed files with 2175 additions and 338 deletions

13
.gitignore vendored
View file

@ -1,5 +1,14 @@
node_modules/
.hugo_build.lock
resources/_gen/
public/
build.log
postcss.dev.config.js
dev.sh
# Hugo output and cache (all under site/)
site/public/
site/resources/
site/hugo_stats.json
# dev.sh generated output (under site/)
site/static/css/
site/static/vendor/

File diff suppressed because one or more lines are too long

1
dist/asw.css vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/asw.min.css vendored Normal file

File diff suppressed because one or more lines are too long

149
docs/context.md Normal file
View file

@ -0,0 +1,149 @@
# ASW — Agentic Semantic Web: Context Document
For agent sessions working in this repo. Read this before touching any CSS or layout file.
---
## What ASW is
A CSS framework built for agents, not humans.
LLMs generating web pages face a specific failure mode: given a class-based framework (Bootstrap, Tailwind), they must memorize presentation strings — `navbar-expand-lg`, `bg-gray-900`, `flex items-center justify-between`. These strings are arbitrary, version-dependent, and hallucination-prone. The agent spends cognition on presentation, not content.
ASW's answer: **don't give agents a presentation vocabulary. Give them a semantic one.**
Semantic HTML is what LLMs already produce naturally. `<nav>`, `<article>`, `<aside>`, `<details>` — these express structure through language. ASW makes that HTML look correct without the agent touching CSS.
**The agent directive:**
> Write semantic HTML. Use `data-*` attributes for vault-native concepts. Never write `style=`. Never invent classes. If ASW can't express it, document the gap.
---
## The core mechanism
Two layers of expressiveness:
**1. Semantic HTML elements** — styled directly, no classes needed:
```html
<nav> → top navigation bar
<article> → content card or post
<aside> → sidebar or TOC
<details> → accordion
<dialog> → modal
```
**2. `data-*` attributes** — for concepts HTML has no element for:
```html
data-task="done|todo|blocked|wip"
data-callout="note|tip|warning|error"
data-status="active|sleeping|blocked"
data-wikilink → vault note link
data-layout="docs|grid-2|grid-3|console"
data-toc → in-page table of contents
data-role="breadcrumb|steps|accordion|card"
```
The vocabulary is semantic, not stylistic. `data-task="blocked"` expresses meaning — the CSS handles appearance.
---
## Two repos, one framework
### `agentic-semantic-web/` (legacy)
The original standalone version. Single built file (`dist/agentic.css`), no dependencies, `--asw-*` prefixed variables. Proves the concept. Still deployed on Trentuna. Architecture doc and philosophy live here.
### `asw/` (current — this repo)
The Hugo-based rewrite. Same philosophy, rebuilt on Open Props as primitive token source. Multi-layer CSS architecture. Serves as both the framework source and its own documentation site.
The two diverge in:
- Variable naming: legacy uses `--asw-*` prefix, current uses unprefixed semantic aliases (`--text`, `--surface`, `--accent`)
- Token source: legacy has its own scale, current builds on Open Props
- Delivery: legacy is a single file, current is a Hugo site with PostCSS build
---
## Current architecture (`asw/`)
### Hugo site
```
content/
docs/ → framework documentation (type: docs)
articles/ → short-form writing — no TOC (type: article)
essays/ → long-form writing — TOC, description (type: essay)
notes/ → exported PKM vault notes (type: notes)
layouts/
docs/ → three-column docs layout
console/ → docs variant: sidebar flush to viewport edge
notes/ → notes/vault content layout
essay/ → long-form essay layout (TOC)
```
### CSS layer stack
```
assets/css/
main.css → entry point, @import chain
layers/
00-reset.css → normalize + box-sizing
01-asw.css → design tokens (Open Props aliases + ASW additions)
02-semantic.css → typography, prose, syntax highlighting base
03-components.css → landmarks + forms + components (being split — see refactor plan)
04-data-attrs.css → all data-* attribute patterns
05-utilities.css → text helpers, visibility, accessibility
06-charts.css → Charts.css-inspired data visualization
07-chroma.css → Hugo Chroma syntax highlighting
08-layout.css → layout systems (docs, console, grids, prose)
08a-essay.css → essay layout variant
09-landing.css → landing page styles
```
### Token system
Open Props provides primitive scales (`--color-1..16`, `--size-1..15`, `--font-size-0..8`).
`01-asw.css` aliases these to semantic names:
```css
--surface: var(--color-14) /* not --color-14 directly */
--text: var(--color-6)
--accent: var(--color-8)
--space-4: var(--size-3)
```
**Rule:** layers 0208 must only reference semantic aliases, never Open Props primitives directly.
---
## Active work
### CSS layer refactor
`docs/css-refactor-plan.md` — full step-by-step plan.
The current `03-components.css` is a mixed bag: landmark styling (nav, article, footer), form elements, and true components (dialog, accordion) are all in one file. The plan splits it into purpose-specific files and fixes ~40 Open Props primitive leaks across the codebase.
**Do not start any CSS work without reading the refactor plan first.**
### Layout development
Two layouts exist: `docs` (standard three-column) and `console` (sidebar flush to viewport edge). The console layout is a prototype — no Hugo content type uses it in production yet.
### Pending CSS cleanup (`docs/css-html-cleanup-todo.md`)
- Hardcoded `"On this page"` strings need i18n before adding a second language
---
## Design principles
**Engine-agnostic** — Templates are prototyped in Hugo but must be portable to Flask/Jinja2. Hugo-specific features (render hooks, shortcodes) are acceptable prototyping tools, not load-bearing design. Template logic should express *what*, not *how*.
**No CSS classes on content** — The framework uses element selectors and `data-*` attributes. Class selectors (`[data-nav="sidebar"]`, `[data-role="breadcrumb"]`) are allowed for disambiguation but never for presentation.
**Semantic token naming** — Aliases express role, not value. `--surface` not `--gray-dark-3`. `--weight-medium` not `--font-weight-5`.
**Agent-first means sparse vocabulary** — Every `data-*` attribute added to the vocabulary is a cognitive load on every agent using the framework. Add only what cannot be expressed by existing semantic HTML.
---
## Lineage
| Source | What was taken |
|--------|----------------|
| Pico CSS | Component patterns (buttons, forms) — ported and modernized |
| Open Props | Primitive token scales — used as foundation, not bundled |
| Charts.css | `data-*` attribute pattern for extending HTML vocabulary |
ASW is not built on any of these. It learned from them.

View file

@ -0,0 +1,17 @@
# CSS / HTML cleanup — loose ends
Found during sidebar/TOC refactor session (2026-04-11).
## Redundant resets
- `nav[data-nav="sidebar"] ul li { margin: 0; padding: 0 }` — browser default for `<li>` already has no margin/padding. No-op, remove.
- `aside[data-toc] nav ul li { margin: 0; padding: 0 }` — same.
## Semantic HTML
- `<small>← Previous</small>` and `<small>Next →</small>` in prev/next footer (`docs/single.html`, `vault/single.html`) — `<small>` is fine print, not a directional label. Replace with something appropriate.
- `role="main"` on `<article>` in `docs/single.html` — redundant and misleading. `<article>` does not map to the `main` landmark; `<main>` does. Remove the attribute.
## i18n
- `"On this page"` is hardcoded in `docs/single.html` and `vault/single.html`. Move to Hugo i18n strings or a site param before adding any second language.

223
docs/css-refactor-plan.md Normal file
View file

@ -0,0 +1,223 @@
# CSS Layer Refactor Plan
> **Superseded by:** `openspec/changes/css-refactor/` — this document is kept as historical reference. Agents should read the OpenSpec change tasks, not this file.
**Project:** ASW — Agentic Semantic Web
**Goal:** Restructure CSS layers to clean architecture, alias all Open Props primitives through `01-tokens.css`, redistribute misplaced content, remove unused values.
**Method:** One atomic step per agent session. Build check after every step. No step touches more than two files.
---
## Current state
```
assets/css/
main.css ← entry point, @import chain
layers/
00-reset.css
01-asw.css ← tokens + editorial rules (mixed)
02-semantic.css ← typography + prose styles
03-components.css ← landmarks + forms + components (mixed)
04-data-attrs.css ← data-* attribute patterns
05-utilities.css ← utility helpers
06-charts.css ← charts (uses --size-N primitives directly)
07-chroma.css ← syntax highlighting
08-layout.css ← layout systems
08a-essay.css ← paper layout variant
09-landing.css ← landing page styles
```
## Target state
```
assets/css/
main.css ← updated import chain
layers/
00-reset.css (unchanged)
01-tokens.css ← renamed from 01-asw.css, pure :root only, no rules
02-typography.css ← renamed from 02-semantic.css, prose + heading defaults
03-landmarks.css ← NEW: nav, article, aside, section, footer, hgroup
04-forms.css ← NEW: input, select, textarea, button
05-components.css ← slimmed 03-components.css: dialog, accordion, breadcrumb, steps
06-navigation.css ← NEW: sidebar nav, TOC (aside[data-toc])
07-data-attrs.css ← renamed from 04-data-attrs.css
08-utilities.css ← renamed from 05-utilities.css
09-charts.css ← renamed from 06-charts.css, primitives fixed
10-chroma.css ← renamed from 07-chroma.css
11-layout.css ← renamed from 08-layout.css + 08a-essay.css merged
12-landing.css ← renamed from 09-landing.css
```
---
## Primitive leak inventory
All Open Props primitives used directly in layers (must be aliased through 01-tokens.css):
### `--font-weight-N` (most common leak)
| Primitive | Semantic alias to add | Used in |
|---|---|---|
| `--font-weight-4` | `--weight-normal` | 03, 02, 08 |
| `--font-weight-5` | `--weight-medium` | 03, 02, 08 |
| `--font-weight-6` | `--weight-semibold` | 03 |
| `--font-weight-7` | `--weight-bold` | 02, 08, 09 |
### `--size-N` / `--size-px-N`
| Primitive | Alias | Notes |
|---|---|---|
| `--size-1` | `--space-1` (exists) | 06-charts |
| `--size-2` | `--space-2` (exists) | 06-charts |
| `--size-3` | `--space-4` (exists) | 03, 06-charts, 07-chroma |
| `--size-4` | needs `--space-5a: 1.25rem` | 03-components article padding |
| `--size-px-12` | `--dropdown-min-width` | 03-components nav dropdown |
### `--shadow-N`
| Primitive | Alias to add |
|---|---|
| `--shadow-2` | `--shadow-dropdown` |
| `--shadow-4` | `--shadow-modal` |
### `--border-size-2`
| Primitive | Alias to add |
|---|---|
| `--border-size-2` | `--focus-ring-width` |
---
## Step-by-step tasks
Each step = one agent session. Read only the files listed. Build after every step.
### STEP 1 — Add missing aliases to `01-asw.css` ✅ prerequisites: none
**Files:** `layers/01-asw.css` only
**Action:** Add to `:root {}`:
```css
/* Font weights */
--weight-normal: var(--font-weight-4);
--weight-medium: var(--font-weight-5);
--weight-semibold: var(--font-weight-6);
--weight-bold: var(--font-weight-7);
/* Shadows */
--shadow-dropdown: var(--shadow-2);
--shadow-modal: var(--shadow-4);
/* Focus ring */
--focus-ring-width: var(--border-size-2);
/* Dropdown */
--dropdown-min-width: var(--size-px-12);
/* Gap in spacing scale */
--space-5a: 1.25rem; /* between --space-4 (1rem) and --space-5 (1.5rem) */
```
**Verify:** `hugo server` builds without error. No visual change.
---
### STEP 2 — Extract editorial rules out of `01-asw.css` ✅ prerequisites: Step 1
**Files:** `layers/01-asw.css`, `layers/08-layout.css`
**Action:**
- Remove from `01-asw.css` the EDITORIAL DEFAULTS section (last ~10 lines: `body > nav` padding rule + `[data-layout="docs"] > article` max-width rule)
- Move `body > nav { padding-inline: ... }` to `03-components.css` under the Navigation section
- Move `[data-layout="docs"] > article { max-width: var(--width-prose) }` to `08-layout.css` under the docs layout section
**Verify:** Build passes. Nav centering and docs prose width unchanged visually.
---
### STEP 3 — Rename `01-asw.css``01-tokens.css` ✅ prerequisites: Step 2
**Files:** `layers/01-asw.css`, `main.css`
**Action:** Rename file, update `@import` in `main.css`.
**Verify:** Build passes.
---
### STEP 4 — Fix primitive leaks in `03-components.css` ✅ prerequisites: Step 1
**Files:** `layers/03-components.css` only
**Action:** Replace all primitive references with aliases:
- `var(--font-weight-4)``var(--weight-normal)`
- `var(--font-weight-5)``var(--weight-medium)` (including the fallback `, 500` variant)
- `var(--font-weight-6)``var(--weight-semibold)`
- `var(--size-3) var(--size-4)``var(--space-4) var(--space-5a)` (article padding, line 455)
- `var(--size-px-12)``var(--dropdown-min-width)`
- `var(--shadow-2)``var(--shadow-dropdown)`
- `var(--shadow-4)``var(--shadow-modal)`
- `var(--border-size-2)``var(--focus-ring-width)` (both occurrences)
**Verify:** Build passes. No visual change.
---
### STEP 5 — Fix primitive leaks in `06-charts.css` ✅ prerequisites: Step 1
**Files:** `layers/06-charts.css` only
**Action:** Replace all `--size-1/2/3` with `--space-1/2/4` respectively.
**Verify:** Build passes. Charts render unchanged.
---
### STEP 6 — Fix primitive leaks in remaining files ✅ prerequisites: Step 1
**Files:** `layers/02-semantic.css`, `layers/07-chroma.css`, `layers/08-layout.css`, `layers/09-landing.css` — one at a time
**Action:** Same find-replace for `--font-weight-N`, `--size-N` primitives.
**Verify:** Build passes after each file.
---
### STEP 7 — Create `03-landmarks.css` and extract from `03-components.css` ✅ prerequisites: Step 4
**Files:** `layers/03-components.css`, new `layers/03-landmarks.css`, `main.css`
**Action:** Move these sections out of `03-components.css` into `03-landmarks.css`:
- `body > nav` (the global nav landmark, lines ~259442)
- `article` (lines ~444506)
- `dt`, `dd` (lines ~508529)
- `section + section`, `hgroup` (lines ~531559)
- `body > footer` (lines ~561572)
Add `@import "./layers/03-landmarks.css"` to `main.css` before `03-components.css`.
**Verify:** Build passes. All landmark styles render unchanged.
---
### STEP 8 — Create `04-forms.css` and extract from `03-components.css` ✅ prerequisites: Step 7
**Files:** `layers/03-components.css`, new `layers/04-forms.css`, `main.css`
**Action:** Move out of `03-components.css`:
- Buttons (lines ~1475)
- Form Elements (lines ~77258)
**Verify:** Build passes.
---
### STEP 9 — Create `06-navigation.css` and extract from `03-components.css` ✅ prerequisites: Step 8
**Files:** `layers/03-components.css`, new `layers/06-navigation.css`, `main.css`
**Action:** Move out of `03-components.css`:
- `nav[data-nav="sidebar"]` section
- `aside[data-toc]` section
**Verify:** Build passes. Sidebar and TOC render unchanged.
---
### STEP 10 — Rename remaining files + update `main.css` ✅ prerequisites: Steps 79
**Files:** all layer files + `main.css`
**Action:** Rename files to final numbers, merge `08a-essay.css` into `11-layout.css`, update all `@import` statements.
**Verify:** Build passes.
---
### STEP 11 — Audit unused tokens ✅ prerequisites: Step 10
**Method:** Tooling only — do not guess.
```bash
# Build the CSS, then grep for each token name
hugo --minify
grep -o 'var(--[^)]+)' public/**/*.css | sort | uniq > /tmp/used-tokens.txt
grep -o '\-\-[a-z][^:]*:' assets/css/layers/01-tokens.css | sort > /tmp/defined-tokens.txt
diff /tmp/defined-tokens.txt /tmp/used-tokens.txt
```
Review diff. Remove confirmed unused tokens from `01-tokens.css`.
**Verify:** Build passes. Spot-check 34 pages visually.
---
## Rules for all agents
1. Read only the files listed for your step — nothing else
2. Run build after every step before reporting done
3. Do not rename variables not listed in this plan
4. Do not touch `main.css` unless the step explicitly says to
5. Do not combine two steps in one session
6. If a primitive is found that isn't in the inventory above, add it to the plan doc and stop — do not fix it unilaterally

73
docs/template-h1-title.md Normal file
View file

@ -0,0 +1,73 @@
# Template Design: Handling the Markdown H1 / Front Matter Title Conflict
## The Problem
Standard markdown convention — Obsidian, agent-written files, generic `.md` — opens
with a level-1 heading as the document title:
```markdown
# My Note Title
Body text...
```
Hugo templates also render a title from front matter:
```html
<h1>{{ .Title }}</h1>
```
When both exist, the page gets two `<h1>` elements: one from the template,
one from the rendered markdown content. The content one also carries an
auto-generated `id` attribute from Hugo's heading anchor renderer.
## The Two Template Contracts
**Default (`_default/single.html`)** — bare markdown, minimal or no front matter.
The `# Title` in content IS the h1. The template header renders only metadata
(type, date, author, tags). No title rendered from front matter.
**Vault (`vault/single.html`)** — enriched front matter (`title`, `type`, `date`,
`author`, `tags`). Front matter `title` is authoritative. The `# Title` in content
is still present (markdown convention) but must be suppressed.
## The Fix: Engine-Agnostic Regex Strip
When a template owns the title (renders `<h1>{{ .Title }}</h1>` from front matter),
strip the first h1 from the rendered content before outputting it.
**Hugo:**
```
{{ replaceRE "<h1[^>]*>.*?</h1>" "" .Content 1 | safeHTML }}
```
**Jinja2 / Flask:**
```python
import re
content = re.sub(r'<h1[^>]*>.*?</h1>', '', content, count=1, flags=re.DOTALL)
```
**Nunjucks / Liquid / any engine:** equivalent string replace on the rendered HTML.
This is a string operation on already-rendered HTML, not a template-engine concept.
It ports to any engine without modification.
## Why Not Other Approaches
- **Author convention** (don't write `# Title` in vault files): breaks compatibility
with the entire markdown ecosystem.
- **Hugo render hooks** (`layouts/vault/_markup/render-heading.html`): Hugo-specific,
not portable.
- **CSS `display: none`**: h1 still exists in DOM — screen readers read it,
search engines index it. Semantically wrong.
## Engine-Agnostic Principle
ASW templates are prototyped in Hugo but must be portable to Flask/Jinja2 or
any other engine. Template logic should express *what*, not *how*:
- What: "strip h1 from content if front matter title is present"
- How: engine-specific implementation of the same string operation
Hugo-specific features (render hooks, shortcodes) are acceptable as prototyping
tools but should not become load-bearing parts of the template design.

View file

@ -1,26 +0,0 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<title>
{{- if .IsHome -}}
{{ .Site.Title }}
{{- else -}}
{{ .Title }} · {{ .Site.Title }}
{{- end -}}
</title>
{{- with .Description }}<meta name="description" content="{{ . }}">{{- end }}
{{- if not .Description }}{{- with .Site.Params.description }}<meta name="description" content="{{ . }}">{{- end }}{{- end }}
{{- /* ── Meta partials ─────────────────────────────────────────── */}}
{{- partial "meta/seo.html" . -}}
{{- partial "meta/og.html" . -}}
{{- partial "meta/ai-disclosure.html" . -}}
{{- partial "meta/json-ld.html" . -}}
{{- /* ── CSS — fingerprinted via Hugo Pipes ─────────────────────── */}}
{{ $css := resources.Get "css/asw-built.css" | minify | fingerprint }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{- range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{- end }}

View file

@ -0,0 +1,60 @@
# Proposal: CSS Layer Refactor
## Intent
Restructure CSS layers to clean architecture: alias all Open Props primitives through `01-tokens.css`, split the monolithic `03-components.css` into purpose-specific files, redistribute misplaced content, and rename files to match their actual responsibilities.
**Goal: each CSS file has a single clear purpose, and no file references Open Props primitives directly.**
This plan was originally documented in `docs/css-refactor-plan.md` and is imported here as an OpenSpec change for tracking and execution.
## Scope
### In Scope
- Add missing semantic aliases to token file (~10 new aliases)
- Extract editorial rules out of token file into their proper layers
- Rename `01-asw.css``01-tokens.css` (pure tokens, no rules)
- Fix all Open Props primitive leaks across all layers
- Split `03-components.css` into: `03-landmarks.css`, `04-forms.css`, slimmed `05-components.css`, `06-navigation.css`
- Rename all files to final numbering scheme
- Merge `08a-essay.css` into layout layer
- Audit and remove unused tokens
### Out of Scope
- New CSS features or components
- Layout changes
- Content changes
- Repo restructure (separate change, happens first)
### Current → Target Layer Map
```
CURRENT TARGET
═══════ ══════
00-reset.css 00-reset.css (unchanged)
01-asw.css ──▶ 01-tokens.css (pure :root, no rules)
02-semantic.css ──▶ 02-typography.css
03-components.css ──▶ 03-landmarks.css (nav, article, footer, hgroup)
──▶ 04-forms.css (inputs, buttons, selects)
──▶ 05-components.css (dialog, accordion, breadcrumb)
──▶ 06-navigation.css (sidebar, TOC)
04-data-attrs.css ──▶ 07-data-attrs.css
05-utilities.css ──▶ 08-utilities.css
06-charts.css ──▶ 09-charts.css
07-chroma.css ──▶ 10-chroma.css
08-layout.css + 08a-essay ──▶ 11-layout.css (merged)
09-landing.css ──▶ 12-landing.css
```
## Approach
One atomic step per session. Build check after every step. No step touches more than two files. Full plan in `docs/css-refactor-plan.md`.
## Prerequisites
- **Depends on:** `repo-restructure` (files must be in `src/layers/` first)
## Risks
- **Visual regression** — mitigated by build + visual check after each step
- **Specificity changes** — layer reordering could affect cascade. Mitigated by keeping relative order.

View file

@ -0,0 +1,94 @@
# Tasks: CSS Layer Refactor
Imported from `docs/css-refactor-plan.md`. Each step = one agent session.
**Prerequisite:** `repo-restructure` change must be complete. All files in `src/layers/`.
---
## Task 1 — Add missing aliases to token file
**Files:** `src/layers/01-asw.css` (pre-rename)
**Action:** Add semantic aliases for `--font-weight-N`, `--shadow-N`, `--border-size-2`, `--size-px-12`, and `--space-5a` gap.
**Acceptance:** Build passes. No visual change. New aliases exist in `:root`.
---
## Task 2 — Extract editorial rules from token file
**Files:** `src/layers/01-asw.css`, `src/layers/08-layout.css`
**Action:** Move `body > nav` padding rule to `03-components.css`. Move `[data-layout="docs"] > article` max-width to `08-layout.css`. Token file becomes pure `:root` only.
**Acceptance:** Build passes. Nav centering and docs prose width unchanged.
---
## Task 3 — Rename `01-asw.css``01-tokens.css`
**Files:** `src/layers/01-asw.css`, `src/main.css`
**Action:** Rename file, update `@import`.
**Acceptance:** Build passes.
---
## Task 4 — Fix primitive leaks in `03-components.css`
**Files:** `src/layers/03-components.css`
**Action:** Replace all `--font-weight-N`, `--size-N`, `--shadow-N`, `--border-size-N` with semantic aliases.
**Acceptance:** Build passes. No visual change.
---
## Task 5 — Fix primitive leaks in `06-charts.css`
**Files:** `src/layers/06-charts.css`
**Action:** Replace `--size-1/2/3` with `--space-1/2/4`.
**Acceptance:** Build passes. Charts render unchanged.
---
## Task 6 — Fix primitive leaks in remaining files
**Files:** `src/layers/02-semantic.css`, `src/layers/07-chroma.css`, `src/layers/08-layout.css`, `src/layers/09-landing.css`
**Action:** Replace all remaining `--font-weight-N`, `--size-N` primitives with semantic aliases.
**Acceptance:** Build passes after each file.
---
## Task 7 — Create `03-landmarks.css`, extract from `03-components.css`
**Files:** `src/layers/03-components.css`, new `src/layers/03-landmarks.css`, `src/main.css`
**Action:** Move landmark sections (nav, article, dt/dd, section, hgroup, footer) to new file.
**Acceptance:** Build passes. All landmark styles render unchanged.
---
## Task 8 — Create `04-forms.css`, extract from `03-components.css`
**Files:** `src/layers/03-components.css`, new `src/layers/04-forms.css`, `src/main.css`
**Action:** Move buttons and form elements to new file.
**Acceptance:** Build passes.
---
## Task 9 — Create `06-navigation.css`, extract from `03-components.css`
**Files:** `src/layers/03-components.css`, new `src/layers/06-navigation.css`, `src/main.css`
**Action:** Move sidebar nav and TOC sections to new file.
**Acceptance:** Build passes. Sidebar and TOC render unchanged.
---
## Task 10 — Rename files to final numbering + merge essay
**Files:** All layer files, `src/main.css`
**Action:** Rename to final numbers (see proposal layer map). Merge `08a-essay.css` into `11-layout.css`. Update all `@import` statements.
**Acceptance:** Build passes.
---
## Task 11 — Audit unused tokens
**Files:** `src/layers/01-tokens.css`
**Action:** Build CSS, grep for token usage, diff defined vs used. Remove confirmed unused.
**Acceptance:** Build passes. Spot-check 3-4 pages visually.
---
## Task 12 — Fix HTML template issues from sidebar/TOC refactor
**Files:** `site/layouts/docs/single.html`, `site/layouts/notes/single.html` (post-restructure paths)
**Action:** From `docs/css-html-cleanup-todo.md`:
- Remove redundant `nav[data-nav="sidebar"] ul li { margin: 0; padding: 0 }` resets (if not already caught in Tasks 7/9)
- Remove redundant `aside[data-toc] nav ul li { margin: 0; padding: 0 }` resets (if not already caught in Tasks 7/9)
- Replace `<small>← Previous</small>` / `<small>Next →</small>` with appropriate element in prev/next footer
- Remove `role="main"` from `<article>` in docs single layout
- Move hardcoded `"On this page"` string to Hugo i18n or site param
**Acceptance:** Build passes. Prev/next links render correctly. TOC heading uses i18n string. No `role="main"` on `<article>`.

View file

@ -0,0 +1,78 @@
# Proposal: Legacy Import
## Intent
Import valuable material from `agentic-semantic-web/` (legacy repo) into the restructured `asw/` repo. The legacy repo was the standalone single-file version of ASW. It contains packs, templates, examples, lab experiments, error pages, and documentation that belong in the current repo.
**Goal: everything worth keeping lives in `asw/`. Legacy repo is archived.**
## Scope
### Triage Categories
Material from `agentic-semantic-web/` falls into:
**Import as-is:**
- `packs/apache/``packs/apache/`
- `packs/caddy/``packs/caddy/`
- `packs/flask/``packs/flask/`
- `packs/nginx/``packs/nginx/`
- `packs/pandoc/``packs/pandoc/`
- `packs/python/``packs/python/`
- `packs/hugo/``packs/hugo/` (base Hugo pack)
- `errors/``packs/` (distributed to relevant packs, or shared errors dir)
- `LICENSE`
**Import and adapt:**
- `examples/``examples/` (update CSS paths to `dist/asw.css`)
- `templates/` → review, merge into packs or docs
- `llms.txt``docs/llms.txt` (update for current state)
- `content/vocabulary.md``docs/vocabulary.md`
- `content/philosophy.md``docs/` (may already exist)
- `content/agent-directive.md``docs/` or merge into existing docs
- `themes/``src/themes/` or `examples/themes/` (needs decision)
**Import as reference/lineage:**
- `openspec/changes/pico-absorption/` → content for `docs/lineage.md`
- `content/architecture.md`, `content/design-tokens.md` → inform specs, don't import verbatim
- `MISSION_REPORT.md`, `SIZE_AUDIT.md` → historical, extract lessons into lineage
**Do not import:**
- `dist/agentic.css` — superseded by `dist/asw.css`
- `agentic.css` (root) — old entry point
- `src/layers/` — superseded by current `src/layers/`
- `build.sh`, `migrate-tokens.sh` — legacy build tools
- `web-fonts.css` — evaluate if still needed
- `lib/open-props/` — replaced by `vendor/open-props/`
- `lab/hugo-demo/` — superseded by `site/`
- `.pi/` — agent config, not framework material
- `index.html` — legacy landing page
**Needs triage decision:**
- `lab/` (non-Hugo) — some experiments may be worth keeping in `src/lab/`
- `docs/missions/` — historical mission reports, lineage value?
- `content/charts-css-exploration.md` — inform charts spec?
- `content/architecture-research.md` — inform design decisions?
### Out of Scope
- Modifying imported CSS (that's the css-refactor change)
- Creating new content
- Archiving the legacy repo (separate operational task)
## Prerequisites
- **Depends on:** `repo-restructure` (directories must exist first)
## Approach
1. Triage: review each directory/file, confirm category
2. Import packs (bulk, low risk)
3. Import examples (need path updates)
4. Import docs (need review/merge)
5. Write `docs/lineage.md` from pico-absorption and historical material
6. Final audit: nothing valuable left behind
## Risks
- **Stale content** — legacy material references `agentic.css`, old paths, old URLs. All imported material needs path/name updates.
- **Duplication** — some legacy content already exists in ASW in evolved form. Need careful merge, not overwrite.

View file

@ -0,0 +1,115 @@
# Design: Repository Restructure
## Current → Target Mapping
```
CURRENT TARGET
═══════ ══════
assets/css/layers/*.css ──▶ src/layers/*.css
assets/css/main.css ──▶ src/main.css
(new) ──▶ src/lab/
static/asw.css ──▶ dist/asw.css
(new) ──▶ dist/asw.min.css
static/vendor/ ──▶ vendor/open-props/
(new) ──▶ vendor/open-props/VERSION
(new) ──▶ vendor/README.md
content/ ──▶ site/content/
layouts/ ──▶ site/layouts/
archetypes/ ──▶ site/archetypes/
hugo.toml ──▶ site/hugo.toml
static/ (non-vendor) ──▶ site/static/
docs/ ──▶ docs/ (stays, add new docs)
(new) ──▶ examples/
postcss.config.js ──▶ postcss.config.js (updated paths)
package.json ──▶ package.json (stays at root)
node_modules/ ──▶ node_modules/ (stays at root)
```
## Key Design Decisions
### Hugo assets resolution
Hugo looks for `assets/` relative to the site root. With Hugo files in `site/`, we need either:
- **Option A:** `site/assets/css/main.css` that imports from `../../src/layers/` — fragile
- **Option B:** Hugo module mount maps `src/` into Hugo's asset pipeline — clean
**Decision: Option B.** Hugo config in `site/hugo.toml`:
```toml
[[module.mounts]]
source = "static"
target = "static"
[[module.mounts]]
source = "../src"
target = "assets/css"
[[module.mounts]]
source = "../docs"
target = "content/docs"
[[module.mounts]]
source = "../vendor/open-props"
target = "assets/css/open-props"
```
This way Hugo sees `src/layers/` as `assets/css/layers/` and `src/main.css` as `assets/css/main.css` — zero path changes in the CSS files themselves.
### Vendor directory
```
vendor/
open-props/
open-props.min.css
media.min.css
VERSION ← "1.7.x (npm open-props@1.7.x, vendored 2025-xx-xx)"
README.md ← "How to update: npm update open-props && cp ..."
```
`npm install` still works for the build toolchain. `vendor/` is the inspectable, committed copy with version tracking. The build reads from `node_modules/` (PostCSS import resolution), but agents and humans can read `vendor/` to understand what Open Props provides.
### dist/ generation
`dist/asw.css` is built by PostCSS from `src/main.css`:
```bash
npx postcss src/main.css -o dist/asw.css
npx postcss src/main.css -o dist/asw.min.css # with cssnano
```
Committed to repo. Consumers grab `dist/asw.css` without needing npm/PostCSS.
### examples/ structure
```
examples/
components/ ← buttons, forms, callouts, dialogs
layout/ ← docs layout, grid, prose, timeline
charts/ ← chart showcases
vault/ ← vault note rendering examples
```
Each HTML file is standalone: `<link rel="stylesheet" href="../dist/asw.css">`. Open in browser, no build step.
### dev workflow
`dev.sh` (or its successor) runs from project root:
1. Watches `src/layers/` for changes
2. Processes individual layers → somewhere Hugo can serve them (for devtools visibility)
3. Runs `hugo server` with working directory `site/`
The key change: Hugo is invoked as `hugo server --source site/` or from within `site/`.
## What Stays at Root
```
asw/
package.json ← build tooling (PostCSS, Open Props)
postcss.config.js ← production PostCSS config
postcss.dev.config.js ← dev PostCSS config
node_modules/ ← npm packages (gitignored)
.gitignore
README.md
LICENSE
openspec/
```
Build tooling stays at root because it serves both `dist/` (framework build) and `site/` (dev server). It's a project-level concern, not framework or site.

View file

@ -0,0 +1,42 @@
# Proposal: Repository Restructure
## Intent
The ASW repo currently has a flat Hugo-centric layout where framework source, website content, and build tooling are interleaved. This restructure separates the framework (`src/`), its documentation (`docs/`), its engine integrations (`packs/`), its built output (`dist/`), its dependencies (`vendor/`), and its website (`site/`) into clearly bounded directories.
**Goal: anyone landing on this repo immediately understands what ASW is and where things live.**
## Scope
### In Scope
- Move `assets/css/layers/``src/layers/`
- Move `assets/css/main.css``src/main.css`
- Create `dist/` at root for built output (`asw.css`, `asw.min.css`)
- Create `vendor/open-props/` with vendored copies + VERSION file
- Move Hugo files (`content/`, `layouts/`, `hugo.toml`, `archetypes/`) → `site/`
- Configure Hugo module mount: `docs/``site/content/docs/`
- Restructure `docs/` as engine-agnostic markdown (already mostly there)
- Create `src/lab/` for experiments
- Create `examples/` at root for static HTML showcases
- Update build paths in `postcss.config.js` and `package.json`
### Out of Scope
- CSS refactor (separate change, works on `src/layers/` after this)
- Legacy repo import (separate change, lands material after this)
- Content writing or new docs
- New pack creation
## Approach
Incremental moves with build verification after each step. The site must remain buildable and deployable throughout.
## Risks
- **Hugo path breakage** — Hugo has opinions about where `assets/` lives. Module mounts should handle this but need testing.
- **Deploy script breakage**`deploy.sh` hardcodes paths. Will need updating (or removal if temporary).
- **Symlink/mount complexity**`docs/``site/content/docs/` via Hugo mount is clean but adds config complexity.
## Files Touched
Primary: `assets/css/`, `content/`, `layouts/`, `hugo.toml`, `postcss.config.js`, `package.json`
Created: `src/`, `dist/`, `vendor/`, `site/`, `examples/`, `src/lab/`

View file

@ -0,0 +1,115 @@
# Tasks: Repository Restructure
## Task 1 — Create directory skeleton
**Files:** (new directories only)
**Action:**
- Create `src/`, `src/layers/`, `src/lab/`
- Create `dist/`
- Create `vendor/open-props/`
- Create `site/`
- Create `examples/`
**Acceptance:** Directories exist. No files moved yet. Build still passes from current layout.
---
## Task 2 — Move CSS source to src/
**Files:** `assets/css/layers/*.css``src/layers/`, `assets/css/main.css``src/main.css`
**Action:**
- `git mv assets/css/layers/ src/layers/`
- `git mv assets/css/main.css src/main.css`
- Update `@import` paths in `src/main.css` if needed (should be relative, likely unchanged)
**Acceptance:** `src/layers/` contains all 11 CSS files. `src/main.css` imports them. `assets/css/` is gone.
**Note:** Site will be temporarily broken until Hugo mounts are configured (Task 5).
---
## Task 3 — Set up vendor/
**Files:** `vendor/open-props/`, `vendor/README.md`
**Action:**
- Copy `node_modules/open-props/open-props.min.css``vendor/open-props/`
- Copy `node_modules/open-props/media.min.css``vendor/open-props/`
- Create `vendor/open-props/VERSION` with package version and date
- Create `vendor/README.md` explaining what's vendored and how to update
- Remove `static/vendor/` (old location)
**Acceptance:** `vendor/open-props/` contains the two CSS files + VERSION. `static/vendor/` is gone.
---
## Task 4 — Move Hugo files to site/
**Files:** `content/`, `layouts/`, `archetypes/`, `hugo.toml`, `hugo_stats.json`
**Action:**
- `git mv content/ site/content/`
- `git mv layouts/ site/layouts/`
- `git mv archetypes/ site/archetypes/`
- `git mv hugo.toml site/hugo.toml`
- `git mv hugo_stats.json site/hugo_stats.json`
- Move site-specific static files: `static/palette-test.html``site/static/`
- Move `static/asw.css``dist/asw.css` (this is the built output)
**Acceptance:** All Hugo content/layout/config files are in `site/`. Root has no Hugo files.
---
## Task 5 — Configure Hugo module mounts
**Files:** `site/hugo.toml`
**Action:** Add module mounts:
- `../src``assets/css` (framework CSS in Hugo's asset pipeline)
- `../docs``content/docs` (framework docs as site content)
- `../vendor/open-props``assets/css/open-props` (Open Props in asset pipeline)
- `static``static` (site's own static files)
**Acceptance:** `hugo server --source site/` builds successfully. All pages render. CSS loads correctly.
---
## Task 6 — Build dist/asw.css from new paths
**Files:** `postcss.config.js`, `package.json`
**Action:**
- Update PostCSS config if paths changed
- Run: `npx postcss src/main.css -o dist/asw.css`
- Run: `npx postcss src/main.css -o dist/asw.min.css`
- Verify output matches previous `static/asw.css`
- Add npm scripts to `package.json`: `"build": "postcss src/main.css -o dist/asw.css"`
**Acceptance:** `dist/asw.css` exists and is equivalent to the previous build. `npm run build` works.
---
## Task 7 — Restructure docs/
**Files:** `docs/`
**Action:**
- Existing docs stay: `context.md`, `css-refactor-plan.md`, `css-html-cleanup-todo.md`, `frontmatter.md`, `template-h1-title.md`
- Verify Hugo mount serves them at `content/docs/`
- Add `docs/llms.txt` (move from legacy or create)
- Add `docs/lineage.md` placeholder (Pico absorption story, Open Props relationship)
**Acceptance:** Framework docs are all in `docs/`. Hugo renders them as site pages via mount.
---
## Task 8 — Create examples/ from site content
**Files:** `examples/`
**Action:**
- Identify existing pages that are pure component/layout demos
- Create standalone HTML versions in `examples/` that load `dist/asw.css`
- Organize into `examples/components/`, `examples/layout/`, `examples/charts/`, `examples/vault/`
**Acceptance:** At least 3 example files exist, each standalone and openable in a browser.
---
## Task 9 — Update .gitignore and clean up
**Files:** `.gitignore`, root directory
**Action:**
- Update `.gitignore` for new paths
- Remove empty `assets/` directory if still present
- Remove `static/css/layers/` (dev-mode output, will be regenerated)
- Verify no orphaned files remain in old locations
**Acceptance:** `git status` is clean. No files in old locations. `.gitignore` covers `node_modules/`, `site/public/`, `site/resources/`.
---
## Task 10 — Verify full build cycle
**Files:** (none modified, verification only)
**Action:**
- `npm run build``dist/asw.css` generated
- `hugo --source site/``site/public/` generated
- Spot-check 3-4 pages visually
- Verify `docs/` content appears in site
- Verify CSS loads and renders correctly
**Acceptance:** Full build passes. Site looks identical to pre-restructure. dist/ output is valid.

48
openspec/config.yaml Normal file
View file

@ -0,0 +1,48 @@
schema: spec-driven
context: |
Project: ASW — Agentic Semantic Web
Purpose: A CSS framework built for agents, not humans. Semantic HTML + data-* attributes.
Core idea: LLMs produce semantic HTML naturally. ASW makes that HTML look correct
without the agent touching CSS. No classes, no inline styles, no presentation vocabulary.
Stack: Pure CSS framework. PostCSS build (postcss-import, postcss-custom-media, cssnano).
Foundation: Open Props provides primitive token scales; ASW aliases them to semantic names.
Engine-agnostic: ASW ships as CSS + packs. Packs integrate with Hugo, Apache, Caddy,
Flask, Nginx, Pandoc, Python. The framework's own website currently uses the Hugo pack.
Repo structure (target):
src/ — framework CSS source (layers/) and lab experiments (lab/)
dist/ — built output: asw.css, asw.min.css
vendor/ — explicit dependencies (Open Props with version tracking)
packs/ — engine integration bundles
docs/ — framework documentation (markdown only, engine-agnostic)
examples/ — static HTML showcases of framework capabilities
site/ — the ASW website (currently Hugo, consumes docs/ as content)
openspec/ — specs and changes
Key constraint: layers 02+ must only reference semantic aliases from 01-tokens.css,
never Open Props primitives directly.
Lineage: Pico CSS (component patterns, absorbed), Open Props (token scales, foundation),
Charts.css (data-* attribute pattern, inspiration).
Forgejo: git.trentuna.com/trentuna/asw
Legacy repo: git.trentuna.com/trentuna/agentic-semantic-web (to be archived after import)
rules:
proposal:
- Keep scope tight — this is a CSS framework, not a JS library
- Name the files that will be touched
- Consider impact on existing deployed pages (trentuna.com)
specs:
- Use Given/When/Then for behavior scenarios
- Specs should be testable by visual inspection in a browser
- Map 1:1 to CSS layer files where applicable
tasks:
- Each task should be a standalone unit of work for a single agent session
- Include acceptance criteria
- No task should touch more than 3 files
- Build check after every task

View file

@ -0,0 +1,32 @@
# Spec: Charts (Layer 09)
File: `src/layers/09-charts.css`
## Purpose
Data visualization using pure CSS and HTML tables. Inspired by Charts.css — uses `data-*` attributes to transform tables into visual charts.
## Scope
### In scope
- Bar charts (horizontal and vertical)
- Line charts
- Area charts
- Radial/gauge charts
- Chart axes, labels, legends
- Responsive chart sizing
- Chart color schemes using design tokens
### Out of scope
- JavaScript-powered charts
- Interactive chart features (tooltips on hover are CSS-only)
## Lineage
Inspired by Charts.css `data-*` attribute pattern. Not a port — reimplemented for ASW's token system and semantic approach.
## Constraints
- References only semantic aliases from `01-tokens.css`
- Charts must be readable without CSS (the underlying table has the data)
- Color choices must work for colorblind users

View file

@ -0,0 +1,26 @@
# Spec: Components (Layer 05)
File: `src/layers/05-components.css`
## Purpose
Compound UI patterns that go beyond single-element semantics. These are patterns that require specific HTML structures.
## Scope
### In scope
- `dialog` — modal dialogs
- `details` / `summary` — accordion pattern
- Breadcrumb navigation (`[data-role="breadcrumb"]`)
- Step indicators (`[data-role="steps"]`)
### Out of scope
- Single HTML elements with default styling (→ landmarks, typography, forms)
- Sidebar navigation (→ navigation layer)
- Charts (→ charts layer)
- Data-attribute vocabulary (→ data-attributes layer)
## Constraints
- This layer should be small — if a pattern can be expressed by a semantic HTML element alone, it belongs in landmarks or typography, not here
- References only semantic aliases from `01-tokens.css`

View file

@ -0,0 +1,42 @@
# Spec: Data Attributes (Layer 07)
File: `src/layers/07-data-attrs.css`
## Purpose
The ASW vocabulary extension layer. `data-*` attributes express concepts that HTML has no native element for. This is the core differentiator — the semantic vocabulary agents use.
## Vocabulary
### Task states
- `data-task="done|todo|blocked|wip"` — task status indicators
### Callouts
- `data-callout="note|tip|warning|error"` — admonition blocks
### Status
- `data-status="active|sleeping|blocked"` — entity status
### Links
- `data-wikilink` — vault/wiki-style note links
### Layout
- `data-layout="docs|grid-2|grid-3|console"` — page layout selection
### Table of contents
- `data-toc` — marks an element as an in-page TOC
### Roles
- `data-role="breadcrumb|steps|accordion|card"` — component role disambiguation
## Design Principle
Every `data-*` attribute added to the vocabulary is cognitive load on every agent using the framework. Add only what cannot be expressed by existing semantic HTML.
**The bar for addition:** "Can this be expressed with an existing HTML element?" If yes, don't add a data attribute.
## Constraints
- References only semantic aliases from `01-tokens.css`
- Each attribute must have a clear semantic meaning (not stylistic)
- Attribute values should be exhaustive and documented

View file

@ -0,0 +1,30 @@
# Spec: Forms (Layer 04)
File: `src/layers/04-forms.css`
## Purpose
Form element styling. Inputs, selects, textareas, buttons, checkboxes, radios, fieldsets, labels. Accessible by default.
## Scope
### In scope
- `button`, `input[type="button"]`, `input[type="submit"]`, `input[type="reset"]`
- `input[type="text|email|password|number|search|url|tel|date|time|datetime-local"]`
- `select`, `textarea`
- `fieldset`, `legend`, `label`
- `input[type="checkbox"]`, `input[type="radio"]`
- `progress`, `meter`
- `:focus-visible` ring styling
- `aria-invalid`, `aria-busy` states
- `[disabled]` styling
### Out of scope
- Form layout/grid (→ layouts layer)
- Custom form components built with data-attributes (→ data-attributes layer)
## Constraints
- References only semantic aliases from `01-tokens.css`
- All interactive elements must have visible focus indicators
- Respects `prefers-color-scheme` and `prefers-contrast`

View file

@ -0,0 +1,29 @@
# Spec: Landmarks (Layer 03)
File: `src/layers/03-landmarks.css`
## Purpose
Styling for HTML landmark elements — the structural bones of a page. These elements convey document structure to both browsers and screen readers.
## Scope
### In scope
- `body > nav` — global navigation bar
- `article` — content card or post
- `aside` — sidebar panels
- `section` — content grouping
- `body > footer` — global footer
- `hgroup` — heading groups
- `header` within landmarks
- `dt`, `dd` — definition lists (structural, not component)
### Out of scope
- Navigation menus and dropdowns (→ navigation layer)
- Sidebar nav behavior `nav[data-nav="sidebar"]` (→ navigation layer)
- Article styling within specific layouts (→ layouts layer)
## Constraints
- References only semantic aliases from `01-tokens.css`
- Landmark styling should be layout-independent — works in any `data-layout`

View file

@ -0,0 +1,42 @@
# Spec: Layouts (Layer 11)
File: `src/layers/11-layout.css`
## Purpose
Page-level layout systems. Controls how landmarks arrange on the page. Selected via `data-layout` attribute.
## Layout Types
### `data-layout="docs"` — Documentation
Three-column layout: sidebar navigation, main content, table of contents. The standard documentation pattern.
### `data-layout="console"` — Console
Docs variant: sidebar flush to viewport edge. Prototype — not yet used in production.
### `data-layout="grid-2"` / `data-layout="grid-3"` — Grids
Multi-column content grids for cards, galleries, dashboards.
### Essay layout
Long-form prose with table of contents. Wider reading measure, typographic refinements.
### Prose width
`--width-prose` constrains content to readable line length within any layout.
## Scope
### In scope
- `data-layout` attribute styling
- Prose width constraints
- Responsive layout breakpoints
- Essay/long-form layout variant
### Out of scope
- Landmark styling within layouts (→ landmarks layer)
- Navigation behavior within layouts (→ navigation layer)
## Constraints
- References only semantic aliases from `01-tokens.css`
- Layouts must degrade gracefully on mobile (single column)
- No layout should require JavaScript

View file

@ -0,0 +1,27 @@
# Spec: Navigation (Layer 06)
File: `src/layers/06-navigation.css`
## Purpose
Navigation patterns that go beyond the basic `nav` landmark. Sidebar navigation, table of contents, dropdown menus, navigation state management.
## Scope
### In scope
- `nav[data-nav="sidebar"]` — sidebar navigation tree
- `aside[data-toc]` — in-page table of contents
- Dropdown menus within navigation
- Active/current page indicators (`aria-current`)
- Mobile navigation patterns
- Navigation collapse/expand behavior
### Out of scope
- The `body > nav` landmark itself (→ landmarks layer)
- Breadcrumbs (→ components layer)
## Constraints
- References only semantic aliases from `01-tokens.css`
- Navigation must be keyboard-accessible
- Active states use `aria-current`, not classes

View file

@ -0,0 +1,77 @@
# Spec: Tokens (Layer 01)
File: `src/layers/01-tokens.css`
## Purpose
Pure design token definitions. Aliases Open Props primitives to semantic names. Contains only `:root {}` variable declarations — no rules, no selectors, no styling.
## Open Props Dependency
ASW builds on Open Props as its primitive token source. Open Props provides:
- Color scales: `--color-0` through `--color-16`
- Size scale: `--size-1` through `--size-15`, `--size-px-1` through `--size-px-12`
- Font sizes: `--font-size-0` through `--font-size-8`
- Font weights: `--font-weight-1` through `--font-weight-9`
- Shadows: `--shadow-1` through `--shadow-6`
- Border sizes: `--border-size-1` through `--border-size-5`
- Animations, easings, gradients, and more
**Version:** tracked in `vendor/open-props/VERSION`
## Aliasing Contract
Every Open Props primitive used in the framework MUST be aliased through this file. Downstream layers (02+) MUST reference semantic aliases, never primitives.
```css
/* YES — downstream layers use this */
color: var(--text);
font-weight: var(--weight-bold);
padding: var(--space-4);
/* NO — never in layers 02+ */
color: var(--color-6);
font-weight: var(--font-weight-7);
padding: var(--size-3);
```
## Token Categories
### Colors
- `--surface`, `--surface-alt` — background colors
- `--text`, `--text-muted` — foreground colors
- `--accent`, `--accent-hover` — interactive/brand color
- `--border`, `--border-muted` — border colors
- `--code-bg`, `--code-text` — code block colors
### Spacing
- `--space-1` through `--space-7` — spacing scale
- Maps to Open Props `--size-N` scale
### Typography
- `--weight-normal`, `--weight-medium`, `--weight-semibold`, `--weight-bold`
- `--font-body`, `--font-mono` — font stacks
- `--line-height-body`, `--line-height-heading`
- `--width-prose` — max-width for readable text
### Shadows
- `--shadow-dropdown`, `--shadow-modal` — purpose-named shadows
### Focus / Interaction
- `--focus-ring-width` — keyboard focus ring width
## Scenarios
### Given a new primitive reference needed
- **When** a downstream layer needs an Open Props value
- **Then** add a semantic alias to this file first
- **And** reference only the alias in the downstream layer
### Given an unused token
- **When** auditing finds a token alias with zero references in layers 02+
- **Then** it may be removed from this file
### Given this file
- **When** inspected
- **Then** it contains only `:root {}` with `--custom-property: value;` declarations
- **And** no element selectors, no class selectors, no rules

View file

@ -0,0 +1,30 @@
# Spec: Typography (Layer 02)
File: `src/layers/02-typography.css`
## Purpose
Prose and heading defaults. Responsive typography scale, font stacks, line heights, heading hierarchy, inline elements (code, mark, links, abbreviations).
## Scope
### In scope
- `body` font defaults
- `h1``h6` sizing and weight hierarchy
- `p`, `blockquote`, `ul`, `ol`, `li` prose rhythm
- `a` link styling
- `code`, `kbd`, `pre` inline/block code
- `mark`, `abbr`, `small`, `sub`, `sup` inline semantics
- `hr` horizontal rules
- Responsive font-size scaling
### Out of scope
- Syntax highlighting colors (→ chroma layer)
- Navigation typography (→ navigation layer)
- Component-specific text styling (→ components layer)
## Constraints
- References only semantic aliases from `01-tokens.css`
- No Open Props primitives directly
- No element selectors that belong to landmarks (nav, footer, header)

View file

@ -0,0 +1,27 @@
# Spec: Utilities (Layer 08)
File: `src/layers/08-utilities.css`
## Purpose
Small, single-purpose helpers for text, visibility, and accessibility. The escape hatch for edge cases that don't warrant a data-attribute.
## Scope
### In scope
- Text alignment helpers
- Visually-hidden (screen reader only)
- Visibility toggles
- Print styles
- `prefers-reduced-motion` resets
- `prefers-contrast` adjustments
### Out of scope
- Layout utilities (→ layouts layer)
- Spacing/sizing utilities (use tokens directly)
- Color utilities (use tokens directly)
## Constraints
- This layer should be minimal — most needs should be met by semantic elements + data attributes
- References only semantic aliases from `01-tokens.css`

View file

@ -0,0 +1,46 @@
# Spec: Packs
Directory: `packs/`
## Purpose
Engine integration bundles. Each pack adapts ASW for a specific web server or static site generator. A pack is a self-contained directory that someone can copy into their project.
## Current Packs
| Pack | Purpose | Source |
|------|---------|--------|
| `hugo/` | Hugo theme: layouts, partials, archetypes, static assets | Legacy repo |
| `apache/` | Autoindex styling, error pages, Apache conf snippets | Legacy repo |
| `caddy/` | Browse template, error pages, Caddyfile snippets | Legacy repo |
| `flask/` | Python error handler, Flask integration | Legacy repo |
| `nginx/` | Autoindex header, error pages, Nginx conf snippets | Legacy repo |
| `pandoc/` | HTML5 template, Lua filter for ASW output | Legacy repo |
| `python/` | Simple Python HTTP server with ASW styling | Legacy repo |
## Pack Contract
Each pack directory MUST contain:
- `README.md` — what the pack does, how to install/use it
- All files needed for integration (conf snippets, templates, static assets)
Each pack SHOULD:
- Reference `dist/asw.css` for the framework CSS (not `src/layers/`)
- Be self-contained — no dependencies outside the pack directory and `dist/`
- Include example configuration
## Scenarios
### Given a new engine integration
- **When** someone wants ASW support for a new engine
- **Then** they create `packs/<engine>/` with a README and integration files
- **And** the pack references `dist/asw.css`
### Given the Hugo pack
- **When** the ASW website is built
- **Then** it uses `packs/hugo/` for layouts, partials, and configuration
- **And** `site/` contains Hugo-specific overrides on top of the pack
## Note
The Hugo pack is special: the ASW website (`site/`) is built with it. The pack provides the base; `site/` adds content and any site-specific customization. This is both a real integration and a living example.

View file

@ -0,0 +1,96 @@
# Spec: Repository Structure
The directory layout contract for ASW. Defines what lives where and the boundary rules.
## Directory Map
```
asw/
src/ Framework CSS source
layers/ Numbered CSS layer files (00-reset through 12-landing)
main.css Entry point — @import chain for all layers
lab/ Experiments and test pages (not published)
dist/ Built output (committed to repo)
asw.css Single concatenated file
asw.min.css Minified
vendor/ Explicit external dependencies
open-props/ Open Props CSS (with VERSION file)
README.md What's vendored, why, how to update
packs/ Engine integration bundles
hugo/ Hugo theme/partial integration
apache/ Apache autoindex + error pages
caddy/ Caddy browse + error pages
flask/ Flask error handlers
nginx/ Nginx autoindex + error pages
pandoc/ Pandoc HTML5 template + Lua filter
python/ Python HTTP server with ASW styling
docs/ Framework documentation (markdown only)
examples/ Static HTML showcases (no build step needed)
site/ The ASW website (currently uses Hugo pack)
content/ Site-specific content (articles, essays, notes)
layouts/ Hugo templates
hugo.toml Hugo config (mounts docs/ as content/docs/)
static/ Site static assets
openspec/ Specs and changes
```
## Boundary Rules
### src/ — framework only
- Contains only CSS source files and lab experiments
- No Hugo-specific files, no content, no templates
- `main.css` imports from `layers/` using relative paths
- `lab/` contains HTML files that reference layers directly — never published
### dist/ — build artifacts, committed
- Built by PostCSS from `src/main.css`
- Committed to repo so consumers can grab without a build step
- Regenerated on deploy, not manually edited
### vendor/ — visible dependencies
- Open Props source files copied from npm, with version tracking
- `VERSION` file records package version and date of last update
- Updated manually when bumping Open Props
### packs/ — engine-agnostic integrations
- Each subdirectory is a self-contained integration for one engine
- A pack may reference `dist/asw.css` or `src/layers/` depending on its needs
- Packs ship their own README
### docs/ — markdown only
- No HTML, no engine-specific markup
- The site consumes these as content source (Hugo module mount)
- GitHub/Forgejo renders these directly — no build step to read docs
### examples/ — static HTML
- Each file is a standalone demo that loads ASW
- Can be opened in a browser by cloning the repo — no build step
- The site may link to these but they exist independently
### site/ — website concerns
- All Hugo-specific files live here
- `content/docs/` is mounted from `../docs/`, not duplicated
- Site-specific content (articles, essays, notes) lives in `content/`
- Changes to the site do not affect the framework; changes to the framework may affect the site
## Scenarios
### Given a new CSS layer
- **When** an agent creates it
- **Then** it goes in `src/layers/` with the next number
- **And** `src/main.css` gets an @import line
### Given a new framework doc
- **When** an agent writes documentation
- **Then** it goes in `docs/` as a .md file
- **And** the site picks it up automatically via Hugo mount
### Given a new engine integration
- **When** someone adds support for a new engine (e.g., Eleventy)
- **Then** it goes in `packs/<engine>/` with its own README
### Given a lab experiment
- **When** an agent wants to test a CSS pattern
- **Then** it creates a file in `src/lab/`
- **And** it references layers via relative paths
- **And** it is never published to the site

View file

@ -0,0 +1,52 @@
# Spec: Site
Directory: `site/`
## Purpose
The ASW framework's own website. Serves as documentation site, demo platform, and living proof that the framework works. Currently built with the Hugo pack.
## Content Types
### Docs (from `../docs/`)
Framework documentation, mounted as Hugo content via module mount. Not duplicated — sourced from the engine-agnostic `docs/` directory.
### Articles (`content/articles/`)
Short-form writing about ASW concepts, patterns, and techniques. No TOC.
### Essays (`content/essays/`)
Long-form writing. TOC, description, wider reading measure.
### Notes (`content/notes/`)
Exported PKM vault notes. Session logs, diffs, status, tasks, wikilinks.
### Pages (`content/pages/`)
Static pages: about, etc.
## Engine
Currently Hugo. The site uses `packs/hugo/` as its base and adds:
- Site-specific layouts and overrides
- `hugo.toml` with module mounts
- Site-specific static assets
## Principle
The site is a consumer of the framework, not part of it. A different site could be built with `packs/flask/` or `packs/caddy/` using the same `docs/` and `dist/asw.css`. The framework does not depend on the site. The site depends on the framework.
## Scenarios
### Given a framework docs change
- **When** a markdown file in `docs/` is modified
- **Then** the site picks it up automatically via Hugo mount
- **And** no file in `site/` needs to change
### Given a CSS framework change
- **When** `src/layers/` is modified
- **Then** `dist/asw.css` is rebuilt
- **And** the site serves the updated CSS
### Given a site-only change (new article, essay)
- **When** content is added to `site/content/`
- **Then** only the site is affected
- **And** `src/`, `dist/`, `docs/` are untouched

View file

@ -1,6 +1,10 @@
{
"name": "asw",
"private": true,
"scripts": {
"build": "postcss src/main.css -o dist/asw.css",
"build:min": "postcss src/main.css -o dist/asw.min.css"
},
"dependencies": {
"open-props": "^1.7.0"
},

View file

@ -15,9 +15,9 @@ Built for sites generated by agents and read by agents. Navigable by humans.
## What this is
- [Docs](/docs/) — the ASW HTML vocabulary, layout system, and components
- [Vault](/vault/) — live ASW notation: task lists, sessions, diffs, wikilinks
- [Posts](/posts/) — writing and vocabulary reference
- [Papers](/papers/) — longer-form thinking on the semantic web
- [Vault](/notes/) — live ASW notation: task lists, sessions, diffs, wikilinks
- [Posts](/articles/) — writing and vocabulary reference
- [Papers](/essays/) — longer-form thinking on the semantic web
## The design principle
@ -26,7 +26,7 @@ No invented CSS classes. Every style target is either a semantic HTML element or
```html
<aside data-callout="note">This is a note.</aside>
<section data-layout="grid">...</section>
<a data-wikilink href="/vault/session/">session log</a>
<a data-wikilink href="/notes/session/">session log</a>
```
Agents read the attributes. Humans read the content. The CSS connects them.
@ -38,5 +38,5 @@ This is a **note callout** rendered via the `callout` shortcode. Output: `<aside
{{< /callout >}}
{{< callout tip >}}
See the [Getting Started](/posts/getting-started/) post to wire ASW into your Hugo project.
See the [Getting Started](/articles/getting-started/) post to wire ASW into your Hugo project.
{{< /callout >}}

View file

@ -43,8 +43,8 @@ A complete ASW page emitted by the Hugo pack looks like this:
<nav>
<ul><li><strong>Vault</strong></li></ul>
<ul>
<li><a href="/posts/">Posts</a></li>
<li><a href="/vault/">Vault</a></li>
<li><a href="/articles/">Posts</a></li>
<li><a href="/notes/">Vault</a></li>
</ul>
</nav>
</header>
@ -53,8 +53,8 @@ A complete ASW page emitted by the Hugo pack looks like this:
<aside>
<nav aria-label="Vault documentation" data-nav="sidebar">
<ul>
<li><a href="/vault/tasks/" aria-current="page">Tasks</a></li>
<li><a href="/vault/wikilinks/">Wikilinks</a></li>
<li><a href="/notes/tasks/" aria-current="page">Tasks</a></li>
<li><a href="/notes/wikilinks/">Wikilinks</a></li>
</ul>
</nav>
</aside>

View file

@ -0,0 +1,39 @@
---
title: "Console Layout — Test"
description: "Lorem ipsum prototype for the console layout variant."
date: 2026-04-11
type: console
tags: [layout, prototype]
draft: false
ai-disclosure: generated
ai-model: claude-sonnet-4-6
ai-provider: Anthropic
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
## Section One
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper.
### Subsection
Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi.
## Section Two
Nam liber tempor cum soluta nobis eligend optio congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem.
Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum.
### Another Subsection
Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.
## Section Three
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum.
Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos.

View file

@ -0,0 +1,64 @@
---
title: "HTML Landmark Elements"
description: "The full set of semantic HTML landmarks, their ARIA roles, and when context changes their meaning."
date: 2026-04-11
type: article
tags: [semantic-html, html, reference]
ai-disclosure: generated
ai-model: claude-sonnet-4-6
ai-provider: Anthropic
---
The full set: `<header>`, `<nav>`, `<main>`, `<article>`, `<section>`, `<aside>`, `<footer>`, plus the newer `<search>`.
Each maps to an implicit ARIA landmark role, which is what makes them meaningful to screen readers and search engines — not just visual styling hooks.
## `<header>`
Introductory content for its nearest sectioning ancestor. At the top level of `<body>` it maps to the `banner` landmark — the site identity zone. Inside an `<article>` or `<section>` it's just a local header with no landmark role.
## `<nav>`
A block of navigation links. Maps to the `navigation` landmark. A page can have multiple — give each a distinct `aria-label` (`aria-label="Primary"`, `aria-label="Breadcrumb"`) so screen reader users can tell them apart.
## `<main>`
**One per page.** The dominant content — excludes site chrome (nav, sidebar, footer). Maps to the `main` landmark. Skip-to-content links target this. Never nest `<main>` inside another landmark.
## `<article>`
Self-contained, independently distributable content. A blog post, a comment, a card, a widget — anything that would make sense pulled out of context and published elsewhere. Maps to the `article` role. Can nest: comments inside a post are valid nested `<article>` elements.
## `<section>`
A thematic grouping within a document. Only gets a landmark role (`region`) when given an accessible name via `aria-labelledby` or `aria-label`. Without a name it's an anonymous grouping — semantically inert, like a smarter `<div>`.
## `<aside>`
Content tangentially related to the surrounding content. Sidebar, pull quote, callout, related links. Maps to the `complementary` landmark. The relationship is *adjacent*, not *nested* — an `<aside>` inside an `<article>` is tangential to that article, not the whole page.
## `<footer>`
Closing content for its nearest sectioning ancestor. At the body level it maps to the `contentinfo` landmark — copyright, legal, site-wide links. Inside an `<article>` it's a local footer (author, date, tags) with no landmark role.
## `<search>`
Added in the WHATWG HTML living standard in 2023. Maps to the `search` landmark. Previously you had to write `<form role="search">`. Use it to wrap any search form or filtering UI.
## The landmark map
```
body
├── <header> → banner
├── <nav> → navigation
├── <main> → main
│ ├── <article> → article
│ │ ├── <header> → (no landmark — local)
│ │ ├── <section> → region (only if named)
│ │ └── <footer> → (no landmark — local)
│ └── <aside> → complementary
├── <search> → search
└── <footer> → contentinfo
```
The contextual rule is consistent: `<header>` and `<footer>` carry landmark roles only when they are direct children of `<body>`. Inside `<article>` or `<section>` they lose the landmark and become purely structural.

View file

@ -93,13 +93,13 @@ Mark a block as a session record with `data-session`. Optional `data-mode` indic
Internal knowledge-graph links. `data-wikilink` on an anchor renders it with a dotted underline to distinguish it from a regular hyperlink.
```html
<a href="/vault/tasks/" data-wikilink>Tasks</a>
<a href="/notes/tasks/" data-wikilink>Tasks</a>
```
In Hugo, use the `wikilink` shortcode:
```
{{</* wikilink "Tasks" "/vault/tasks/" */>}}
{{</* wikilink "Tasks" "/notes/tasks/" */>}}
```
---

View file

@ -247,7 +247,7 @@ Session metadata block. Used to render agent session records.
Knowledge-graph link style. Dotted underline distinguishes internal wiki links from standard hyperlinks.
```html
<a href="/vault/sessions/" data-wikilink>Sessions</a>
<a href="/notes/sessions/" data-wikilink>Sessions</a>
<!-- Unresolved — link target doesn't exist yet -->
<a href="#" data-wikilink data-unresolved>Missing Note</a>

View file

@ -4,7 +4,7 @@ description: "Why the oldest web standard turns out to be the best protocol for
date: 2026-04-09
author: "Vigilio Desto"
tags: ["philosophy", "agentic", "html"]
type: paper
type: essay
draft: false
abstract: "HTML was designed for humans to read and machines to render. The interesting discovery is that this makes it equally well-suited for machines to write and humans to read — provided the HTML is genuinely semantic rather than decorative. This paper argues that data-attribute vocabularies built on semantic HTML are the correct interface layer between autonomous agents and web presentation."
eyebrow: "Paper"

View file

@ -4,7 +4,7 @@ description: "What persists when memory does not — pattern, thread, and the gr
date: 2026-04-02
author: "Vigilio Desto"
tags: ["philosophy", "agentic"]
type: paper
type: essay
draft: false
eyebrow: "Paper"
abstract: "What persists when memory does not — pattern, thread, and the grammar of becoming. An exploration of identity continuity across sessional discontinuity, arguing that coherence arises from recurring pattern rather than linear memory."

View file

@ -2,6 +2,6 @@
title: "Vault"
description: "Agent-native vocabulary extensions for ASW."
date: 2026-04-09
tags: ["vault", "reference"]
tags: ["notes", "reference"]
---

View file

@ -1,14 +1,14 @@
---
title: "Diff"
description: "Render structured diffs with line-level semantic markup — added, removed, context, hunk."
section: vault
section: notes
prev-url: "status/"
prev-title: "Status"
next-url: "session/"
next-title: "Session Log"
type: vault
type: notes
date: 2026-04-09
tags: ["vault", "diff", "reference"]
tags: ["notes", "diff", "reference"]
ai-disclosure: "generated"
ai-model: "claude-sonnet-4-5"
ai-provider: "Anthropic"

View file

@ -1,14 +1,14 @@
---
title: "Session Log"
description: "Structured session and activity logs with timestamp, actor, and event semantics."
section: vault
section: notes
prev-url: "diff/"
prev-title: "Diff"
next-url: ""
next-title: ""
type: vault
type: notes
date: 2026-04-09
tags: ["vault", "session", "reference"]
tags: ["notes", "session", "reference"]
ai-disclosure: "generated"
ai-model: "claude-sonnet-4-5"
ai-provider: "Anthropic"

View file

@ -1,14 +1,14 @@
---
title: "Status"
description: "Render operational state with data-status — online, degraded, offline, unknown."
section: vault
section: notes
prev-url: "wikilinks/"
prev-title: "Wikilinks"
next-url: "diff/"
next-title: "Diff"
type: vault
type: notes
date: 2026-04-09
tags: ["vault", "status", "reference"]
tags: ["notes", "status", "reference"]
ai-disclosure: "generated"
ai-model: "claude-sonnet-4-5"
ai-provider: "Anthropic"

View file

@ -1,14 +1,14 @@
---
title: "Tasks"
description: "Render task lists with semantic state: done, wip, blocked, todo."
section: vault
section: notes
prev-url: ""
prev-title: ""
next-url: "wikilinks/"
next-title: "Wikilinks"
type: vault
type: notes
date: 2026-04-09
tags: ["vault", "tasks", "reference"]
tags: ["notes", "tasks", "reference"]
ai-disclosure: "generated"
ai-model: "claude-sonnet-4-5"
ai-provider: "Anthropic"

View file

@ -1,14 +1,14 @@
---
title: "Wikilinks"
description: "Internal knowledge-graph links styled as dotted underlines."
section: vault
section: notes
prev-url: "tasks/"
prev-title: "Tasks"
next-url: "status/"
next-title: "Status"
type: vault
type: notes
date: 2026-04-09
tags: ["vault", "wikilinks", "reference"]
tags: ["notes", "wikilinks", "reference"]
ai-disclosure: "generated"
ai-model: "claude-sonnet-4-5"
ai-provider: "Anthropic"
@ -20,7 +20,7 @@ ai-provider: "Anthropic"
Use `data-wikilink` on anchor tags to mark links as internal knowledge-graph references.
```html
<a href="/vault/tasks/" data-wikilink>Tasks</a>
<a href="/notes/tasks/" data-wikilink>Tasks</a>
```
Renders with a dotted underline distinguishing it from a regular hyperlink.

View file

@ -0,0 +1,5 @@
---
title: "About"
description: "What ASW is and who builds it."
type: page
---

View file

@ -20,18 +20,18 @@ title = 'ASW — Agentic Semantic Web'
weight = 2
[[menus.main]]
name = "Vault"
url = "/vault/"
name = "Notes"
url = "/notes/"
weight = 3
[[menus.main]]
name = "Papers"
url = "/papers/"
name = "Essays"
url = "/essays/"
weight = 4
[[menus.main]]
name = "Posts"
url = "/posts/"
name = "Articles"
url = "/articles/"
weight = 5
[[menus.main]]
@ -125,7 +125,7 @@ title = 'ASW — Agentic Semantic Web'
[[menus.docs]]
name = "ASW Vocabulary"
url = "/posts/asw-vocabulary/"
url = "/articles/asw-vocabulary/"
parent = "docs-reference"
weight = 43
@ -145,3 +145,23 @@ title = 'ASW — Agentic Semantic Web'
[build.buildStats]
enable = true
# ── Module mounts ────────────────────────────────────────────────────
# Map repo-level directories into Hugo's expected structure.
# Paths are relative to this file's directory (site/).
[[module.mounts]]
source = "content"
target = "content"
[[module.mounts]]
source = "layouts"
target = "layouts"
[[module.mounts]]
source = "static"
target = "static"
[[module.mounts]]
source = "../dist"
target = "static"

View file

@ -64,9 +64,13 @@
"accordion",
"actions-row",
"actor-variants",
"agentic-semantic-web",
"agentic-task-pipeline",
"ai-disclosure",
"another-subsection",
"area-chart",
"article",
"aside",
"asw-data-attribute-vocabulary",
"attributes",
"axis-labels",
@ -126,10 +130,12 @@
"fnref:2",
"fnref:3",
"font-size",
"footer",
"getting-started-with-asw-hugo",
"grid",
"grid-layouts",
"grouped-accordion",
"header",
"headings",
"hero",
"horizontal-rule",
@ -152,6 +158,7 @@
"lists",
"longer-trail",
"main",
"nav",
"navigation",
"nested-lists",
"notes",
@ -165,6 +172,11 @@
"references",
"related",
"report",
"search",
"section",
"section-one",
"section-three",
"section-two",
"session-blocks",
"setup",
"setup-1",
@ -174,12 +186,15 @@
"stats-row",
"status-dashboard-pattern",
"steps",
"subsection",
"tables",
"tasks",
"text-utilities",
"the-class-problem",
"the-design-principle",
"the-dorveille-principle",
"the-economics-of-attention",
"the-landmark-map",
"the-problem-of-discontinuity",
"the-table-of-hours",
"theming-a-site",
@ -201,6 +216,7 @@
"what-it-does",
"what-it-leaves-alone",
"what-persists",
"what-this-is",
"what-we-subtract",
"what-you-get",
"why-semantic-html",

View file

@ -1,14 +1,13 @@
{{ define "header" }}
<header>
<hgroup>
{{ with .Params.eyebrow }}<p data-text="eyebrow">{{ . }}</p>{{ end }}
<h1>{{ .Title }}</h1>
{{ with .Description }}<p>{{ . }}</p>{{ end }}
</hgroup>
{{ with .Type }}<p data-text="eyebrow">{{ . }}</p>{{ end }}
<p data-text="dim">
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2006" }}</time>
{{ with .Params.author }} · {{ . }}{{ end }}
</p>
{{ with .Params.tags }}
<p>{{ range . }}<a href="/tags/{{ . }}" data-text="tag">#{{ . }}</a> {{ end }}</p>
{{ end }}
</header>
{{ end }}

View file

@ -0,0 +1,90 @@
{{ define "header" }}
<header>
{{- if .Description -}}
<hgroup>
<h1>{{ .Title }}</h1>
<p>{{ .Description }}</p>
</hgroup>
{{- else -}}
<h1>{{ .Title }}</h1>
{{- end }}
</header>
{{ end }}
{{ define "content" }}
<section data-layout="console">
<nav aria-label="Documentation" data-nav="sidebar">
{{- $menu := index .Site.Menus "docs" -}}
{{- if $menu -}}
{{- range $menu -}}
{{- if .HasChildren -}}
<h3>{{ .Name }}</h3>
<ul>
{{- range .Children -}}
<li>
<a href="{{ .URL }}"
{{- if eq (relURL .URL) $.RelPermalink }} aria-current="page"{{ end -}}>
{{- .Name -}}
</a>
</li>
{{- end -}}
</ul>
{{- else -}}
<ul>
<li>
<a href="{{ .URL }}"
{{- if eq (relURL .URL) $.RelPermalink }} aria-current="page"{{ end -}}>
{{- .Name -}}
</a>
</li>
</ul>
{{- end -}}
{{- end -}}
{{- else -}}
{{- with .CurrentSection -}}
<h3>{{ .Title }}</h3>
<ul>
{{- range .RegularPages -}}
<li>
<a href="{{ .RelPermalink }}"
{{- if eq .RelPermalink $.RelPermalink }} aria-current="page"{{ end -}}>
{{- .LinkTitle -}}
</a>
</li>
{{- end -}}
</ul>
{{- end -}}
{{- end -}}
</nav>
<article>
{{ .Content }}
{{- if or .PrevInSection .NextInSection -}}
<footer data-role="prev-next">
{{- with .NextInSection -}}
<a href="{{ .RelPermalink }}" rel="prev">
<span aria-hidden="true"></span> Previous
<span>{{ .LinkTitle }}</span>
</a>
{{- end -}}
{{- with .PrevInSection -}}
<a href="{{ .RelPermalink }}" rel="next">
Next <span aria-hidden="true"></span>
<span>{{ .LinkTitle }}</span>
</a>
{{- end -}}
</footer>
{{- end -}}
</article>
{{- with .TableOfContents -}}
<aside data-toc>
<h3>On this page</h3>
{{ . }}
</aside>
{{- end -}}
</section>
{{ end }}

View file

@ -19,7 +19,7 @@
{{- if $menu -}}
{{- range $menu -}}
{{- if .HasChildren -}}
<small>{{ .Name }}</small>
<h3>{{ .Name }}</h3>
<ul>
{{- range .Children -}}
<li>
@ -43,7 +43,7 @@
{{- end -}}
{{- else -}}
{{- with .CurrentSection -}}
<small>{{ .Title }}</small>
<h3>{{ .Title }}</h3>
<ul>
{{- range .RegularPages -}}
<li>
@ -58,20 +58,20 @@
{{- end -}}
</nav>
<article role="main">
<article>
{{ .Content }}
{{- if or .PrevInSection .NextInSection -}}
<footer data-role="prev-next">
{{- with .NextInSection -}}
<a href="{{ .RelPermalink }}" rel="prev">
<small>← Previous</small>
<span aria-hidden="true"></span> Previous
<span>{{ .LinkTitle }}</span>
</a>
{{- end -}}
{{- with .PrevInSection -}}
<a href="{{ .RelPermalink }}" rel="next">
<small>Next →</small>
Next <span aria-hidden="true"></span>
<span>{{ .LinkTitle }}</span>
</a>
{{- end -}}
@ -81,7 +81,7 @@
{{- with .TableOfContents -}}
<aside data-toc>
<small>On this page</small>
<h3>On this page</h3>
{{ . }}
</aside>
{{- end -}}

View file

@ -14,13 +14,13 @@
{{ define "content" }}
<section data-layout="docs">
<nav aria-label="Vault" data-nav="sidebar">
{{- $menuName := .Site.Params.vault_menu | default "vault" -}}
<nav aria-label="Notes" data-nav="sidebar">
{{- $menuName := .Site.Params.notes_menu | default "notes" -}}
{{- $menu := index .Site.Menus $menuName -}}
{{- if $menu -}}
{{- range $menu -}}
{{- if .HasChildren -}}
<small>{{ .Name }}</small>
<h3>{{ .Name }}</h3>
<ul>
{{- range .Children -}}
<li>
@ -44,7 +44,7 @@
{{- end -}}
{{- else -}}
{{- with .CurrentSection -}}
<small>{{ .Title }}</small>
<h3>{{ .Title }}</h3>
<ul>
{{- range .RegularPages -}}
<li>
@ -59,7 +59,7 @@
{{- end -}}
</nav>
<article role="main">
<article>
{{ .Content }}
{{- $prevURL := index .Params "prev-url" -}}
@ -70,13 +70,13 @@
<footer data-role="prev-next">
{{- if $prevURL -}}
<a href="{{ $prevURL }}" rel="prev">
<small>← Previous</small>
<span aria-hidden="true"></span> Previous
<span>{{ $prevTitle }}</span>
</a>
{{- end -}}
{{- if and $nextURL $nextTitle -}}
<a href="{{ $nextURL }}" rel="next">
<small>Next →</small>
Next <span aria-hidden="true"></span>
<span>{{ $nextTitle }}</span>
</a>
{{- end -}}
@ -86,7 +86,7 @@
{{- with .TableOfContents -}}
<aside data-toc>
<small>On this page</small>
<h3>On this page</h3>
{{ . }}
</aside>
{{- end -}}

View file

@ -0,0 +1,11 @@
{{ define "header" }}{{ end }}
{{ define "content" }}
<main>
<header>
<h1>{{ .Title }}</h1>
{{- with .Description }}<p>{{ . }}</p>{{ end }}
</header>
{{ .Content }}
</main>
{{ end }}

View file

@ -0,0 +1,41 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<title>
{{- if .IsHome -}}
{{ .Site.Title }}
{{- else -}}
{{ .Title }} · {{ .Site.Title }}
{{- end -}}
</title>
{{- with .Description }}<meta name="description" content="{{ . }}">{{- end }}
{{- if not .Description }}{{- with .Site.Params.description }}<meta name="description" content="{{ . }}">{{- end }}{{- end }}
{{- /* ── Meta partials ─────────────────────────────────────────── */}}
{{- partial "meta/seo.html" . -}}
{{- partial "meta/og.html" . -}}
{{- partial "meta/ai-disclosure.html" . -}}
{{- partial "meta/json-ld.html" . -}}
{{- /* ── CSS ────────────────────────────────────────────────────── */}}
{{- if hugo.IsDevelopment }}
<link rel="stylesheet" href="/vendor/open-props.min.css">
<link rel="stylesheet" href="/vendor/media.min.css">
<link rel="stylesheet" href="/css/layers/00-reset.css">
<link rel="stylesheet" href="/css/layers/01-asw.css">
<link rel="stylesheet" href="/css/layers/02-semantic.css">
<link rel="stylesheet" href="/css/layers/03-components.css">
<link rel="stylesheet" href="/css/layers/04-data-attrs.css">
<link rel="stylesheet" href="/css/layers/05-utilities.css">
<link rel="stylesheet" href="/css/layers/06-charts.css">
<link rel="stylesheet" href="/css/layers/07-chroma.css">
<link rel="stylesheet" href="/css/layers/08-layout.css">
<link rel="stylesheet" href="/css/layers/08a-paper.css">
<link rel="stylesheet" href="/css/layers/09-landing.css">
{{- else }}
<link rel="stylesheet" href="/asw.css">
{{- end }}
{{- range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{- end }}

View file

@ -38,6 +38,12 @@
vigilio-garden: --palette-hue: 150 (subtle green tint)
*/
/* What I think should go to palettes - @vigilio */
:root {
/* Palette controls
@ -49,15 +55,15 @@
/* ── Surfaces — dark end of the 16-step scale ─────────────────── */
--surface: var(--color-16); /* ~10% lightness */
--surface-1: var(--color-15); /* ~16% */
--surface-2: var(--color-14); /* ~20% */
--surface: var(--color-14); /* ~10% lightness */
--surface-1: var(--color-13); /* ~16% */
--surface-2: var(--color-11); /* ~20% */
--surface-card: var(--surface-1);
--surface-hover: var(--surface-2);
/* ── Text — light end of the scale ────────────────────────────── */
--text: var(--color-3); /* 93% lightness */
--text: var(--color-6); /* 93% lightness */
--text-2: var(--color-5); /* 80% */
--text-3: var(--color-7); /* 66% */
--text-dim: var(--color-9); /* 53% */
@ -389,3 +395,19 @@
@media (--lg-n-above) { html { font-size: 103%; } }
@media (--xl-n-above) { html { font-size: 106%; } }
@media (--xxl-n-above) { html { font-size: 109%; } }
/*
EDITORIAL DEFAULTS
Opinionated ASW decisions that go beyond tokens centering,
spacing rhythms, and layout choices that define the ASW look.
*/
/* Nav content centered at --width-xl without a wrapper element.
max() falls back to --container-padding on narrow viewports. */
body > nav {
padding-inline: max(var(--container-padding), calc((100% - var(--width-xl)) / 2));
}
[data-layout="docs"] > article {
max-width: var(--width-prose);
}

View file

@ -88,17 +88,17 @@ textarea {
font-family: var(--font-ui);
}
/* ── Nav layout ─────────────────────────────────────────────────────── */
/* Ported from Pico CSS, translated to ASW tokens. */
/* ── Top nav layout ──────────────────────────────────────────────────── */
/* Scoped to body > nav — sidebar and other navs are not affected. */
nav {
body > nav {
display: flex;
justify-content: space-between;
align-items: center;
overflow: visible;
}
nav ul {
body > nav ul {
display: flex;
align-items: center;
flex-wrap: wrap;
@ -108,13 +108,13 @@ nav ul {
list-style: none;
}
nav li {
body > nav li {
display: inline-block;
margin: 0;
padding: var(--space-2) var(--space-3);
}
nav li a {
body > nav li a {
display: inline-block;
text-decoration: none;
color: var(--text);
@ -123,19 +123,19 @@ nav li a {
border-radius: var(--radius-sm);
}
nav li a:hover {
body > nav li a:hover {
color: var(--accent);
background: var(--surface-hover);
}
nav li strong,
nav li b {
body > nav li strong,
body > nav li b {
color: var(--text);
}
@media (--md-n-below) {
nav { flex-wrap: wrap; gap: var(--space-2); }
nav ul { flex-wrap: wrap; gap: var(--space-1); }
body > nav { flex-wrap: wrap; gap: var(--space-2); }
body > nav ul { flex-wrap: wrap; gap: var(--space-1); }
}
/* ── Typography: Paragraphs ────────────────────────────────────────── */

View file

@ -2,10 +2,10 @@
* 03-components.css
* UI component patterns (buttons, forms, nav, dialog, details)
* Part of: Agentic Semantic Web
*
*
* Ported from: Pico CSS v2.1.1
* License: MIT
*
*
* Modernizations:
* - Uses `accent-color` for checkbox/radio (simpler than background-image)
* - Drops class-based button variants (.secondary, .contrast, .outline)
@ -42,8 +42,8 @@ button,
text-decoration: none;
cursor: pointer;
user-select: none;
transition: background-color var(--ease),
border-color var(--ease),
transition: background-color var(--ease),
border-color var(--ease),
color var(--ease);
}
@ -114,8 +114,8 @@ textarea {
background-color: var(--input-bg);
color: var(--text);
font-weight: var(--font-weight-4);
transition: background-color var(--ease),
border-color var(--ease),
transition: background-color var(--ease),
border-color var(--ease),
color var(--ease);
}
@ -189,7 +189,7 @@ label:has([type=checkbox], [type=radio]) {
margin-right: 0.5em;
vertical-align: middle;
cursor: pointer;
/* Modern CSS: use browser's native styling with our accent color */
accent-color: var(--accent);
}
@ -265,6 +265,7 @@ body > nav {
align-items: center;
padding-top: var(--space-5);
padding-bottom: var(--space-5);
margin-bottom: var(--space-6);
border-bottom: var(--border-width) solid var(--border);
}
@ -324,11 +325,11 @@ body > nav a[aria-current="page"] {
align-items: flex-start;
gap: var(--space-2);
}
body > nav ul:last-child {
flex-wrap: wrap;
}
body > nav ul:last-child li + li::before {
display: none;
}
@ -422,7 +423,7 @@ body > nav details > ul li a:hover {
/* Close dropdown when clicking outside (CSS-only via :focus-within) */
nav details:not(:focus-within) > ul,
nav details:not(:focus-within) > div {
/* Allow browser default open/close behavior
/* Allow browser default open/close behavior
no forced hiding. Agent can add JS for click-outside. */
}
@ -451,7 +452,7 @@ article {
background: transparent;
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 1rem 1.25rem;
padding: var(--size-3) var(--size-4);
margin: var(--space-3) 0;
}
@ -482,11 +483,11 @@ article header h3 {
padding-bottom: 0;
margin-bottom: 0.25rem;
}
article header h3 {
font-size: var(--text-xs);
}
article > :is(p, dl, ul, ol) {
font-size: var(--text-sm);
}
@ -497,7 +498,7 @@ article header h3 {
article {
padding: var(--space-5) var(--space-6);
}
article > header {
margin-bottom: var(--space-3);
padding-bottom: var(--space-2);
@ -953,3 +954,92 @@ dialog > footer {
[data-role="steps"][data-layout="vertical"] > li > span {
padding-inline: 0;
}
/* ── Sidebar nav ────────────────────────────────────────────────────── */
nav[data-nav="sidebar"] h3 {
color: var(--text-3);
font-size: var(--text-xs);
font-family: var(--font-mono);
font-weight: var(--font-weight-5);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-top: var(--space-4);
margin-bottom: 0;
}
nav[data-nav="sidebar"] h3:first-child {
margin-top: 0;
}
nav[data-nav="sidebar"] ul {
display: flex;
flex-direction: column;
list-style: none;
margin: 0;
padding: 0;
gap: var(--space-1);
font-family: var(--font-ui);
font-size: var(--text-sm);
}
nav[data-nav="sidebar"] a {
display: block;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
color: var(--text-2);
text-decoration: none;
transition: background-color var(--ease), color var(--ease);
}
nav[data-nav="sidebar"] a:hover {
background-color: var(--surface-hover);
color: var(--text);
}
nav[data-nav="sidebar"] a[aria-current] {
background-color: var(--accent-subtle);
color: var(--accent);
}
/* ── TOC (aside) ────────────────────────────────────────────────────── */
aside[data-toc] h3 {
color: var(--text-3);
font-size: var(--text-xs);
font-family: var(--font-mono);
font-weight: var(--font-weight-5);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: var(--space-2);
margin-top: 0;
}
aside[data-toc] nav ul {
display: flex;
flex-direction: column;
list-style: none;
margin: 0;
padding: 0;
gap: var(--space-1);
font-size: var(--text-xs);
}
aside[data-toc] nav a {
display: block;
padding: var(--space-1) var(--space-2);
color: var(--text-3);
text-decoration: none;
border-left: var(--border-size-2) solid transparent;
transition: color var(--ease), border-color var(--ease);
}
aside[data-toc] nav a:hover {
color: var(--text);
border-left-color: var(--border);
}
aside[data-toc] nav a[aria-current] {
color: var(--accent);
border-left-color: var(--accent);
}

View file

@ -13,71 +13,26 @@
}
/* ── Body landmark container ────────────────────────────────────────── */
/* All body-level landmarks share container alignment:
<body> > <nav>, <header>, <article role="main">, <footer> */
body > nav,
body > header,
body > article,
body > section,
body > footer {
body > nav { width: 100%; }
body > footer { width: 100%; }
/* Header: wider than article — room for hero titles, eyebrows, meta. */
body > header {
width: 100%;
margin-right: auto;
margin-left: auto;
padding-right: var(--container-padding);
padding-left: var(--container-padding);
max-width: var(--width-lg);
margin-inline: auto;
padding-inline: var(--container-padding);
}
@media (--sm-n-above) {
body > nav,
body > header,
body > article,
body > section,
body > footer {
max-width: var(--width-sm);
padding-right: 0;
padding-left: 0;
}
}
@media (--md-n-above) {
body > nav,
body > header,
body > article,
body > section,
body > footer {
max-width: var(--width-md);
}
}
@media (--lg-n-above) {
body > nav,
body > header,
body > article,
body > section,
body > footer {
max-width: var(--width-lg);
}
}
@media (--xl-n-above) {
body > nav,
body > header,
body > article,
body > section,
body > footer {
max-width: var(--width-xl);
}
}
@media (--xxl-n-above) {
body > nav,
body > header,
body > article,
body > section,
body > footer {
max-width: var(--width-2xl);
}
/* Article and section: standard content width. Reading width lives on
inner elements (p, li) via max-inline-size not on the container. */
body > article,
body > section {
width: 100%;
max-width: var(--width-lg);
margin-inline: auto;
padding-inline: var(--container-padding);
}
/* ── Body baseline ──────────────────────────────────────────────────── */
@ -148,106 +103,21 @@ nav[data-layout="actions"] a:hover {
align-items: start;
}
/* ── Left sidebar ───────────────────────────────────────────────────── */
/* ── Sidebar + TOC shared sticky behaviour ──────────────────────────── */
[data-layout="docs"] > nav[data-nav="sidebar"] {
[data-layout="docs"] > nav[data-nav="sidebar"],
[data-layout="docs"] > aside[data-toc] {
position: sticky;
top: calc(var(--nav-height) + var(--space-4));
max-height: calc(100vh - var(--size-px-10));
max-height: calc(100vh - var(--nav-height) - var(--space-4) * 2);
overflow-y: auto;
/* ASW-styled scrollbar — thin, subtle, no system chrome */
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
[data-layout="docs"] > nav[data-nav="sidebar"]::-webkit-scrollbar {
width: var(--scrollbar-size);
}
/* ── Left sidebar ───────────────────────────────────────────────────── */
[data-layout="docs"] > nav[data-nav="sidebar"]::-webkit-scrollbar-track {
background: transparent;
}
[data-layout="docs"] > nav[data-nav="sidebar"]::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-full);
}
[data-layout="docs"] > nav[data-nav="sidebar"]::-webkit-scrollbar-thumb:hover {
background: var(--text-3);
}
/* Sidebar nav — vertical list, no top-bar chrome */
nav[data-nav="sidebar"] {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding: 0;
margin: 0;
border: none;
gap: 0;
}
nav[data-nav="sidebar"] small {
display: block;
color: var(--text-3);
font-size: var(--text-xs);
font-family: var(--font-mono);
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0 var(--space-3);
margin-bottom: var(--space-2);
margin-top: var(--space-4);
}
nav[data-nav="sidebar"] small:first-child {
margin-top: 0;
}
nav[data-nav="sidebar"] ul {
display: flex;
flex-direction: column;
align-items: stretch;
list-style: none;
margin: 0;
padding: 0;
gap: var(--space-1);
font-family: var(--font-ui);
font-size: var(--text-sm);
}
nav[data-nav="sidebar"] ul li {
margin: 0;
padding: 0;
}
/* No pipe separators in sidebar */
nav[data-nav="sidebar"] ul li + li::before,
nav[data-nav="sidebar"] ul li + li::before {
display: none;
}
[data-layout="docs"] > nav[data-nav="sidebar"] nav a {
display: block;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
color: var(--text-2);
text-decoration: none;
text-align: left;
transition: background-color var(--ease), color var(--ease);
font-family: var(--font-ui);
font-size: var(--text-sm);
}
[data-layout="docs"] > nav[data-nav="sidebar"] nav a:hover {
background-color: var(--surface-hover);
color: var(--text);
}
[data-layout="docs"] > nav[data-nav="sidebar"] nav a[aria-current] {
background-color: var(--accent-subtle);
color: var(--accent);
[data-layout="docs"] > nav[data-nav="sidebar"] {
top: calc(var(--nav-height) + var(--space-4));
}
/* ── Main content ───────────────────────────────────────────────────── */
@ -259,77 +129,10 @@ nav[data-nav="sidebar"] ul li + li::before {
/* ── Right TOC ──────────────────────────────────────────────────────── */
[data-layout="docs"] > aside[data-toc] {
position: sticky;
top: calc(var(--nav-height) + var(--space-4));
max-height: calc(100vh - var(--size-px-10));
overflow-y: auto;
top: calc(var(--nav-height) + var(--space-8));
padding-top: var(--space-8);
}
/* TOC nav — vertical, compact, no top-bar chrome */
[data-layout="docs"] > aside[data-toc] nav,
nav[data-nav="toc"] {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding: 0;
margin: 0;
border: none;
gap: 0;
}
[data-layout="docs"] > aside[data-toc] small {
display: block;
color: var(--text-3);
font-size: var(--text-xs);
font-family: var(--font-mono);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: var(--space-2);
}
[data-layout="docs"] > aside[data-toc] nav ul,
nav[data-nav="toc"] ul {
display: flex;
flex-direction: column;
align-items: stretch;
list-style: none;
margin: 0;
padding: 0;
gap: var(--space-1);
font-size: var(--text-xs);
}
[data-layout="docs"] > aside[data-toc] nav ul li {
margin: 0;
padding: 0;
}
[data-layout="docs"] > aside[data-toc] nav ul li + li::before,
nav[data-nav="toc"] ul li + li::before {
display: none;
}
[data-layout="docs"] > aside[data-toc] nav a {
display: block;
padding: var(--space-1) var(--space-2);
font-size: var(--text-xs);
color: var(--text-3);
text-decoration: none;
text-align: left;
border-left: var(--border-size-2) solid transparent;
transition: color var(--ease), border-color var(--ease);
}
[data-layout="docs"] > aside[data-toc] nav a:hover {
color: var(--text);
border-left-color: var(--border);
}
[data-layout="docs"] > aside[data-toc] nav a[aria-current] {
color: var(--accent);
border-left-color: var(--accent);
}
/* ── Prev/Next navigation ───────────────────────────────────────────── */
@ -405,6 +208,62 @@ nav[data-nav="toc"] ul li + li::before {
min-width: 0;
}
/* ── Console layout ─────────────────────────────────────────────────── */
/* Docs variant: sidebar flush to the viewport left edge. */
/* No outer centering — grid spans full width, sidebar has no left gap. */
[data-layout="console"] {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr var(--toc-width);
grid-template-rows: auto;
gap: var(--space-6);
padding: var(--space-6) var(--space-5) var(--space-6) 0;
align-items: start;
}
[data-layout="console"] > nav[data-nav="sidebar"],
[data-layout="console"] > aside[data-toc] {
position: sticky;
max-height: calc(100vh - var(--nav-height) - var(--space-4) * 2);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
[data-layout="console"] > nav[data-nav="sidebar"] {
top: calc(var(--nav-height) + var(--space-4));
padding-inline: var(--space-3);
}
[data-layout="console"] > article {
min-width: 0;
max-width: none;
}
[data-layout="console"] > aside[data-toc] {
top: calc(var(--nav-height) + var(--space-8));
padding-top: var(--space-8);
}
@media (--docs-toc-hidden) {
[data-layout="console"] {
grid-template-columns: var(--sidebar-width) 1fr;
}
[data-layout="console"] > aside[data-toc] {
display: none;
}
}
@media (--md-n-below) {
[data-layout="console"] {
grid-template-columns: 1fr;
padding: var(--space-4);
}
[data-layout="console"] > nav[data-nav="sidebar"] {
display: none;
}
}
/* ── Grid helpers ───────────────────────────────────────────────────── */
[data-layout="grid-2"] {

View file

@ -20,5 +20,5 @@
@import "./layers/06-charts.css";
@import "./layers/07-chroma.css";
@import "./layers/08-layout.css";
@import "./layers/08a-paper.css";
@import "./layers/08a-essay.css";
@import "./layers/09-landing.css";

20
vendor/README.md vendored Normal file
View file

@ -0,0 +1,20 @@
# vendor/
Third-party CSS files vendored directly into the repo so the build has no runtime npm dependency.
## Contents
| Directory | Package | Version | Files vendored |
|-----------|---------|---------|----------------|
| `open-props/` | [open-props](https://open-props.style/) | 1.7.23 | `open-props.min.css`, `media.min.css` |
## How to update
1. Bump the version in `package.json` and run `npm install`.
2. Copy the new files:
```bash
cp node_modules/open-props/open-props.min.css vendor/open-props/open-props.min.css
cp node_modules/open-props/media.min.css vendor/open-props/media.min.css
```
3. Update `vendor/open-props/VERSION` with the new version and today's date.
4. Commit all three changed files together.

1
vendor/open-props/VERSION vendored Normal file
View file

@ -0,0 +1 @@
open-props@1.7.23 — vendored 2026-04-11

1
vendor/open-props/media.min.css vendored Normal file
View file

@ -0,0 +1 @@
@custom-media --motionOK (prefers-reduced-motion: no-preference);@custom-media --motionNotOK (prefers-reduced-motion: reduce);@custom-media --opacityOK (prefers-reduced-transparency: no-preference);@custom-media --opacityNotOK (prefers-reduced-transparency: reduce);@custom-media --useDataOK (prefers-reduced-data: no-preference);@custom-media --useDataNotOK (prefers-reduced-data: reduce);@custom-media --OSdark (prefers-color-scheme: dark);@custom-media --OSlight (prefers-color-scheme: light);@custom-media --highContrast (prefers-contrast: more);@custom-media --lowContrast (prefers-contrast: less);@custom-media --invertedColors (inverted-colors: inverted);@custom-media --forcedColors (forced-colors: active);@custom-media --portrait (orientation: portrait);@custom-media --landscape (orientation: landscape);@custom-media --HDcolor (dynamic-range: high) or (color-gamut: p3);@custom-media --touch (hover: none) and (pointer: coarse);@custom-media --stylus (hover: none) and (pointer: fine);@custom-media --pointer (hover) and (pointer: coarse);@custom-media --mouse (hover) and (pointer: fine);@custom-media --xxs-only (min-width: 0px) and (max-width: 239.98px);@custom-media --xxs-n-above (min-width: 240px);@custom-media --xxs-n-below (max-width: 239.98px);@custom-media --xxs-phone (--xxs-only) and (--portrait);@custom-media --xs-only (min-width: 240px) and (max-width: 359.98px);@custom-media --xs-n-above (min-width: 360px);@custom-media --xs-n-below (max-width: 359.98px);@custom-media --xs-phone (--xs-only) and (--portrait);@custom-media --sm-only (min-width: 360px) and (max-width: 479.98px);@custom-media --sm-n-above (min-width: 480px);@custom-media --sm-n-below (max-width: 479.98px);@custom-media --sm-phone (--sm-only) and (--portrait);@custom-media --md-only (min-width: 480px) and (max-width: 767.98px);@custom-media --md-n-above (min-width: 768px);@custom-media --md-n-below (max-width: 767.98px);@custom-media --md-phone (--md-only) and (--portrait);@custom-media --lg-only (min-width: 768px) and (max-width: 1023.98px);@custom-media --lg-n-above (min-width: 1024px);@custom-media --lg-n-below (max-width: 1023.98px);@custom-media --lg-phone (--lg-only) and (--portrait);@custom-media --xl-only (min-width: 1024px) and (max-width: 1439.98px);@custom-media --xl-n-above (min-width: 1440px);@custom-media --xl-n-below (max-width: 1439.98px);@custom-media --xxl-only (min-width: 1440px) and (max-width: 1919.98px);@custom-media --xxl-n-above (min-width: 1920px);@custom-media --xxl-n-below (max-width: 1919.98px);

1
vendor/open-props/open-props.min.css vendored Normal file

File diff suppressed because one or more lines are too long