wal.sh Design System

Table of Contents

1. Brand identity

1.1. Site purpose

wal.sh is a personal research archive. It collects technical notes, conference observations, and extended investigations in the areas of programming languages, distributed systems, AI agent architectures, and formal methods. The audience is technical and self-selecting. There is no marketing copy and no onboarding flow. Readers arrive already knowing what they are looking for.

The site does not have a product to sell. Its purpose is to make research thinking legible across time and across sessions.

1.2. Voice

The writing voice is direct and claim-forward. Notes open with an assertion or a concrete fact, not a description of what the note will cover. Abstractions are introduced after the concrete example that motivates them, not before.

Sentences are complete. Lists are used for enumeration, not as a substitute for prose. Code blocks contain code; prose describes what the code does and why it matters.

1.3. Visual tone

The visual language is quiet and scholarly. The page presents text with a minimum of decoration. Color appears in diagrams, not in navigation or prose layout. The chrome (header, footer, links) is subdued — greys and a single blue accent. Diagrams are the visual centerpiece of each research page; they carry structure and color while the surrounding page recedes.

The design deliberately avoids:

  • Pull quotes, callout boxes, or highlight panels on prose sections
  • Colored backgrounds behind text
  • Decorative icons in navigation
  • Heavy border treatments on content elements

The one exception is the .ai block, which uses a pale blue left-border treatment to mark content that originated from an AI assistant.

2. Color palette

2.1. Primary palette: Tailwind 100/700 pastel system

This palette governs all Graphviz diagrams. Each color family encodes a fixed semantic role across all diagrams on the site. The mapping must not be broken for aesthetic reasons — it is a protocol, not a preference.

Family Fill (100) Border / Text (700) Cluster BG (50) Semantic role
Blue #dbeafe #1d4ed8 #eff6ff Primary data flow, input, process
Purple #ede9fe #6b21a8 #f5f3ff Abstraction, protocol, API
Green #dcfce7 #15803d #f0fdf4 Output, success, storage
Amber #fef3c7 #b45309 #fffbeb Config, orchestration, external
Yellow #fef9c3 #a16207 #fefce8 Warning, intermediate, cache
Red #fee2e2 #b91c1c #fef2f2 Error, critical path, alert

The 100-weight fills are the pastel node backgrounds. The 700-weight values serve triple duty: node border, node text color, and cluster label. The 50-weight is for cluster backgrounds only, lighter than the node fill so the cluster recedes behind its contents.

All hex values are full 6-digit. Shorthand hex (#d63) is prohibited. Named HTML colors (lightblue, yellow) are prohibited. They are not reproducible across Graphviz renderers.

2.2. Neutral palette: CSS variables and prose greys

These values come from the style.css :root block and are used for page chrome, text, and UI elements.

Variable Value Usage
--bg #fafafa Page background
--bg-content #ffffff Content area background
--text #1a1a1a Body text, headings
--text-secondary #666 Author, date, nav links, secondary labels
--accent #0066cc Links, blockquote border, postamble links
--accent-light #e6f2ff Link hover background, .ai block background
--border #eaeaea Horizontal rules, table cell borders, header border
--code-bg #f6f8fa Inline code background, pre background
--success #4CAF50 DONE todo-state badge
--warning #FFA500 TODO todo-state badge

Additional grey values appear in component-specific contexts:

Value Context
#888 Diagram edge default color, webring muted label
#555 Diagram edge label text
#444 Webring border fallback (if --border not set)

2.3. Meaning mapping across the site

The same blue accent (#0066cc) serves as the link color in prose, the blockquote border, and the .ai block border. This creates a consistent "interactive or generated" signal: anything with that blue treatment is either a link to follow or content that came from an AI source.

Diagram colors are intentionally distinct from the CSS neutral palette. The pastel diagram fills (#dbeafe, #dcfce7, etc.) do not appear in the site CSS. This separation prevents visual bleed between page chrome and diagram semantics.

3. Typography

3.1. Body text

Property Value
Font family Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif
Font size 17px (desktop), 16px (mobile, max-width 768px)
Font weight 400 (regular)
Line height 1.8
Color #1a1a1a (--text)
Max width 1200px (1400px at min-width 1400px)
Padding 3rem clamp(1rem, 5vw, 4rem)

3.2. Headings

All headings use font-weight: 700 and letter-spacing: -0.02em. Color is #1a1a1a, same as body text.

Level Size Top margin
h1 2.5rem 2.5rem
h2 1.75rem 3rem
h3 1.25rem 2.5rem
h4 1.1rem 2.5rem
h5 1rem 2.5rem
h6 0.9rem 2.5rem

The page h1.title from org-publish is hidden via #content > h1 { display: none }. The visible page title is emitted by the preamble as a <header class"page-header"><h1>= on non-index pages. Index pages use the org-mode h1 directly.

3.3. Links

Links use color: var(--accent) (#0066cc) with text-decoration: underline and text-underline-offset: 2px. On hover, the background becomes var(--accent-light) (#e6f2ff). Nav links in the site header suppress the underline and use var(--text-secondary) at 0.9rem.

3.4. Code blocks

Two distinct code block treatments coexist:

3.4.1. Inline code and plain pre blocks

Property Value
Font family JetBrains Mono, Fira Code, Consolas, monospace
Background #f6f8fa (--code-bg)
Border 1px solid #eaeaea (--border) for pre
Border radius 4px
Font size (inline) 0.85em
Font size (pre) 0.9rem
Line height (pre) 1.5
Padding (inline) 0.15rem 0.35rem
Padding (pre) 1.25rem

3.4.2. Org src blocks (language-tagged)

Org source blocks with a language tag (#+begin_src python, etc.) receive the Catppuccin Mocha dark theme treatment:

Property Value
Background #1e1e2e
Text #cdd6f4
Border None (dark background makes a border redundant)
Border radius 8px
Box shadow 0 2px 8px rgba(0,0,0,0.15)

A language label is prepended via CSS ::before pseudo-element in #6c7086 at 0.7rem uppercase. The label classes are: src-python, src-javascript, src-typescript, src-rust, src-json, src-sql, src-bash / src-shell / src-sh, src-elisp / src-emacs-lisp, src-scheme, src-clojure, src-yaml, src-toml, src-hcl. Blocks tagged src-text suppress the label entirely.

Syntax highlighting uses semantic CSS classes output by Emacs htmlize in css mode:

Class Color Meaning
.org-keyword #cba6f7 Keywords (def, return, if, etc.)
.org-string #a6e3a1 Strings and docstrings
.org-builtin #f38ba8 Built-in functions
.org-function-name #89b4fa Function / method names
.org-variable-name #f9e2af Variable names
.org-type #89dceb Type names and annotations
.org-constant #fab387 Constants, numbers, True/False
.org-comment #6c7086 Comments (italic)
.org-preprocessor #f5c2e7 Decorators
.org-operator #94e2d5 Operators and punctuation

3.5. Diagram text

All diagram text uses fontname"Helvetica"=. Three sizes, applied consistently across all diagrams:

Element fontsize
Graph label 11
Node label 10
Edge label 9

3.6. Secondary text

Navigation links, author lines, dates, and footer text use var(--text-secondary) (#666) at reduced sizes (0.85rem–0.95rem).

4. Layout

4.1. Page structure

Every published page follows this vertical structure:

<header class="site-header">          preamble: sitewide nav + logo
<div class="breadcrumbs">             preamble: path from home to current page
<header class="page-header">          preamble: page h1 (non-index pages only)

  [org-mode content body]

    banner image (728x90 greyscale)
    prose sections
    diagrams
    tables
    code blocks

#postamble                             postamble: author, date, build info
                                       postamble: adtech includes (conditional)
                                       postamble: web-vitals (conditional)
.webring                               postamble: webring navigation

The content body is the org-mode HTML export. The page title h1 is suppressed from the org export (display: none) and re-emitted by the preamble so it appears before breadcrumbs do not interrupt the title.

4.2. Sitewide header

The site header is a flex row with justify-content: space-between, separated from content by a 1px #eaeaea border-bottom and 3rem margin-bottom.

Left: navigation list with four items (Home, Current, Research, Events) in var(--text-secondary) at 0.9rem.

Right: logo image (48x48px) linking to /, wrapped in .site-logo.

4.3. Breadcrumbs

Generated programmatically from the file path relative to the site root. The root index page emits no breadcrumbs. All other pages show a path: home > section > subsection > page (YYYY-MM-DD). Breadcrumb links use var(--text-secondary) at 0.85rem, with last-modified date in parentheses on the leaf entry.

4.4. Banner

Each research page opens with a 728x90 greyscale leaderboard banner image. Banners sit between the page-header and the first body section. Images are centered via display: block; margin: 2rem auto.

Org reference form:

#+ATTR_HTML: :alt <description> :class banner-leaderboard :width 728 :height 90
[[file:banner.png]]

Banner images are produced from a source graphic using:

magick input.png -resize 728x90^ -gravity center -extent 728x90 -colorspace Gray output.png

The greyscale constraint is intentional: diagrams carry the color on the page. A color banner would compete with the diagrams for attention.

4.5. Diagrams

Diagrams are PNG images exported from Graphviz .dot files by org-babel during publish. They are placed via standard org image links:

#+CAPTION: <description of what the diagram shows>
#+NAME: fig:<identifier>
#+ATTR_HTML: :alt <alt text describing content for screen readers>
[[file:<diagram-name>.png]]

Images are max-width: 100%, height: auto, centered with 2rem margins. No explicit width or height is set in HTML for diagram images; they scale to content width.

4.6. Postamble

The postamble is generated by wal-sh/org-html-postamble. It contains:

  1. <p class"author">= — Author name
  2. <p class"email">= — Email in angle brackets (via CSS ::before / ::after)
  3. <p class"date">= — Last modified timestamp from file system
  4. <p class"build-info"><code>= — build timestamp and git SHA

Followed by conditional adtech performance-art includes, web-vitals measurement, and the webring navigation element.

The #postamble element uses font-size: 0.85em, color: var(--text-secondary), centered, with a 1px #eaeaea border-top and 3rem top margin.

5. Component inventory

5.1. Banners

728x90 pixels, greyscale, leaderboard format. One per research page, placed immediately after the page title / first section heading. Alt text is required and should describe the visual content meaningfully for screen readers. The :class banner-leaderboard attribute is always set.

5.2. Diagrams

Graphviz dot diagrams, compiled to PNG via org-babel #+begin_src dot blocks during publish. Every diagram follows the palette and node conventions described in section 6. Each diagram image requires:

  • #+CAPTION — prose description of the claim the diagram makes
  • #+NAME — a fig: prefixed identifier for internal linking
  • #+ATTR_HTML with :alt — screen-reader description

The site had 53+ dot files as of 2026-05-19, all following the canonical palette. Mermaid diagrams in older pages have been converted to dot.

5.3. Code blocks

Org source blocks with language tags export with the dark Catppuccin Mocha theme. Plain #+begin_example blocks export as plain grey <pre> elements. The language label is rendered via CSS ::before.

The :eval no :tangle no header args appear on code blocks that are documentation examples not intended for execution.

5.4. Tables

Org tables export as HTML tables with width: 100%, border-collapse: collapse, and margin: 1.5rem 0. Cell padding is 0.75rem 1rem. Rows are separated by a 1px #eaeaea border-bottom. Headers use font-weight: 600. No zebra striping. Tables scroll horizontally on narrow viewports.

5.5. Blockquotes

Left border: 3px solid #0066cc (var(--accent)). Italic text in var(--text-secondary). No background fill.

5.6. AI blocks

<div class"ai">= is used to mark content that originated from an AI assistant. It uses var(--accent-light) background and a 3px var(--accent) left border, matching the blockquote treatment. A copy button (.copy-javascript) is positioned absolutely in the top-right corner.

5.7. Todo state badges

Org TODO/DONE keywords export as inline badges:

State Color
TODO #FFA500 text on 10% orange bg
DONE #4CAF50 text on 10% green bg

0.7em, uppercase, padded, border-radius: 3px.

5.8. Property drawers

Exported as .property-drawer in var(--text-secondary) at 0.8em, using font-family: monospace and white-space: pre.

5.9. External and internal links

Both internal [[file:...]] links and external [[https://...]] links render identically: #0066cc with underline, pale blue hover background. The distinction between internal and external is not visually signaled beyond what the URL itself communicates.

5.10. Webring navigation

The webring element appears at the very bottom of every page, outside the main postamble block. It is a <nav class"webring">= with prev and next links populated by /static/js/webring.js at runtime.

Styling: centered, padding: 0.75rem 0, border-top: 1px solid var(--border), font-size: 0.85rem, opacity: 0.7 (rising to 1 on hover). The .ring-label element uses font-style: italic and color: var(--muted, #888).

6. Diagram conventions

6.1. Three governing principles

  1. Color encodes semantics, not decoration. Each of the six color families carries a fixed meaning across all diagrams. Blue is input and primary flow. Green is output and success. Red is error and critical path. A reader can identify state semantics from color before reading labels.
  2. Diagrams carry the color; banners stay quiet. Research page banners are greyscale. The pastel diagrams are the visual centerpiece of the page. A color banner would compete with the diagram for the reader's attention.
  3. The palette is a protocol, not a preference. The Tailwind 100/700 pairs were chosen because they are widely known, machine-readable, and have accessible contrast ratios. The cost of replacing them would require updating every diagram simultaneously.

6.2. Node conventions

Every node uses shape=box with style"rounded,filled"=. Three color attributes are mandatory and must come from the same family:

  • fillcolor — 100-weight pastel fill
  • color — 700-weight border
  • fontcolor — 700-weight text (black text on a pastel node means this attribute is missing)

Special shapes:

  • shape=note — annotation nodes and legend entries; still uses the palette
  • shape=diamond — decision points in pipelines; typically amber
  • shape=plain — invisible structural nodes (start/end markers in state machines); no fill, no border

6.3. Cluster conventions

Default cluster style is border-only: a colored border with a white interior. The cluster border and label both use the 700-weight of the dominant color family of its contents.

subgraph cluster_name {
    label="Cluster Title";
    style="rounded";
    color="#1d4ed8";      // border: 700-weight
    fontcolor="#1d4ed8";  // label: 700-weight
    fontname="Helvetica";
    fontsize=11;
}

Use style"rounded,filled"= with a 50-weight fillcolor only when the cluster background needs to visually separate regions — layer stacks or side-by-side comparisons. Prefer border-only for all other cases. Never use #fafafa for clusters; it has no semantic meaning.

6.4. Edge conventions

Condition color fontcolor style
Default #888 #555 solid
Failure / error path #b91c1c #b91c1c solid
Optional / fallback #888 #555 dashed
Deprecated / pressure #888 #555 dotted
Feedback loop #888 #555 dashed

The feedback edge in a process loop is always dashed and uses the default grey, signaling that the loop is structural rather than representing an error or degraded path.

6.5. The six archetypes

6.5.1. Pipeline

Data flowing through sequential transformations. Layout: rankdir=LR. Color follows the data lifecycle: blue for ingestion, amber for processing, green for output, yellow for intermediate caches and queues. Clusters group stages by concern; each cluster border matches its dominant color family.

Examples: diagram-rag-pipeline, diagram-ci-cd-pipeline, diagram-observability, jq/diagram-pipeline.

6.5.2. Layer stack

Architectural layers from top (user-facing) to bottom (substrate). Layout: rankdir=TB. Each layer is a cluster with a distinct color family. Top layer is typically blue (closest to the user); lower layers shift through purple, amber, and green. Cross-layer edges use default grey; failure-path edges use red.

Examples: diagram-feature-surface, diagram-tool-systems-layers, diagram-llm-frameworks-taxonomy.

6.5.3. Comparison

Two or more systems placed side by side. Layout: rankdir=TB with clusters arranged horizontally. Each system gets a distinct color family. The contrast between cluster colors is the primary visual signal. Rule: never use the same color family for both sides of a comparison.

Examples: diagram-isolation-comparison (blue vs amber), diagram-datalayer-schema-compare (four formats, four colors), diagram-eda-vs-actor.

6.5.4. State machine

Transitions between states. Layout: rankdir=LR. Each state gets the color that reflects its semantic nature: green for healthy/fresh, amber for degraded/stale, blue for active/processing, purple for fallback, red for terminal/error. State machines are the only archetype where individual nodes routinely use different color families within the same diagram.

Examples: diagram-cache-states (HTTP cache per RFC 9111), diagram-order-states, diagram-process-lifecycle.

6.5.5. Process loop

Cyclical workflow with named phases. Layout: rankdir=LR to emphasize forward flow, with a dashed feedback edge closing the loop. Each phase is a cluster with a distinct color family, cycling through blue, purple, amber, green. Exception nodes use red with red dashed edges.

Example: diagram-review-loop (elenctic vibe code review).

6.5.6. Ecosystem map

Relationships between tools, libraries, or systems in a domain. Layout: rankdir=TB with clusters grouping related items by category. Color assignment follows function: blue for core implementations, purple for protocols/standards, green for outputs/tooling, amber for configuration/build, yellow for community/ecosystem, red for deprecated or risky components.

Examples: diagram-scheme-llm-toolkit, diagram-ecosystem (Unix V4 games), diagram-clojure-ecosystem.

6.6. ASCII diagram policy

ASCII arrow diagrams in #+begin_example blocks that represent data flows, state machines, or architectures should be converted to Graphviz dot diagrams. ASCII directory trees (├── .config/) and inline prose arrows stay as text.

6.7. What to avoid

  • Shorthand hex (#369, #d63)
  • Named HTML colors (lightblue, yellow)
  • Dark backgrounds with light text (inverts the site's light theme)
  • Hard saturated fills (always use the 100-weight pastel)
  • Non-Helvetica fonts
  • Neutral grey clusters (#444 / #fafafa)
  • Missing fontcolor (produces black text on pastel background)

7. Content conventions

7.1. Required org headers

Every published page requires these five headers:

Header Example value
#+TITLE Claude Code Features --- 2026 Q2
#+AUTHOR Jason Walsh
#+EMAIL j@wal.sh
#+DATE 2026-04-17
#+DESCRIPTION One paragraph, used for search and social sharing

Optional but common:

  • #+URL — canonical URL for the page
  • #+KEYWORDS — comma-separated, lowercase, hyphen-separated
  • #+OPTIONS: toc:2 num:t — table of contents depth and section numbering
  • #+SUBTITLE — appears under the title in some contexts

7.2. Template structure

New research notes are scaffolded from site/templates/research-note-dir/index.org for directory-form notes, or site/templates/research-note.org for flat files. The template provides the correct header set and a placeholder body. The slug must be kebab-case (^[a-z0-9][a-z0-9-]*[a-z0-9]$).

7.3. Research pages vs event pages

Research pages live in site/research/ (flat .org files) or site/research/<slug>/index.org (directory form). Directory form is preferred for notes that include multiple images, diagrams, or supplementary files.

Event pages live in site/events/<conference>/<year>/index.org. They record observations from attended conferences and follow the same header requirements.

7.4. current focus section on the homepage

The * current focus section in site/index.org lists recent research in reverse chronological order, newest first. Each entry follows this format:

- YYYY-MM [[file:research/<slug>/index.org][display title]] --- one-line abstract

Older items are moved to the * 2025, * past, or ** 20XX sections below current focus. The current focus section should contain only the most recent entries (roughly the last six to twelve months).

External links (GitHub repos, external sites) use [[https://...][title]] in the same list.

7.5. Org link forms

Internal links to published pages use [[file:research/<slug>/index.org]] or [[file:research/<slug>.org]]. The Emacs export filter wal-sh/clean-url-filter rewrites foo/index.html to foo/ and foo.html to foo in the published HTML, producing clean extensionless URLs.

7.6. Excluded from publish

The following are excluded from org-publish:

  • README.org, SECURITY.org, CLAUDE.md, HEALTH_CHECKS.org
  • site/includes/ — HTML fragments assembled at publish time
  • site/_drafts/ — work in progress not ready for publication
  • site/templates/ — scaffolding files
  • site/research/ai_model_outputs/ — raw data files
  • Files matching *-YYYY.org — year-template placeholders

7.7. Build info

The postamble includes a build timestamp and git SHA in the form build: YYYY-MM-DD HH:MM | sha: <short-sha>. This appears in a <code> element inside a .build-info paragraph.

7.8. Draft workflow

Half-formed notes belong in site/_drafts/. That directory is excluded from org-publish, so drafts can be committed without appearing on the live site. When ready, the file is moved into the appropriate site/research/ or site/events/ path.

Author: Jason Walsh

j@wal.sh

Last Updated: 2026-05-20 03:20:22

build: 2026-05-20 03:35 | sha: 12ce5fe