Initial release: diagram-design v1.0
Thirteen editorial diagram types (architecture, flowchart, sequence, state, ER, timeline, swimlane, quadrant, nested, tree, layers, venn, pyramid) as self-contained HTML files with inline SVG. Progressive disclosure: lean SKILL.md points to per-type references. Neutral stone+rust default skin, first-run gate that prompts users to customize the style guide from their own website, URL-based onboarding, annotation-callout primitive, sketchy filter variant, live gallery at assets/index.html.
@@ -0,0 +1,214 @@
|
|||||||
|
# Diagram Design
|
||||||
|
|
||||||
|
**I wanted good-looking diagrams for my blog posts.**
|
||||||
|
|
||||||
|
I write at [littlemight.com](https://littlemight.com). Every time I needed a diagram — an architecture sketch, a flowchart, a pyramid of what matters most — I'd ask Claude and get back a generic rounded-box thing that looked nothing like the rest of the site. I'd either fight with Figma for 30 minutes or just skip the diagram.
|
||||||
|
|
||||||
|
So I built a Claude Code skill for it. 13 types, editorial quality, matches your brand in 60 seconds by reading your website.
|
||||||
|
|
||||||
|
> *The highest-quality move is usually deletion.* Every node earns its place. The accent color is reserved for the 1–2 things the reader should look at first. Target density: 4/10.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What it makes
|
||||||
|
|
||||||
|
All 13 diagrams ship in three variants: minimal light, minimal dark, and full-editorial. Open any of them directly in a browser — no build step, no JS, no external images.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center" width="33%"><img src="docs/screenshots/architecture.png" alt="Architecture"><br><b>Architecture</b><br><sub>Components + connections</sub></td>
|
||||||
|
<td align="center" width="33%"><img src="docs/screenshots/flowchart.png" alt="Flowchart"><br><b>Flowchart</b><br><sub>Decision logic</sub></td>
|
||||||
|
<td align="center" width="33%"><img src="docs/screenshots/sequence.png" alt="Sequence"><br><b>Sequence</b><br><sub>Messages over time</sub></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><img src="docs/screenshots/state.png" alt="State machine"><br><b>State machine</b><br><sub>States + transitions</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/er.png" alt="ER / data model"><br><b>ER / data model</b><br><sub>Entities + fields</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/timeline.png" alt="Timeline"><br><b>Timeline</b><br><sub>Events on an axis</sub></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><img src="docs/screenshots/swimlane.png" alt="Swimlane"><br><b>Swimlane</b><br><sub>Cross-functional flow</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/quadrant.png" alt="Quadrant"><br><b>Quadrant</b><br><sub>Two-axis positioning</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/nested.png" alt="Nested"><br><b>Nested</b><br><sub>Hierarchy by containment</sub></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><img src="docs/screenshots/tree.png" alt="Tree"><br><b>Tree</b><br><sub>Parent → children</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/layers.png" alt="Layers"><br><b>Layer stack</b><br><sub>Stacked abstractions</sub></td>
|
||||||
|
<td align="center"><img src="docs/screenshots/venn.png" alt="Venn"><br><b>Venn</b><br><sub>Set overlap</sub></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" colspan="3"><img src="docs/screenshots/pyramid.png" alt="Pyramid" width="40%"><br><b>Pyramid / funnel</b><br><sub>Ranked hierarchy or conversion drop-off</sub></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
**Browse the live gallery:** open [`assets/index.html`](assets/index.html) in your browser to flip through all 13 types with light / dark / full-editorial tabs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone into your Claude Code skills directory
|
||||||
|
git clone git@github.com:cathrynlavery/diagram-design.git ~/.claude/skills/diagram-design
|
||||||
|
|
||||||
|
# Or if you keep skills elsewhere, symlink it
|
||||||
|
git clone git@github.com:cathrynlavery/diagram-design.git ~/code/diagram-design
|
||||||
|
ln -s ~/code/diagram-design ~/.claude/skills/diagram-design
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Claude Code. The skill registers as `diagram-design` and activates whenever you ask Claude to make a diagram.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Onboarding — make it look like *your* brand
|
||||||
|
|
||||||
|
The whole point: ship editorial-quality diagrams in **your** colors and typography, not a generic template.
|
||||||
|
|
||||||
|
Out of the box, diagrams render in a neutral **stone + rust** palette (warm off-white paper, deep charcoal ink, rust-orange accent). Good enough to screenshot straight away. But 60 seconds of onboarding is better — the skill will pull your brand from your website and apply it across every diagram.
|
||||||
|
|
||||||
|
### The flow
|
||||||
|
|
||||||
|
```
|
||||||
|
You: "onboard diagram-design to https://yoursite.com"
|
||||||
|
Claude: → fetches the homepage
|
||||||
|
→ extracts the dominant palette + font stack
|
||||||
|
→ maps detected values to semantic roles:
|
||||||
|
paper, ink, muted, accent, link
|
||||||
|
→ shows a proposed diff
|
||||||
|
→ writes your tokens to references/style-guide.md
|
||||||
|
You: "yes, apply it"
|
||||||
|
```
|
||||||
|
|
||||||
|
Every new diagram now uses your colors. Your website's paper color becomes the diagram background. Your CTA color becomes the focal accent. Your body font stack becomes the node label family.
|
||||||
|
|
||||||
|
### What gets extracted
|
||||||
|
|
||||||
|
| Detected from your site | Becomes |
|
||||||
|
|---|---|
|
||||||
|
| `<body>` background | `paper` token |
|
||||||
|
| Primary text color | `ink` token |
|
||||||
|
| Secondary / caption text | `muted` token |
|
||||||
|
| Cards or containers | `paper-2` token |
|
||||||
|
| Most-used brand color (CTA, link, heading) | `accent` token |
|
||||||
|
| `<h1>` font family | `title` font |
|
||||||
|
| `<body>` font family | `node-name` font |
|
||||||
|
| `<code>` / `<pre>` font | `sublabel` font |
|
||||||
|
|
||||||
|
### Contrast checks happen automatically
|
||||||
|
|
||||||
|
Before writing tokens, the skill verifies WCAG AA contrast on `ink` over `paper`. If your site has a color that fails contrast at diagram sizes (9–12px), it proposes an adjusted value and explains why.
|
||||||
|
|
||||||
|
### Manual override
|
||||||
|
|
||||||
|
Prefer to set tokens by hand? Open [`references/style-guide.md`](references/style-guide.md) and edit the table. Everything downstream reads from there — the 13 types, the annotation primitive, and the gallery all inherit semantic role names (`accent`, not `#b5523a`).
|
||||||
|
|
||||||
|
### First-run gate
|
||||||
|
|
||||||
|
The skill won't silently ship default-skinned diagrams into a branded project. On first use in a new project, it checks if `style-guide.md` has been customized. If not, it pauses and asks:
|
||||||
|
|
||||||
|
> *"This is your first diagram in this project. The style guide is still at the default. Want to run onboarding, paste tokens manually, or proceed with default?"*
|
||||||
|
|
||||||
|
See [`references/onboarding.md`](references/onboarding.md) for the full spec.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Open the gallery to see all 13 types
|
||||||
|
open ~/.claude/skills/diagram-design/assets/index.html
|
||||||
|
|
||||||
|
# In Claude Code, just ask:
|
||||||
|
# "Make me an architecture diagram of my app: frontend, backend, database, Redis cache."
|
||||||
|
# "I need a quadrant showing Q2 projects by impact vs effort."
|
||||||
|
# "Give me a sequence diagram of the OAuth handshake."
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude will pick the right type, build the HTML, and save it. You can also start from a template directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/template.html my-diagram.html # minimal light
|
||||||
|
cp assets/template-full.html my-diagram.html # editorial with summary cards
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Progressive disclosure. `SKILL.md` is a lean index — it tells Claude how to pick a type and where to look for detail. Every type lives in its own reference file, loaded only when relevant.
|
||||||
|
|
||||||
|
```
|
||||||
|
diagram-design/
|
||||||
|
├── SKILL.md — top-level: philosophy, selection guide, checklist
|
||||||
|
├── references/ — loaded only when a type or primitive is chosen
|
||||||
|
│ ├── style-guide.md — single source of truth for colors + fonts
|
||||||
|
│ ├── onboarding.md — the URL-to-tokens flow
|
||||||
|
│ ├── type-architecture.md
|
||||||
|
│ ├── type-flowchart.md
|
||||||
|
│ ├── type-sequence.md
|
||||||
|
│ ├── type-state.md
|
||||||
|
│ ├── type-er.md
|
||||||
|
│ ├── type-timeline.md
|
||||||
|
│ ├── type-swimlane.md
|
||||||
|
│ ├── type-quadrant.md
|
||||||
|
│ ├── type-nested.md
|
||||||
|
│ ├── type-tree.md
|
||||||
|
│ ├── type-layers.md
|
||||||
|
│ ├── type-venn.md
|
||||||
|
│ ├── type-pyramid.md
|
||||||
|
│ ├── primitive-annotation.md — italic-serif editorial callouts
|
||||||
|
│ └── primitive-sketchy.md — hand-drawn SVG filter variant
|
||||||
|
├── assets/
|
||||||
|
│ ├── index.html — live gallery, tabbed
|
||||||
|
│ ├── template*.html — scaffolds for new diagrams
|
||||||
|
│ └── example-<type>.html — 3 variants × 13 types
|
||||||
|
└── docs/screenshots/ — the images in this README
|
||||||
|
```
|
||||||
|
|
||||||
|
This keeps Claude's working context tight (only load what you need) and makes the skill easy to extend — drop a new `type-<name>.md` and wire it into the selection guide.
|
||||||
|
|
||||||
|
### What loads when
|
||||||
|
|
||||||
|
The top-level `SKILL.md` is always in context. Everything else is pulled in only when relevant — this is what keeps the skill fast even with 15 reference files.
|
||||||
|
|
||||||
|
| You ask for… | Claude loads |
|
||||||
|
|---|---|
|
||||||
|
| "Make me a flowchart" | `SKILL.md` + `references/type-flowchart.md` |
|
||||||
|
| "Build an architecture diagram" | `SKILL.md` + `references/type-architecture.md` |
|
||||||
|
| "Onboard this skill to my site" | `SKILL.md` + `references/onboarding.md` + `references/style-guide.md` |
|
||||||
|
| "Add an editorial callout to this diagram" | `SKILL.md` + `references/primitive-annotation.md` |
|
||||||
|
| "Give me a hand-drawn version" | `SKILL.md` + `references/primitive-sketchy.md` |
|
||||||
|
| Routine diagram-making (any of the 13 types) | Only `SKILL.md` + that one type's reference |
|
||||||
|
|
||||||
|
No matter how many types exist, Claude only reads the one you need. Add a new type tomorrow and nothing else changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The design system (in one paragraph)
|
||||||
|
|
||||||
|
One accent color, 1–2 focal elements per diagram. Three font families: Instrument Serif (title + italic callouts), Geist sans (node names), Geist Mono (technical sublabels). 1px hairline borders, no shadows, max border-radius 10px. Every coord, width, and gap divisible by 4 — non-negotiable, it's what keeps the diagrams from feeling AI-generated. Mono is for technical content (ports, URLs, field types), not a blanket "dev" aesthetic. Coral-tinted focal nodes draw the eye to the 1–2 things that matter. Full spec in [`SKILL.md`](SKILL.md#5-design-system).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Primitives
|
||||||
|
|
||||||
|
- **Annotation callout** — italic Instrument Serif + dashed Bézier leader, for editorial asides that sit in the margins. See [`references/primitive-annotation.md`](references/primitive-annotation.md).
|
||||||
|
- **Sketchy filter** — SVG turbulence + displacement map for a hand-drawn variant. Good for essays, not for technical docs. See [`references/primitive-sketchy.md`](references/primitive-sketchy.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When *not* to use this skill
|
||||||
|
|
||||||
|
- **Quick unicode diagrams** for tweets or terminal output → wiretext-style skill.
|
||||||
|
- **Lists of anything** → a table or bullets.
|
||||||
|
- **Before/after comparisons** → a table.
|
||||||
|
- **One-shape "diagrams"** — a single box with a label → just write the sentence.
|
||||||
|
|
||||||
|
Before drawing, ask: *would a reader learn more from this than from a well-written paragraph?* If no, don't draw.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Made by **Cathryn Lavery** — founder of [BestSelf.co](https://bestself.co?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio). I write about AI, entrepreneurship, and designing nice-looking things at [littlemight.com](https://littlemight.com?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio) — blog + newsletter.
|
||||||
|
|
||||||
|
If this is useful, **star the repo** and come [say hi on X](https://x.com/cathrynlavery).
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
---
|
||||||
|
name: diagram-design
|
||||||
|
description: Create technical and product diagrams — architecture, flowchart, sequence, state machine, ER / data model, timeline, swimlane, quadrant, nested, tree, layer stack, venn, pyramid — as standalone HTML files with inline SVG. Ships with a neutral editorial skin and a first-run gate that prompts users to customize the style guide (colors, fonts) from their own website before generating. Includes annotation-callout primitive and optional sketchy variant.
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
version: "1.0"
|
||||||
|
author: Cocoon AI (hello@cocoon-ai.com)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diagram Design
|
||||||
|
|
||||||
|
Create visual diagrams as self-contained HTML files with inline SVG and CSS, following an opinionated editorial design system.
|
||||||
|
|
||||||
|
Thirteen diagram types. One shared design system, complexity budget, and taste gate. Type-specific conventions live in `references/` and are loaded only when you pick a type.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. First-time setup — style guide gate
|
||||||
|
|
||||||
|
**Before generating your first diagram in a new project, verify the style guide has been customized.**
|
||||||
|
|
||||||
|
Open [`references/style-guide.md`](references/style-guide.md) and check the default tokens. If they're still the shipped defaults (paper `#faf7f2`, ink `#1c1917`, accent `#b5523a` rust), **pause and ask the user**:
|
||||||
|
|
||||||
|
> *"This is your first diagram in this project. The style guide is still at the default (neutral stone + rust). Do you want to customize it to match your brand first? Options: (a) run onboarding — I'll pull colors and fonts from your website, (b) paste your tokens manually, (c) proceed with the default for now."*
|
||||||
|
|
||||||
|
Then branch:
|
||||||
|
- **(a)** → follow [`references/onboarding.md`](references/onboarding.md) to fetch the site, extract palette + fonts, propose a diff, and write `style-guide.md`.
|
||||||
|
- **(b)** → accept the user's tokens and write them into `style-guide.md` under a new "Custom tokens" section.
|
||||||
|
- **(c)** → proceed; optionally remind the user they can run onboarding later.
|
||||||
|
|
||||||
|
**Once the style guide has been customized** (or the user explicitly opted for default), skip this gate on subsequent runs. A simple way to detect customization: if the `accent` value in `style-guide.md` differs from `#b5523a`, assume custom.
|
||||||
|
|
||||||
|
Don't silently ship default-skinned diagrams into a branded project — that's the failure mode this gate exists to prevent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Philosophy
|
||||||
|
|
||||||
|
**The highest-quality move is usually deletion.**
|
||||||
|
|
||||||
|
From `.impeccable.md`: *"Confident restraint. Earn every element. One color accent, two families, a small spacing vocabulary. If removing it wouldn't hurt the page, remove it."*
|
||||||
|
|
||||||
|
Applied to diagrams:
|
||||||
|
- Every node represents a distinct idea. Two nodes that always travel together are one node.
|
||||||
|
- Every connection carries information. If the relationship is obvious from layout, remove the line.
|
||||||
|
- Coral is **editorial, not a flag.** 1–2 focal nodes per diagram. Using it on 5 nodes erases the signal.
|
||||||
|
- The diagram isn't done when everything is added. It's done when nothing can be removed.
|
||||||
|
|
||||||
|
**Target density: 4/10.** Enough to be technically complete. Not so dense it needs a guide. Above 9 nodes, it's probably two diagrams.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. When to Use
|
||||||
|
|
||||||
|
Use for any of the 13 diagram types (§3) when a reader will learn more from a visual than from prose, a table, or a bulleted list.
|
||||||
|
|
||||||
|
**Don't use for:**
|
||||||
|
- Quick unicode diagrams → use **wiretext**.
|
||||||
|
- Lists of things → table or bullets.
|
||||||
|
- Simple before/after → table.
|
||||||
|
- One-shape "diagrams" → just write the sentence.
|
||||||
|
|
||||||
|
Before drawing, ask: *Would the reader learn more from this than from a well-written paragraph?* If no, don't draw.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Diagram Types
|
||||||
|
|
||||||
|
### Selection guide
|
||||||
|
|
||||||
|
| If you're showing… | Use | Reference |
|
||||||
|
|---|---|---|
|
||||||
|
| Components + connections in a system | **Architecture** | [type-architecture.md](references/type-architecture.md) |
|
||||||
|
| Decision logic with branches | **Flowchart** | [type-flowchart.md](references/type-flowchart.md) |
|
||||||
|
| Time-ordered messages between actors | **Sequence** | [type-sequence.md](references/type-sequence.md) |
|
||||||
|
| States + transitions + guards | **State machine** | [type-state.md](references/type-state.md) |
|
||||||
|
| Entities + fields + relationships | **ER / data model** | [type-er.md](references/type-er.md) |
|
||||||
|
| Events positioned in time | **Timeline** | [type-timeline.md](references/type-timeline.md) |
|
||||||
|
| Cross-functional process with handoffs | **Swimlane** | [type-swimlane.md](references/type-swimlane.md) |
|
||||||
|
| Two-axis positioning / prioritization | **Quadrant** | [type-quadrant.md](references/type-quadrant.md) |
|
||||||
|
| Hierarchy through containment / scope | **Nested** | [type-nested.md](references/type-nested.md) |
|
||||||
|
| Parent → children relationships | **Tree** | [type-tree.md](references/type-tree.md) |
|
||||||
|
| Stacked abstraction levels | **Layer stack** | [type-layers.md](references/type-layers.md) |
|
||||||
|
| Overlap between sets | **Venn** | [type-venn.md](references/type-venn.md) |
|
||||||
|
| Ranked hierarchy or conversion drop-off | **Pyramid / funnel** | [type-pyramid.md](references/type-pyramid.md) |
|
||||||
|
|
||||||
|
Rules of thumb:
|
||||||
|
- If a 3-column table communicates the same thing, pick the table.
|
||||||
|
- If you're combining two types, pick the dominant axis — don't hybridize grammars.
|
||||||
|
- If you're past the complexity budget (§7), split into an overview + detail.
|
||||||
|
|
||||||
|
**Always load the relevant `references/type-*.md` before drawing** — it contains layout conventions, anti-patterns, and example files for that type.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Universal Anti-patterns
|
||||||
|
|
||||||
|
These mark "AI slop" diagrams of any type:
|
||||||
|
|
||||||
|
| Anti-pattern | Why it fails |
|
||||||
|
|---|---|
|
||||||
|
| Dark mode + cyan/purple glow | Looks "technical" without design decisions |
|
||||||
|
| JetBrains Mono as blanket "dev" font | Mono is for *technical* content — ports, commands, URLs. Names go in Geist sans. |
|
||||||
|
| Identical boxes for every node | Erases hierarchy |
|
||||||
|
| Legend floating inside the diagram area | Collides with nodes |
|
||||||
|
| Arrow labels with no masking rect | Bleeds through the line |
|
||||||
|
| Vertical `writing-mode` text on arrows | Unreadable |
|
||||||
|
| 3 equal-width summary cards as default | Generic grid — vary widths |
|
||||||
|
| Shadow on any element | Shadows are out. Borders are in. |
|
||||||
|
| `rounded-2xl` on boxes | Max radius 6–10px or none |
|
||||||
|
| Coral on every "important" node | Coral is 1–2 editorial accents, not a signaling system |
|
||||||
|
|
||||||
|
Type-specific anti-patterns live in each `references/type-*.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Design System
|
||||||
|
|
||||||
|
**The design system is skinnable.** All colors, typography, and tokens live in a single source of truth — [`references/style-guide.md`](references/style-guide.md). This file describes semantic roles (`paper`, `ink`, `muted`, `accent`, `link`, …). The default skin is littlemight.com (warm paper, ink, coral); to apply your own brand, either edit `style-guide.md` directly or run the URL-based flow described in [`references/onboarding.md`](references/onboarding.md).
|
||||||
|
|
||||||
|
> When specs below or in type references mention "ink", "accent", "muted", etc., look up the current hex value in `style-guide.md`.
|
||||||
|
|
||||||
|
### Semantic roles (at a glance)
|
||||||
|
|
||||||
|
| Role | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `paper`, `paper-2` | Page bg and container bg |
|
||||||
|
| `ink` | Primary text / stroke |
|
||||||
|
| `muted`, `soft` | Secondary text, default arrows, sublabels |
|
||||||
|
| `rule`, `rule-solid` | Hairline borders |
|
||||||
|
| `accent`, `accent-tint` | 1–2 focal elements per diagram |
|
||||||
|
| `link` | HTTP/API calls, external arrows |
|
||||||
|
|
||||||
|
**Focal rule:** `accent` goes on 1–2 elements max. Everything else is `ink` / `muted` / `soft`. If you're tempted to accent 4 things, you haven't decided what's focal yet.
|
||||||
|
|
||||||
|
### Node type → treatment
|
||||||
|
|
||||||
|
| Type | Fill | Stroke |
|
||||||
|
|---|---|---|
|
||||||
|
| **Focal** (1–2 max) | `accent-tint` | `accent` |
|
||||||
|
| **Backend / API / Step** | white | `ink` |
|
||||||
|
| **Store / State** | `ink @ 0.05` | `muted` |
|
||||||
|
| **External / Cloud** | `ink @ 0.03` | `ink @ 0.30` |
|
||||||
|
| **Input / User** | `muted @ 0.10` | `soft` |
|
||||||
|
| **Optional / Async** | `ink @ 0.02` | `ink @ 0.20` dashed `4,3` |
|
||||||
|
| **Security / Boundary** | `accent @ 0.05` | `accent @ 0.50` dashed `4,4` |
|
||||||
|
|
||||||
|
### Typography (summary — full spec in style-guide.md)
|
||||||
|
|
||||||
|
- **Title** — Instrument Serif, 1.75rem, 400 — H1 only
|
||||||
|
- **Node name** — Geist (sans), 12px, 600 — human-readable labels
|
||||||
|
- **Sublabel** — Geist Mono, 9px — ports, URLs, field types
|
||||||
|
- **Eyebrow / tag** — Geist Mono, 7–8px, uppercase, tracked — type tags, axis labels
|
||||||
|
- **Arrow label** — Geist Mono, 8px — annotation on arrows
|
||||||
|
- **Editorial aside** — Instrument Serif *italic*, 14px — callouts only
|
||||||
|
|
||||||
|
**Mono is for technical content.** Names are Geist sans. Page title is Instrument Serif. Italic Instrument Serif is reserved for annotation callouts. Never JetBrains Mono as a blanket "dev" font.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Core SVG Primitives
|
||||||
|
|
||||||
|
Universal building blocks. Type-specialized primitives (lifeline, activation bar, region) live in the relevant `references/type-*.md`. Optional primitives:
|
||||||
|
- Editorial callouts → [primitive-annotation.md](references/primitive-annotation.md)
|
||||||
|
- Hand-drawn variant → [primitive-sketchy.md](references/primitive-sketchy.md)
|
||||||
|
|
||||||
|
### Background
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.6"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arrow markers (define all three, always)
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#52534e"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#f7591f"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/>
|
||||||
|
</marker>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Arrow | Stroke | When |
|
||||||
|
|---|---|---|
|
||||||
|
| Default | muted `#52534e` | Internal, generic |
|
||||||
|
| Accent | coral `#f7591f` | Primary / highlighted / headline |
|
||||||
|
| Link-blue | `#1a70c7` | HTTP/API calls, external systems |
|
||||||
|
| Dashed | `stroke-dasharray="5,4"` + any color | Optional, passive, return, async |
|
||||||
|
|
||||||
|
**Draw arrows before boxes** so z-order puts lines behind nodes.
|
||||||
|
|
||||||
|
### Node box — full pattern
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<!-- 1. Opaque paper mask — prevents arrows bleeding through transparent fills -->
|
||||||
|
<rect x="X" y="Y" width="W" height="H" rx="6" fill="#f5f4ed"/>
|
||||||
|
<!-- 2. Styled box -->
|
||||||
|
<rect x="X" y="Y" width="W" height="H" rx="6" fill="FILL" stroke="STROKE" stroke-width="1"/>
|
||||||
|
<!-- 3. Rectangular type tag (rx=2, NOT a pill) -->
|
||||||
|
<rect x="X+8" y="Y+6" width="28" height="12" rx="2" fill="transparent" stroke="STROKE@0.40" stroke-width="0.8"/>
|
||||||
|
<text x="X+22" y="Y+15" fill="STROKE@0.8" font-size="7" font-family="'Geist Mono', monospace"
|
||||||
|
text-anchor="middle" letter-spacing="0.08em">API</text>
|
||||||
|
<!-- 4. Node name (Geist sans — human-readable) -->
|
||||||
|
<text x="CX" y="CY+2" fill="#0b0d0b" font-size="12" font-weight="600"
|
||||||
|
font-family="'Geist', sans-serif" text-anchor="middle">Node Name</text>
|
||||||
|
<!-- 5. Technical sublabel (Geist Mono) -->
|
||||||
|
<text x="CX" y="CY+18" fill="#52534e" font-size="9"
|
||||||
|
font-family="'Geist Mono', monospace" text-anchor="middle">tech:port</text>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arrow labels — always mask
|
||||||
|
|
||||||
|
Every arrow label needs an opaque rect behind it. Without one it bleeds through the line.
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<rect x="MID_X-18" y="ARROW_Y-12" width="36" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="MID_X" y="ARROW_Y-3" fill="#65655c" font-size="8"
|
||||||
|
font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">WRITE</text>
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules: ≤14 characters, all-caps, centered on segment midpoint, 8–10px above line. Never `writing-mode` vertical.
|
||||||
|
|
||||||
|
### Legend — horizontal strip at the bottom
|
||||||
|
|
||||||
|
**Never put the legend inside the diagram area.** Place as a horizontal strip after all nodes, with a hairline separator:
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<line x1="30" y1="LEGEND_Y-8" x2="VIEWBOX_W-30" y2="LEGEND_Y-8"
|
||||||
|
stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="30" y="LEGEND_Y+8" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace"
|
||||||
|
letter-spacing="0.14em">LEGEND</text>
|
||||||
|
<!-- Items — horizontal row, ~160px apart -->
|
||||||
|
```
|
||||||
|
|
||||||
|
Expand SVG `viewBox` height by ~60px.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Layout & Spacing
|
||||||
|
|
||||||
|
### 4px grid
|
||||||
|
|
||||||
|
**All values — font sizes, padding, node dimensions, gaps, x/y coords — divisible by 4.** Non-negotiable.
|
||||||
|
|
||||||
|
| Category | Allowed values |
|
||||||
|
|---|---|
|
||||||
|
| Font sizes | 8, 12, 16, 20, 24, 28, 32, 40 |
|
||||||
|
| Node width / height | 80, 96, 112, 120, 128, 140, 144, 160, 180, 200, 240, 320 |
|
||||||
|
| x / y coordinates | multiples of 4 |
|
||||||
|
| Gap between nodes | 20, 24, 32, 40, 48 |
|
||||||
|
| Padding inside boxes | 8, 12, 16 |
|
||||||
|
| Border radius | 4, 6, 8 |
|
||||||
|
|
||||||
|
Exempt: stroke widths (0.8, 1, 1.2), opacity values, and the 22×22 dot-pattern.
|
||||||
|
|
||||||
|
Quick check: if a coordinate ends in 1, 2, 3, 5, 6, 7, 9 — fix it.
|
||||||
|
|
||||||
|
### Complexity budget (per diagram)
|
||||||
|
|
||||||
|
| Limit | Rule |
|
||||||
|
|---|---|
|
||||||
|
| Max nodes | 9 |
|
||||||
|
| Max arrows / transitions | 12 |
|
||||||
|
| Max coral elements | 2 |
|
||||||
|
| Max lifelines (sequence) | 5 |
|
||||||
|
| Max lanes (swimlane) | 5 |
|
||||||
|
| Max items (quadrant) | 12 |
|
||||||
|
| Max entities (ER) | 8 |
|
||||||
|
| Max nesting levels (nested) | 6 |
|
||||||
|
| Max tree depth | 4 |
|
||||||
|
| Max layers (layer stack) | 6 |
|
||||||
|
| Max circles (venn) | 3 |
|
||||||
|
| Max layers (pyramid) | 6 |
|
||||||
|
| Max annotation callouts | 2 |
|
||||||
|
|
||||||
|
If you exceed, split into two diagrams (overview + detail).
|
||||||
|
|
||||||
|
### Page layout
|
||||||
|
|
||||||
|
1. **Header** — eyebrow (Geist Mono), title (Instrument Serif), optional subtitle (Geist muted).
|
||||||
|
2. **Diagram container** — `#efeee5` bg, 1px `rgba(11,13,11,0.12)` border, 8px radius, `overflow-x: auto`.
|
||||||
|
3. **Summary cards** — 2–3 col grid with *varied* widths (e.g., `1.1fr 1fr 0.9fr`).
|
||||||
|
4. **Footer** — colophon in Geist Mono, muted, hairline top border.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Summary Card Pattern
|
||||||
|
|
||||||
|
Don't use 3 identical generic cards. Vary the treatment:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">SECTION LABEL</p>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot coral"></span>
|
||||||
|
<h3>Card Title</h3>
|
||||||
|
</div>
|
||||||
|
<ul><li>Item</li></ul>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- `background: #ffffff` (not paper — slight lift without shadow)
|
||||||
|
- `border: 1px solid rgba(11,13,11,0.12)`
|
||||||
|
- `border-radius: 6px`, `padding: 1.25rem`
|
||||||
|
- **No `box-shadow`**
|
||||||
|
- Card dots: 7px, `border-radius: 50%` — ink / muted / coral / link / soft variants
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Pre-Output Checklist (Taste Gate)
|
||||||
|
|
||||||
|
Run before producing any diagram.
|
||||||
|
|
||||||
|
**Type fit:**
|
||||||
|
- [ ] Right type for what I'm showing? (§3 selection guide)
|
||||||
|
- [ ] Would a table / paragraph do the same job? (If yes — don't draw.)
|
||||||
|
- [ ] Loaded the matching `references/type-*.md`?
|
||||||
|
|
||||||
|
**Remove test:**
|
||||||
|
- [ ] Can I remove any node? (Would a reader still understand?)
|
||||||
|
- [ ] Can I merge any two nodes? (Do they always travel together?)
|
||||||
|
- [ ] Can I remove any arrow? (Is the relationship obvious from layout?)
|
||||||
|
- [ ] Can I remove any label? (Does color or shape already signal it?)
|
||||||
|
|
||||||
|
**Signal:**
|
||||||
|
- [ ] Coral used on ≤2 elements? If more, which actually deserve focal status?
|
||||||
|
- [ ] Legend covers every type used — and nothing extra?
|
||||||
|
- [ ] Within the type's complexity budget (§7)?
|
||||||
|
|
||||||
|
**Technical:**
|
||||||
|
- [ ] Arrows drawn before boxes?
|
||||||
|
- [ ] Every arrow label has an opaque `fill="#f5f4ed"` rect behind it?
|
||||||
|
- [ ] Legend is a horizontal bottom strip, not floating?
|
||||||
|
- [ ] No vertical `writing-mode` text?
|
||||||
|
- [ ] `viewBox` expanded for the legend strip (~60px)?
|
||||||
|
- [ ] Every font size, coord, width, height, gap divisible by 4?
|
||||||
|
|
||||||
|
**Typography:**
|
||||||
|
- [ ] Human-readable names in Geist sans, not Geist Mono?
|
||||||
|
- [ ] Technical sublabels (ports, commands, URLs) in Geist Mono?
|
||||||
|
- [ ] Page title in Instrument Serif?
|
||||||
|
- [ ] Annotation callouts (if any) in *italic* Instrument Serif? (see [primitive-annotation.md](references/primitive-annotation.md))
|
||||||
|
- [ ] No JetBrains Mono anywhere?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Templates & Variants
|
||||||
|
|
||||||
|
Every diagram ships in three variants (see `assets/`):
|
||||||
|
|
||||||
|
| Variant | File pattern | When to use |
|
||||||
|
|---|---|---|
|
||||||
|
| **Minimal light** (default) | `template.html`, `example-<type>.html` | Screenshot-ready. Diagram + title. Warm paper. |
|
||||||
|
| **Minimal dark** | `template-dark.html`, `example-<type>-dark.html` | Dark mode sites, slides, high-contrast posts. |
|
||||||
|
| **Full editorial** | `template-full.html`, `example-<type>-full.html` | Long-form posts where the diagram is the hero. |
|
||||||
|
|
||||||
|
**Sketchy variant** (optional, applied to any of the above) — see [primitive-sketchy.md](references/primitive-sketchy.md). SVG turbulence filter wobbles strokes for a hand-drawn feel. Good for essays, not for technical docs.
|
||||||
|
|
||||||
|
### To create a new diagram
|
||||||
|
|
||||||
|
1. Copy the variant closest to what you want (`template.html` for minimal, `template-full.html` for cards).
|
||||||
|
2. Load the matching `references/type-<name>.md` for layout conventions.
|
||||||
|
3. Replace the eyebrow, h1, and SVG body.
|
||||||
|
4. Run the §9 taste gate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Output
|
||||||
|
|
||||||
|
Always produce a single self-contained `.html` file:
|
||||||
|
- Embedded CSS (no external except Google Fonts)
|
||||||
|
- Inline SVG (no external images)
|
||||||
|
- No JavaScript required
|
||||||
|
|
||||||
|
Renders correctly in any modern browser.
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight.com · Architecture</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Architecture · Diagram Design</p>
|
||||||
|
<h1>littlemight.com in production</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/></marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#5ba8eb"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows first (behind boxes) -->
|
||||||
|
<line x1="168" y1="272" x2="220" y2="272" stroke="#5ba8eb" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<line x1="364" y1="272" x2="416" y2="272" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<line x1="576" y1="256" x2="628" y2="212" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<line x1="576" y1="288" x2="628" y2="332" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="172" y="252" width="48" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="196" y="262" fill="#5ba8eb" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
|
||||||
|
|
||||||
|
<rect x="368" y="252" width="48" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="392" y="262" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
|
||||||
|
|
||||||
|
<rect x="560" y="212" width="56" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="588" y="222" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
|
||||||
|
|
||||||
|
<rect x="560" y="320" width="52" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="586" y="330" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
|
||||||
|
|
||||||
|
<!-- Node: Reader -->
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="rgba(168,166,157,0.10)" stroke="#8e8c83" stroke-width="1"/>
|
||||||
|
<rect x="48" y="248" width="28" height="12" rx="2" fill="transparent" stroke="rgba(142,140,131,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="62" y="257" fill="#8e8c83" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="104" y="276" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="104" y="292" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Node: Cloudflare -->
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="228" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="244" y="257" fill="#8e8c83" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="356" y="300" fill="rgba(241,239,231,0.06)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">01</text>
|
||||||
|
<text x="292" y="276" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="292" y="292" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Node: Astro (focal coral) -->
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<rect x="424" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(255,106,48,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="440" y="257" fill="#ff6a30" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="568" y="300" fill="rgba(255,106,48,0.10)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">02</text>
|
||||||
|
<text x="496" y="276" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="496" y="292" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Node: MDX Bundle -->
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="636" y="168" width="32" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="652" y="177" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BUN</text>
|
||||||
|
<text x="700" y="196" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MDX Bundle</text>
|
||||||
|
<text x="700" y="212" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/*.mdx</text>
|
||||||
|
|
||||||
|
<!-- Node: Content CMS -->
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<rect x="636" y="328" width="28" height="12" rx="2" fill="transparent" stroke="rgba(168,166,157,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="650" y="337" fill="#a8a69d" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CMS</text>
|
||||||
|
<text x="700" y="356" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Content CMS</text>
|
||||||
|
<text x="700" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">assets · og images</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Focal / origin</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Backend / bundle</text>
|
||||||
|
|
||||||
|
<rect x="340" y="436" width="14" height="10" rx="2" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<text x="360" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Store</text>
|
||||||
|
|
||||||
|
<rect x="436" y="436" width="14" height="10" rx="2" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<text x="456" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Cloud</text>
|
||||||
|
|
||||||
|
<rect x="528" y="436" width="14" height="10" rx="2" fill="rgba(168,166,157,0.10)" stroke="#8e8c83" stroke-width="1"/>
|
||||||
|
<text x="548" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">External</text>
|
||||||
|
|
||||||
|
<line x1="636" y1="442" x2="664" y2="442" stroke="#5ba8eb" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="672" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<line x1="784" y1="442" x2="812" y2="442" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="820" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight.com · Architecture</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed; --color-paper-2: #efeee5;
|
||||||
|
--color-ink: #0b0d0b; --color-muted: #52534e; --color-soft: #65655c;
|
||||||
|
--color-rule: rgba(11,13,11,0.12);
|
||||||
|
--color-accent: #f7591f; --color-accent-tint: rgba(247,89,31,0.08);
|
||||||
|
--color-link: #1a70c7;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; letter-spacing: -0.005em; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Architecture · Diagram Design</p>
|
||||||
|
<h1>littlemight.com in production</h1>
|
||||||
|
<p class="subtitle">The static-first stack: Cloudflare's edge absorbs most reads, Astro renders MDX on misses, content is checked into the repo.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows first (behind boxes) -->
|
||||||
|
<line x1="168" y1="272" x2="220" y2="272" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<line x1="364" y1="272" x2="416" y2="272" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<line x1="576" y1="256" x2="628" y2="212" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<line x1="576" y1="288" x2="628" y2="332" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="172" y="252" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="196" y="262" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
|
||||||
|
|
||||||
|
<rect x="368" y="252" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="392" y="262" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
|
||||||
|
|
||||||
|
<rect x="560" y="212" width="56" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="588" y="222" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
|
||||||
|
|
||||||
|
<rect x="560" y="320" width="52" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="586" y="330" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
|
||||||
|
|
||||||
|
<!-- Node: Reader -->
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<rect x="48" y="248" width="28" height="12" rx="2" fill="transparent" stroke="rgba(101,101,92,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="62" y="257" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="104" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="104" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Node: Cloudflare -->
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="228" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="244" y="257" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="356" y="300" fill="rgba(11,13,11,0.06)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">01</text>
|
||||||
|
<text x="292" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="292" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Node: Astro (focal coral) -->
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="424" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="440" y="257" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="568" y="300" fill="rgba(247,89,31,0.10)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">02</text>
|
||||||
|
<text x="496" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="496" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Node: MDX Bundle -->
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="636" y="168" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="652" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BUN</text>
|
||||||
|
<text x="700" y="196" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MDX Bundle</text>
|
||||||
|
<text x="700" y="212" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/*.mdx</text>
|
||||||
|
|
||||||
|
<!-- Node: Content CMS -->
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<rect x="636" y="328" width="28" height="12" rx="2" fill="transparent" stroke="rgba(82,83,78,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="650" y="337" fill="#52534e" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CMS</text>
|
||||||
|
<text x="700" y="356" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Content CMS</text>
|
||||||
|
<text x="700" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">assets · og images</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal / origin</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Backend / bundle</text>
|
||||||
|
|
||||||
|
<rect x="340" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<text x="360" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Store</text>
|
||||||
|
|
||||||
|
<rect x="436" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="456" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Cloud</text>
|
||||||
|
|
||||||
|
<rect x="528" y="436" width="14" height="10" rx="2" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<text x="548" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">External</text>
|
||||||
|
|
||||||
|
<line x1="636" y1="442" x2="664" y2="442" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="672" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<line x1="784" y1="442" x2="812" y2="442" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="820" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Edge absorbs the reads</h3></div>
|
||||||
|
<p>Nearly every reader is served by Cloudflare's edge cache. The Astro origin only wakes on a cold slug or a revalidation.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Content lives in the repo</h3></div>
|
||||||
|
<ul><li>Posts are MDX files</li><li>Checked in, reviewed in PRs</li><li>No runtime database</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>CMS holds the big stuff</h3></div>
|
||||||
|
<p>Images, OG art, and downloadable assets live in a separate bucket keyed by slug. Astro links them at render time.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>littlemight.com · architecture</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight.com · Architecture</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Architecture · Diagram Design</p>
|
||||||
|
<h1>littlemight.com in production</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows first (behind boxes) -->
|
||||||
|
<line x1="168" y1="272" x2="220" y2="272" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<line x1="364" y1="272" x2="416" y2="272" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<line x1="576" y1="256" x2="628" y2="212" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<line x1="576" y1="288" x2="628" y2="332" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="172" y="252" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="196" y="262" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
|
||||||
|
|
||||||
|
<rect x="368" y="252" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="392" y="262" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
|
||||||
|
|
||||||
|
<rect x="560" y="212" width="56" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="588" y="222" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
|
||||||
|
|
||||||
|
<rect x="560" y="320" width="52" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="586" y="330" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
|
||||||
|
|
||||||
|
<!-- Node: Reader -->
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="40" y="240" width="128" height="64" rx="6" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<rect x="48" y="248" width="28" height="12" rx="2" fill="transparent" stroke="rgba(101,101,92,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="62" y="257" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="104" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="104" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Node: Cloudflare -->
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="220" y="240" width="144" height="64" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="228" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="244" y="257" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="356" y="300" fill="rgba(11,13,11,0.06)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">01</text>
|
||||||
|
<text x="292" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="292" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Node: Astro (focal coral) -->
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="416" y="240" width="160" height="64" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="424" y="248" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="440" y="257" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="568" y="300" fill="rgba(247,89,31,0.10)" font-size="32" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">02</text>
|
||||||
|
<text x="496" y="276" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="496" y="292" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Node: MDX Bundle -->
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="628" y="160" width="144" height="64" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="636" y="168" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="652" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BUN</text>
|
||||||
|
<text x="700" y="196" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MDX Bundle</text>
|
||||||
|
<text x="700" y="212" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/*.mdx</text>
|
||||||
|
|
||||||
|
<!-- Node: Content CMS -->
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="628" y="320" width="144" height="64" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<rect x="636" y="328" width="28" height="12" rx="2" fill="transparent" stroke="rgba(82,83,78,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="650" y="337" fill="#52534e" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CMS</text>
|
||||||
|
<text x="700" y="356" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Content CMS</text>
|
||||||
|
<text x="700" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">assets · og images</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal / origin</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Backend / bundle</text>
|
||||||
|
|
||||||
|
<rect x="340" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<text x="360" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Store</text>
|
||||||
|
|
||||||
|
<rect x="436" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="456" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Cloud</text>
|
||||||
|
|
||||||
|
<rect x="528" y="436" width="14" height="10" rx="2" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<text x="548" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">External</text>
|
||||||
|
|
||||||
|
<line x1="636" y1="442" x2="664" y2="442" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="672" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<line x1="784" y1="442" x2="812" y2="442" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="820" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight content model · ER</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">ER · Diagram Design</p>
|
||||||
|
<h1>littlemight content model</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Relationship lines (drawn first) -->
|
||||||
|
<!-- 1. Author — Article (1:N "writes") -->
|
||||||
|
<line x1="260" y1="240" x2="400" y2="240" stroke="#a8a69d" stroke-width="1" />
|
||||||
|
<!-- 2. Article — ArticleTag (1:N) -->
|
||||||
|
<line x1="640" y1="320" x2="780" y2="328" stroke="#a8a69d" stroke-width="1" />
|
||||||
|
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
|
||||||
|
<line x1="880" y1="248" x2="880" y2="280" stroke="#a8a69d" stroke-width="1" />
|
||||||
|
|
||||||
|
<!-- Cardinality labels -->
|
||||||
|
<rect x="266" y="232" width="12" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="272" y="242" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="378" y="232" width="16" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="386" y="242" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="646" y="316" width="12" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="652" y="326" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="760" y="324" width="16" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="768" y="334" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="872" y="252" width="16" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="880" y="262" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="872" y="268" width="16" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="880" y="278" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<!-- Relationship labels -->
|
||||||
|
<rect x="304" y="220" width="56" height="14" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="332" y="230" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">WRITES</text>
|
||||||
|
|
||||||
|
<rect x="688" y="300" width="56" height="14" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="716" y="310" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">TAGGED</text>
|
||||||
|
|
||||||
|
<!-- Entity: Author -->
|
||||||
|
<rect x="60" y="160" width="200" height="160" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="60" y="160" width="200" height="40" rx="6" fill="rgba(241,239,231,0.04)" stroke="none"/>
|
||||||
|
<rect x="60" y="192" width="200" height="8" fill="rgba(241,239,231,0.04)"/>
|
||||||
|
<line x1="60" y1="200" x2="260" y2="200" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
<text x="76" y="176" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="76" y="192" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Author</text>
|
||||||
|
<text x="76" y="220" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="220" y="220" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="76" y="240" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">handle</text>
|
||||||
|
<text x="220" y="240" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="260" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="220" y="260" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="280" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">bio</text>
|
||||||
|
<text x="220" y="280" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="300" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">site_url</text>
|
||||||
|
<text x="220" y="300" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: Article (focal coral) -->
|
||||||
|
<rect x="400" y="120" width="240" height="240" rx="6" fill="rgba(255,106,48,0.04)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<rect x="400" y="120" width="240" height="40" rx="6" fill="rgba(255,106,48,0.10)" stroke="none"/>
|
||||||
|
<rect x="400" y="152" width="240" height="8" fill="rgba(255,106,48,0.10)"/>
|
||||||
|
<line x1="400" y1="160" x2="640" y2="160" stroke="rgba(255,106,48,0.40)" stroke-width="1"/>
|
||||||
|
<text x="416" y="136" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY · AGGREGATE ROOT</text>
|
||||||
|
<text x="416" y="152" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Article</text>
|
||||||
|
<text x="416" y="180" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="600" y="180" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="200" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">title</text>
|
||||||
|
<text x="600" y="200" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="220" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="600" y="220" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="416" y="240" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">body_mdx</text>
|
||||||
|
<text x="600" y="240" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="260" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">published_at</text>
|
||||||
|
<text x="600" y="260" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">timestamp</text>
|
||||||
|
<text x="416" y="280" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">→ author_id</text>
|
||||||
|
<text x="600" y="280" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="300" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">status</text>
|
||||||
|
<text x="600" y="300" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">enum</text>
|
||||||
|
<text x="416" y="320" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">og_image</text>
|
||||||
|
<text x="600" y="320" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · url</text>
|
||||||
|
|
||||||
|
<!-- Entity: Tag -->
|
||||||
|
<rect x="780" y="120" width="200" height="128" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="780" y="120" width="200" height="40" rx="6" fill="rgba(241,239,231,0.04)" stroke="none"/>
|
||||||
|
<rect x="780" y="152" width="200" height="8" fill="rgba(241,239,231,0.04)"/>
|
||||||
|
<line x1="780" y1="160" x2="980" y2="160" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="136" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="796" y="152" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Tag</text>
|
||||||
|
<text x="796" y="180" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="940" y="180" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="200" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="940" y="200" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="796" y="220" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="940" y="220" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="796" y="240" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">description</text>
|
||||||
|
<text x="940" y="240" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: ArticleTag (join) -->
|
||||||
|
<rect x="780" y="280" width="200" height="96" rx="6" fill="rgba(241,239,231,0.04)" stroke="#a8a69d" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="780" y="280" width="200" height="40" rx="6" fill="rgba(241,239,231,0.06)" stroke="none"/>
|
||||||
|
<rect x="780" y="312" width="200" height="8" fill="rgba(241,239,231,0.06)"/>
|
||||||
|
<line x1="780" y1="320" x2="980" y2="320" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="296" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">JOIN</text>
|
||||||
|
<text x="796" y="312" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif">ArticleTag</text>
|
||||||
|
<text x="796" y="340" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">→ article_id</text>
|
||||||
|
<text x="940" y="340" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="360" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace">→ tag_id</text>
|
||||||
|
<text x="940" y="360" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(255,106,48,0.04)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Aggregate root</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Entity</text>
|
||||||
|
|
||||||
|
<rect x="268" y="436" width="14" height="10" rx="2" fill="rgba(241,239,231,0.04)" stroke="#a8a69d" stroke-width="1" stroke-dasharray="3,2"/>
|
||||||
|
<text x="288" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Join table</text>
|
||||||
|
|
||||||
|
<text x="372" y="444" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">#</text>
|
||||||
|
<text x="388" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Primary key</text>
|
||||||
|
|
||||||
|
<text x="476" y="444" fill="#f1efe7" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">→</text>
|
||||||
|
<text x="492" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Foreign key</text>
|
||||||
|
|
||||||
|
<text x="584" y="444" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">1 / N</text>
|
||||||
|
<text x="616" y="444" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Cardinality</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight content model · ER</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">ER · Diagram Design</p>
|
||||||
|
<h1>littlemight content model</h1>
|
||||||
|
<p class="subtitle">The four entities behind the site. Article is the aggregate root — everything else exists to describe or classify it. `#` marks primary keys, `→` marks foreign keys.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Relationship lines (drawn first) -->
|
||||||
|
<!-- 1. Author — Article (1:N "writes") -->
|
||||||
|
<line x1="260" y1="240" x2="400" y2="240" stroke="#52534e" stroke-width="1" />
|
||||||
|
<!-- 2. Article — ArticleTag (1:N) -->
|
||||||
|
<line x1="640" y1="320" x2="780" y2="328" stroke="#52534e" stroke-width="1" />
|
||||||
|
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
|
||||||
|
<line x1="880" y1="248" x2="880" y2="280" stroke="#52534e" stroke-width="1" />
|
||||||
|
|
||||||
|
<!-- Cardinality labels -->
|
||||||
|
<rect x="266" y="232" width="12" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="272" y="242" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="378" y="232" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="386" y="242" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="646" y="316" width="12" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="652" y="326" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="760" y="324" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="768" y="334" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="872" y="252" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="880" y="262" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="872" y="268" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="880" y="278" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<!-- Relationship labels -->
|
||||||
|
<rect x="304" y="220" width="56" height="14" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="332" y="230" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">WRITES</text>
|
||||||
|
|
||||||
|
<rect x="688" y="300" width="56" height="14" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="716" y="310" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">TAGGED</text>
|
||||||
|
|
||||||
|
<!-- Entity: Author -->
|
||||||
|
<rect x="60" y="160" width="200" height="160" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="60" y="160" width="200" height="40" rx="6" fill="rgba(11,13,11,0.04)" stroke="none"/>
|
||||||
|
<rect x="60" y="192" width="200" height="8" fill="rgba(11,13,11,0.04)"/>
|
||||||
|
<line x1="60" y1="200" x2="260" y2="200" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="76" y="176" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="76" y="192" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Author</text>
|
||||||
|
<text x="76" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="220" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="76" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">handle</text>
|
||||||
|
<text x="220" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="260" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="220" y="260" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="280" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">bio</text>
|
||||||
|
<text x="220" y="280" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="300" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">site_url</text>
|
||||||
|
<text x="220" y="300" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: Article (focal coral) -->
|
||||||
|
<rect x="400" y="120" width="240" height="240" rx="6" fill="rgba(247,89,31,0.04)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="400" y="120" width="240" height="40" rx="6" fill="rgba(247,89,31,0.10)" stroke="none"/>
|
||||||
|
<rect x="400" y="152" width="240" height="8" fill="rgba(247,89,31,0.10)"/>
|
||||||
|
<line x1="400" y1="160" x2="640" y2="160" stroke="rgba(247,89,31,0.40)" stroke-width="1"/>
|
||||||
|
<text x="416" y="136" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY · AGGREGATE ROOT</text>
|
||||||
|
<text x="416" y="152" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Article</text>
|
||||||
|
<text x="416" y="180" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="600" y="180" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="200" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">title</text>
|
||||||
|
<text x="600" y="200" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="600" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="416" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">body_mdx</text>
|
||||||
|
<text x="600" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="260" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">published_at</text>
|
||||||
|
<text x="600" y="260" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">timestamp</text>
|
||||||
|
<text x="416" y="280" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ author_id</text>
|
||||||
|
<text x="600" y="280" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="300" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">status</text>
|
||||||
|
<text x="600" y="300" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">enum</text>
|
||||||
|
<text x="416" y="320" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">og_image</text>
|
||||||
|
<text x="600" y="320" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · url</text>
|
||||||
|
|
||||||
|
<!-- Entity: Tag -->
|
||||||
|
<rect x="780" y="120" width="200" height="128" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="780" y="120" width="200" height="40" rx="6" fill="rgba(11,13,11,0.04)" stroke="none"/>
|
||||||
|
<rect x="780" y="152" width="200" height="8" fill="rgba(11,13,11,0.04)"/>
|
||||||
|
<line x1="780" y1="160" x2="980" y2="160" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="136" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="796" y="152" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Tag</text>
|
||||||
|
<text x="796" y="180" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="940" y="180" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="200" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="940" y="200" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="796" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="940" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="796" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">description</text>
|
||||||
|
<text x="940" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: ArticleTag (join) -->
|
||||||
|
<rect x="780" y="280" width="200" height="96" rx="6" fill="rgba(11,13,11,0.04)" stroke="#52534e" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="780" y="280" width="200" height="40" rx="6" fill="rgba(11,13,11,0.06)" stroke="none"/>
|
||||||
|
<rect x="780" y="312" width="200" height="8" fill="rgba(11,13,11,0.06)"/>
|
||||||
|
<line x1="780" y1="320" x2="980" y2="320" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="296" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">JOIN</text>
|
||||||
|
<text x="796" y="312" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">ArticleTag</text>
|
||||||
|
<text x="796" y="340" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ article_id</text>
|
||||||
|
<text x="940" y="340" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="360" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ tag_id</text>
|
||||||
|
<text x="940" y="360" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(247,89,31,0.04)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Aggregate root</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Entity</text>
|
||||||
|
|
||||||
|
<rect x="268" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.04)" stroke="#52534e" stroke-width="1" stroke-dasharray="3,2"/>
|
||||||
|
<text x="288" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Join table</text>
|
||||||
|
|
||||||
|
<text x="372" y="444" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">#</text>
|
||||||
|
<text x="388" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary key</text>
|
||||||
|
|
||||||
|
<text x="476" y="444" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">→</text>
|
||||||
|
<text x="492" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Foreign key</text>
|
||||||
|
|
||||||
|
<text x="584" y="444" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">1 / N</text>
|
||||||
|
<text x="616" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Cardinality</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Article is the root</h3></div>
|
||||||
|
<p>Author, Tag, and the join table only exist to describe Article. If you're thinking about a feature, start here and trace outward.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Many-to-many via a join</h3></div>
|
||||||
|
<ul><li>Tags aren't embedded on Article</li><li>ArticleTag is a pure join — no metadata</li><li>Dashed border signals it's not a primary entity</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Cardinality over arrows</h3></div>
|
||||||
|
<p>Plain lines with 1/N at the ends read cleaner than crow's feet at this size. Every relationship carries both numbers.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>littlemight content model · ER</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight content model · ER</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">ER · Diagram Design</p>
|
||||||
|
<h1>littlemight content model</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Relationship lines (drawn first) -->
|
||||||
|
<!-- 1. Author — Article (1:N "writes") -->
|
||||||
|
<line x1="260" y1="240" x2="400" y2="240" stroke="#52534e" stroke-width="1" />
|
||||||
|
<!-- 2. Article — ArticleTag (1:N) -->
|
||||||
|
<line x1="640" y1="320" x2="780" y2="328" stroke="#52534e" stroke-width="1" />
|
||||||
|
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
|
||||||
|
<line x1="880" y1="248" x2="880" y2="280" stroke="#52534e" stroke-width="1" />
|
||||||
|
|
||||||
|
<!-- Cardinality labels -->
|
||||||
|
<rect x="266" y="232" width="12" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="272" y="242" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="378" y="232" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="386" y="242" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="646" y="316" width="12" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="652" y="326" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="760" y="324" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="768" y="334" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<rect x="872" y="252" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="880" y="262" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">1</text>
|
||||||
|
|
||||||
|
<rect x="872" y="268" width="16" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="880" y="278" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">N</text>
|
||||||
|
|
||||||
|
<!-- Relationship labels -->
|
||||||
|
<rect x="304" y="220" width="56" height="14" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="332" y="230" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">WRITES</text>
|
||||||
|
|
||||||
|
<rect x="688" y="300" width="56" height="14" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="716" y="310" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">TAGGED</text>
|
||||||
|
|
||||||
|
<!-- Entity: Author -->
|
||||||
|
<rect x="60" y="160" width="200" height="160" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="60" y="160" width="200" height="40" rx="6" fill="rgba(11,13,11,0.04)" stroke="none"/>
|
||||||
|
<rect x="60" y="192" width="200" height="8" fill="rgba(11,13,11,0.04)"/>
|
||||||
|
<line x1="60" y1="200" x2="260" y2="200" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="76" y="176" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="76" y="192" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Author</text>
|
||||||
|
<text x="76" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="220" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="76" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">handle</text>
|
||||||
|
<text x="220" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="260" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="220" y="260" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="280" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">bio</text>
|
||||||
|
<text x="220" y="280" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="76" y="300" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">site_url</text>
|
||||||
|
<text x="220" y="300" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: Article (focal coral) -->
|
||||||
|
<rect x="400" y="120" width="240" height="240" rx="6" fill="rgba(247,89,31,0.04)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="400" y="120" width="240" height="40" rx="6" fill="rgba(247,89,31,0.10)" stroke="none"/>
|
||||||
|
<rect x="400" y="152" width="240" height="8" fill="rgba(247,89,31,0.10)"/>
|
||||||
|
<line x1="400" y1="160" x2="640" y2="160" stroke="rgba(247,89,31,0.40)" stroke-width="1"/>
|
||||||
|
<text x="416" y="136" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY · AGGREGATE ROOT</text>
|
||||||
|
<text x="416" y="152" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Article</text>
|
||||||
|
<text x="416" y="180" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="600" y="180" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="200" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">title</text>
|
||||||
|
<text x="600" y="200" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="600" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="416" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">body_mdx</text>
|
||||||
|
<text x="600" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="416" y="260" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">published_at</text>
|
||||||
|
<text x="600" y="260" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">timestamp</text>
|
||||||
|
<text x="416" y="280" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ author_id</text>
|
||||||
|
<text x="600" y="280" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="416" y="300" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">status</text>
|
||||||
|
<text x="600" y="300" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">enum</text>
|
||||||
|
<text x="416" y="320" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">og_image</text>
|
||||||
|
<text x="600" y="320" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · url</text>
|
||||||
|
|
||||||
|
<!-- Entity: Tag -->
|
||||||
|
<rect x="780" y="120" width="200" height="128" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="780" y="120" width="200" height="40" rx="6" fill="rgba(11,13,11,0.04)" stroke="none"/>
|
||||||
|
<rect x="780" y="152" width="200" height="8" fill="rgba(11,13,11,0.04)"/>
|
||||||
|
<line x1="780" y1="160" x2="980" y2="160" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="136" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">ENTITY</text>
|
||||||
|
<text x="796" y="152" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Tag</text>
|
||||||
|
<text x="796" y="180" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace"># id</text>
|
||||||
|
<text x="940" y="180" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="200" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">slug</text>
|
||||||
|
<text x="940" y="200" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text · unique</text>
|
||||||
|
<text x="796" y="220" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">name</text>
|
||||||
|
<text x="940" y="220" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
<text x="796" y="240" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">description</text>
|
||||||
|
<text x="940" y="240" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">text</text>
|
||||||
|
|
||||||
|
<!-- Entity: ArticleTag (join) -->
|
||||||
|
<rect x="780" y="280" width="200" height="96" rx="6" fill="rgba(11,13,11,0.04)" stroke="#52534e" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="780" y="280" width="200" height="40" rx="6" fill="rgba(11,13,11,0.06)" stroke="none"/>
|
||||||
|
<rect x="780" y="312" width="200" height="8" fill="rgba(11,13,11,0.06)"/>
|
||||||
|
<line x1="780" y1="320" x2="980" y2="320" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<text x="796" y="296" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">JOIN</text>
|
||||||
|
<text x="796" y="312" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif">ArticleTag</text>
|
||||||
|
<text x="796" y="340" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ article_id</text>
|
||||||
|
<text x="940" y="340" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
<text x="796" y="360" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace">→ tag_id</text>
|
||||||
|
<text x="940" y="360" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end">uuid</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="404" x2="960" y2="404" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="420" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="436" width="14" height="10" rx="2" fill="rgba(247,89,31,0.04)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Aggregate root</text>
|
||||||
|
|
||||||
|
<rect x="180" y="436" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Entity</text>
|
||||||
|
|
||||||
|
<rect x="268" y="436" width="14" height="10" rx="2" fill="rgba(11,13,11,0.04)" stroke="#52534e" stroke-width="1" stroke-dasharray="3,2"/>
|
||||||
|
<text x="288" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Join table</text>
|
||||||
|
|
||||||
|
<text x="372" y="444" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">#</text>
|
||||||
|
<text x="388" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary key</text>
|
||||||
|
|
||||||
|
<text x="476" y="444" fill="#0b0d0b" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">→</text>
|
||||||
|
<text x="492" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Foreign key</text>
|
||||||
|
|
||||||
|
<text x="584" y="444" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" font-weight="600">1 / N</text>
|
||||||
|
<text x="616" y="444" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Cardinality</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Should you write this as a skill?</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Flowchart · Diagram Design</p>
|
||||||
|
<h1>Should you write this as a skill?</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first, behind nodes) -->
|
||||||
|
<!-- Start → Step -->
|
||||||
|
<line x1="500" y1="88" x2="500" y2="120" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Step → Diamond 1 -->
|
||||||
|
<line x1="500" y1="168" x2="500" y2="192" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "NO" right -->
|
||||||
|
<line x1="600" y1="240" x2="720" y2="240" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "YES" down -->
|
||||||
|
<line x1="500" y1="288" x2="500" y2="328" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "NO" right -->
|
||||||
|
<line x1="600" y1="376" x2="720" y2="376" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "YES" down (coral — happy path) -->
|
||||||
|
<line x1="500" y1="424" x2="500" y2="464" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="644" y="230" width="24" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="656" y="239" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="298" width="32" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="500" y="307" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<rect x="644" y="366" width="24" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="656" y="375" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="434" width="32" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="500" y="443" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<!-- Start oval -->
|
||||||
|
<rect x="420" y="40" width="160" height="48" rx="24" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<text x="500" y="68" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">New workflow</text>
|
||||||
|
|
||||||
|
<!-- Rectangle: Step -->
|
||||||
|
<rect x="420" y="120" width="160" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="500" y="148" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Do it manually once</text>
|
||||||
|
|
||||||
|
<!-- Diamond 1: Repeated >3 times? -->
|
||||||
|
<polygon points="500,192 600,240 500,288 400,240" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="500" y="238" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Will you repeat</text>
|
||||||
|
<text x="500" y="252" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">it >3 times?</text>
|
||||||
|
|
||||||
|
<!-- End oval: One-off -->
|
||||||
|
<rect x="720" y="216" width="160" height="48" rx="24" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="240" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">One-off</text>
|
||||||
|
<text x="800" y="254" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">keep manual</text>
|
||||||
|
|
||||||
|
<!-- Diamond 2: Reusable across projects? -->
|
||||||
|
<polygon points="500,328 600,376 500,424 400,376" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="500" y="374" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reusable across</text>
|
||||||
|
<text x="500" y="388" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">projects?</text>
|
||||||
|
|
||||||
|
<!-- End oval: CLAUDE.md note -->
|
||||||
|
<rect x="720" y="352" width="160" height="48" rx="24" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="376" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md note</text>
|
||||||
|
<text x="800" y="390" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">project-scoped</text>
|
||||||
|
|
||||||
|
<!-- End oval: Write a skill (coral focal) -->
|
||||||
|
<rect x="420" y="464" width="160" height="56" rx="28" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="500" y="492" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Write a skill</text>
|
||||||
|
<text x="500" y="508" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">reusable + assets</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="540" x2="960" y2="540" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="556" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND · SHAPE CARRIES TYPE</text>
|
||||||
|
|
||||||
|
<rect x="40" y="572" width="24" height="12" rx="6" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<text x="72" y="582" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Start / end (oval)</text>
|
||||||
|
|
||||||
|
<rect x="220" y="572" width="24" height="12" rx="2" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="252" y="582" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Step (rectangle)</text>
|
||||||
|
|
||||||
|
<polygon points="412,578 424,572 436,578 424,584" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="448" y="582" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Decision (diamond)</text>
|
||||||
|
|
||||||
|
<line x1="604" y1="580" x2="632" y2="580" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="640" y="582" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Happy path</text>
|
||||||
|
|
||||||
|
<line x1="768" y1="580" x2="796" y2="580" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="804" y="582" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Branch</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Should you write this as a skill?</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Flowchart · Diagram Design</p>
|
||||||
|
<h1>Should you write this as a skill?</h1>
|
||||||
|
<p class="subtitle">A three-decision triage for turning a one-off workflow into something reusable. Shape carries type — ovals bracket the flow, rectangles are steps, diamonds are decisions.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first, behind nodes) -->
|
||||||
|
<!-- Start → Step -->
|
||||||
|
<line x1="500" y1="88" x2="500" y2="120" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Step → Diamond 1 -->
|
||||||
|
<line x1="500" y1="168" x2="500" y2="192" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "NO" right -->
|
||||||
|
<line x1="600" y1="240" x2="720" y2="240" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "YES" down -->
|
||||||
|
<line x1="500" y1="288" x2="500" y2="328" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "NO" right -->
|
||||||
|
<line x1="600" y1="376" x2="720" y2="376" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "YES" down (coral — happy path) -->
|
||||||
|
<line x1="500" y1="424" x2="500" y2="464" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="644" y="230" width="24" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="656" y="239" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="298" width="32" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="500" y="307" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<rect x="644" y="366" width="24" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="656" y="375" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="434" width="32" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="500" y="443" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<!-- Start oval -->
|
||||||
|
<rect x="420" y="40" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="500" y="68" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">New workflow</text>
|
||||||
|
|
||||||
|
<!-- Rectangle: Step -->
|
||||||
|
<rect x="420" y="120" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="148" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Do it manually once</text>
|
||||||
|
|
||||||
|
<!-- Diamond 1: Repeated >3 times? -->
|
||||||
|
<polygon points="500,192 600,240 500,288 400,240" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="238" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Will you repeat</text>
|
||||||
|
<text x="500" y="252" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">it >3 times?</text>
|
||||||
|
|
||||||
|
<!-- End oval: One-off -->
|
||||||
|
<rect x="720" y="216" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">One-off</text>
|
||||||
|
<text x="800" y="254" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">keep manual</text>
|
||||||
|
|
||||||
|
<!-- Diamond 2: Reusable across projects? -->
|
||||||
|
<polygon points="500,328 600,376 500,424 400,376" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="374" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reusable across</text>
|
||||||
|
<text x="500" y="388" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">projects?</text>
|
||||||
|
|
||||||
|
<!-- End oval: CLAUDE.md note -->
|
||||||
|
<rect x="720" y="352" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="376" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md note</text>
|
||||||
|
<text x="800" y="390" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">project-scoped</text>
|
||||||
|
|
||||||
|
<!-- End oval: Write a skill (coral focal) -->
|
||||||
|
<rect x="420" y="464" width="160" height="56" rx="28" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="500" y="492" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Write a skill</text>
|
||||||
|
<text x="500" y="508" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">reusable + assets</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="540" x2="960" y2="540" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="556" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND · SHAPE CARRIES TYPE</text>
|
||||||
|
|
||||||
|
<rect x="40" y="572" width="24" height="12" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="72" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Start / end (oval)</text>
|
||||||
|
|
||||||
|
<rect x="220" y="572" width="24" height="12" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="252" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Step (rectangle)</text>
|
||||||
|
|
||||||
|
<polygon points="412,578 424,572 436,578 424,584" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="448" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Decision (diamond)</text>
|
||||||
|
|
||||||
|
<line x1="604" y1="580" x2="632" y2="580" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="640" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Happy path</text>
|
||||||
|
|
||||||
|
<line x1="768" y1="580" x2="796" y2="580" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="804" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Branch</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">WHY THIS FLOWCHART</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Most things aren't skills</h3></div>
|
||||||
|
<p>The happy path is narrow on purpose. Only workflows that clear all three gates earn the overhead of a reusable skill — everything else is better as a project note.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Shape, not color</h3></div>
|
||||||
|
<ul><li>Oval bookends the flow</li><li>Rectangle is a step</li><li>Diamond is a decision</li><li>Color reserved for the happy path</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Every branch gets a label</h3></div>
|
||||||
|
<p>Unlabeled branches turn a flowchart into a maze. Yes/No is fine; conditions in mono when the logic is richer.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>flowchart · should I write this as a skill?</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Should you write this as a skill?</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Flowchart · Diagram Design</p>
|
||||||
|
<h1>Should you write this as a skill?</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first, behind nodes) -->
|
||||||
|
<!-- Start → Step -->
|
||||||
|
<line x1="500" y1="88" x2="500" y2="120" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Step → Diamond 1 -->
|
||||||
|
<line x1="500" y1="168" x2="500" y2="192" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "NO" right -->
|
||||||
|
<line x1="600" y1="240" x2="720" y2="240" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 1 "YES" down -->
|
||||||
|
<line x1="500" y1="288" x2="500" y2="328" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "NO" right -->
|
||||||
|
<line x1="600" y1="376" x2="720" y2="376" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Diamond 2 "YES" down (coral — happy path) -->
|
||||||
|
<line x1="500" y1="424" x2="500" y2="464" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="644" y="230" width="24" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="656" y="239" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="298" width="32" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="500" y="307" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<rect x="644" y="366" width="24" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="656" y="375" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">NO</text>
|
||||||
|
|
||||||
|
<rect x="484" y="434" width="32" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="500" y="443" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">YES</text>
|
||||||
|
|
||||||
|
<!-- Start oval -->
|
||||||
|
<rect x="420" y="40" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="500" y="68" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">New workflow</text>
|
||||||
|
|
||||||
|
<!-- Rectangle: Step -->
|
||||||
|
<rect x="420" y="120" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="148" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Do it manually once</text>
|
||||||
|
|
||||||
|
<!-- Diamond 1: Repeated >3 times? -->
|
||||||
|
<polygon points="500,192 600,240 500,288 400,240" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="238" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Will you repeat</text>
|
||||||
|
<text x="500" y="252" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">it >3 times?</text>
|
||||||
|
|
||||||
|
<!-- End oval: One-off -->
|
||||||
|
<rect x="720" y="216" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">One-off</text>
|
||||||
|
<text x="800" y="254" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">keep manual</text>
|
||||||
|
|
||||||
|
<!-- Diamond 2: Reusable across projects? -->
|
||||||
|
<polygon points="500,328 600,376 500,424 400,376" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="500" y="374" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reusable across</text>
|
||||||
|
<text x="500" y="388" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">projects?</text>
|
||||||
|
|
||||||
|
<!-- End oval: CLAUDE.md note -->
|
||||||
|
<rect x="720" y="352" width="160" height="48" rx="24" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="800" y="376" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md note</text>
|
||||||
|
<text x="800" y="390" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">project-scoped</text>
|
||||||
|
|
||||||
|
<!-- End oval: Write a skill (coral focal) -->
|
||||||
|
<rect x="420" y="464" width="160" height="56" rx="28" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="500" y="492" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Write a skill</text>
|
||||||
|
<text x="500" y="508" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">reusable + assets</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="540" x2="960" y2="540" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="556" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND · SHAPE CARRIES TYPE</text>
|
||||||
|
|
||||||
|
<rect x="40" y="572" width="24" height="12" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="72" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Start / end (oval)</text>
|
||||||
|
|
||||||
|
<rect x="220" y="572" width="24" height="12" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="252" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Step (rectangle)</text>
|
||||||
|
|
||||||
|
<polygon points="412,578 424,572 436,578 424,584" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="448" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Decision (diamond)</text>
|
||||||
|
|
||||||
|
<line x1="604" y1="580" x2="632" y2="580" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="640" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Happy path</text>
|
||||||
|
|
||||||
|
<line x1="768" y1="580" x2="796" y2="580" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="804" y="582" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Branch</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AI app stack · Layer hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Layer stack · Diagram Design</p>
|
||||||
|
<h1>AI app stack · Where the work actually happens</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Direction column (left margin) -->
|
||||||
|
<text x="60" y="68" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">ABSTRACTION</text>
|
||||||
|
<line x1="80" y1="80" x2="80" y2="400" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="76,80 84,80 80,72" fill="#a8a69d"/>
|
||||||
|
<text x="60" y="416" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">SILICON</text>
|
||||||
|
|
||||||
|
<!-- Stack container hairlines (top + bottom edges) -->
|
||||||
|
<line x1="120" y1="80" x2="960" y2="80" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<line x1="120" y1="400" x2="960" y2="400" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- L5 — UI (top layer, lifted fill) -->
|
||||||
|
<rect x="120" y="80" width="840" height="64" fill="#2a2723"/>
|
||||||
|
<line x1="120" y1="144" x2="960" y2="144" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="116" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L5</text>
|
||||||
|
<text x="260" y="118" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif">UI surface</text>
|
||||||
|
<text x="940" y="118" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">chat, editor, canvas</text>
|
||||||
|
|
||||||
|
<!-- L4 — Agent harness (FOCAL, coral tint + coral stroke) -->
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="rgba(255,106,48,0.12)"/>
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="none" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="140" y="180" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">L4</text>
|
||||||
|
<text x="260" y="182" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Agent harness</text>
|
||||||
|
<text x="940" y="182" fill="#ff6a30" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">tools, memory, loop</text>
|
||||||
|
|
||||||
|
<!-- L3 — Prompt layer -->
|
||||||
|
<rect x="120" y="208" width="840" height="64" fill="#1c1a17"/>
|
||||||
|
<line x1="120" y1="272" x2="960" y2="272" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="244" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L3</text>
|
||||||
|
<text x="260" y="246" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Prompt layer</text>
|
||||||
|
<text x="940" y="246" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">system, few-shot, caching</text>
|
||||||
|
|
||||||
|
<!-- L2 — SDK -->
|
||||||
|
<rect x="120" y="272" width="840" height="64" fill="#221f1c"/>
|
||||||
|
<line x1="120" y1="336" x2="960" y2="336" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="308" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L2</text>
|
||||||
|
<text x="260" y="310" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif">SDK / client</text>
|
||||||
|
<text x="940" y="310" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">auth, retries, streaming</text>
|
||||||
|
|
||||||
|
<!-- L1 — Model -->
|
||||||
|
<rect x="120" y="336" width="840" height="64" fill="#221f1c"/>
|
||||||
|
<text x="140" y="372" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L1</text>
|
||||||
|
<text x="260" y="374" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Model weights</text>
|
||||||
|
<text x="940" y="374" fill="#a8a69d" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">opus, sonnet, haiku</text>
|
||||||
|
|
||||||
|
<!-- Caption -->
|
||||||
|
<text x="120" y="456" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">FOCAL LAYER</text>
|
||||||
|
<text x="240" y="456" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">The harness is where most product differentiation actually lives — tools, memory, and the loop that stitches model calls into useful work.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AI app stack · Layer hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Layer stack · Diagram Design</p>
|
||||||
|
<h1>AI app stack · Where the work actually happens</h1>
|
||||||
|
<p class="subtitle">Five layers between silicon and the user. Most teams over-invest in the model and under-invest in the harness — which is the layer that decides whether the app feels magical or flaky.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Direction column (left margin) -->
|
||||||
|
<text x="60" y="68" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">ABSTRACTION</text>
|
||||||
|
<line x1="80" y1="80" x2="80" y2="400" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="76,80 84,80 80,72" fill="#52534e"/>
|
||||||
|
<text x="60" y="416" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">SILICON</text>
|
||||||
|
|
||||||
|
<!-- Stack container hairlines (top + bottom edges) -->
|
||||||
|
<line x1="120" y1="80" x2="960" y2="80" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<line x1="120" y1="400" x2="960" y2="400" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- L5 — UI (top layer, near-white fill) -->
|
||||||
|
<rect x="120" y="80" width="840" height="64" fill="#ffffff"/>
|
||||||
|
<line x1="120" y1="144" x2="960" y2="144" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="116" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L5</text>
|
||||||
|
<text x="260" y="118" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">UI surface</text>
|
||||||
|
<text x="940" y="118" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">chat, editor, canvas</text>
|
||||||
|
|
||||||
|
<!-- L4 — Agent harness (FOCAL, coral tint + coral stroke) -->
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="rgba(247,89,31,0.08)"/>
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="none" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="140" y="180" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">L4</text>
|
||||||
|
<text x="260" y="182" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Agent harness</text>
|
||||||
|
<text x="940" y="182" fill="#f7591f" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">tools, memory, loop</text>
|
||||||
|
|
||||||
|
<!-- L3 — Prompt layer -->
|
||||||
|
<rect x="120" y="208" width="840" height="64" fill="#f5f4ed"/>
|
||||||
|
<line x1="120" y1="272" x2="960" y2="272" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="244" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L3</text>
|
||||||
|
<text x="260" y="246" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Prompt layer</text>
|
||||||
|
<text x="940" y="246" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">system, few-shot, caching</text>
|
||||||
|
|
||||||
|
<!-- L2 — SDK -->
|
||||||
|
<rect x="120" y="272" width="840" height="64" fill="#efeee5"/>
|
||||||
|
<line x1="120" y1="336" x2="960" y2="336" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="308" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L2</text>
|
||||||
|
<text x="260" y="310" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">SDK / client</text>
|
||||||
|
<text x="940" y="310" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">auth, retries, streaming</text>
|
||||||
|
|
||||||
|
<!-- L1 — Model -->
|
||||||
|
<rect x="120" y="336" width="840" height="64" fill="#efeee5"/>
|
||||||
|
<text x="140" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L1</text>
|
||||||
|
<text x="260" y="374" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Model weights</text>
|
||||||
|
<text x="940" y="374" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">opus, sonnet, haiku</text>
|
||||||
|
|
||||||
|
<!-- Caption -->
|
||||||
|
<text x="120" y="456" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">FOCAL LAYER</text>
|
||||||
|
<text x="240" y="456" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">The harness is where most product differentiation actually lives — tools, memory, and the loop that stitches model calls into useful work.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Coral marks the layer that pays rent</h3></div>
|
||||||
|
<p>Swapping models is a knob. Rewriting the harness is a product decision. The coral band says: this is the layer where you out-execute the competition, not the one where you chase leaderboards.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Reading the stack</h3></div>
|
||||||
|
<ul><li>Abstraction rises up the left column</li><li>L-index on the left, note on the right</li><li>Hairlines between layers, no shadows</li><li>Fill shade shifts from paper-2 to white</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Why only five</h3></div>
|
||||||
|
<p>Six-plus layers become a legend, not a diagram. Five holds the whole thing on one screen — every band readable without squinting, every note a scan away.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>ai app stack · layer hierarchy</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AI app stack · Layer hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Layer stack · Diagram Design</p>
|
||||||
|
<h1>AI app stack · Where the work actually happens</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Direction column (left margin) -->
|
||||||
|
<text x="60" y="68" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">ABSTRACTION</text>
|
||||||
|
<line x1="80" y1="80" x2="80" y2="400" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="76,80 84,80 80,72" fill="#52534e"/>
|
||||||
|
<text x="60" y="416" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">SILICON</text>
|
||||||
|
|
||||||
|
<!-- Stack container hairlines (top + bottom edges) -->
|
||||||
|
<line x1="120" y1="80" x2="960" y2="80" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<line x1="120" y1="400" x2="960" y2="400" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- L5 — UI (top layer, near-white fill) -->
|
||||||
|
<rect x="120" y="80" width="840" height="64" fill="#ffffff"/>
|
||||||
|
<line x1="120" y1="144" x2="960" y2="144" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="116" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L5</text>
|
||||||
|
<text x="260" y="118" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">UI surface</text>
|
||||||
|
<text x="940" y="118" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">chat, editor, canvas</text>
|
||||||
|
|
||||||
|
<!-- L4 — Agent harness (FOCAL, coral tint + coral stroke) -->
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="rgba(247,89,31,0.08)"/>
|
||||||
|
<rect x="120" y="144" width="840" height="64" fill="none" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="140" y="180" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">L4</text>
|
||||||
|
<text x="260" y="182" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Agent harness</text>
|
||||||
|
<text x="940" y="182" fill="#f7591f" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">tools, memory, loop</text>
|
||||||
|
|
||||||
|
<!-- L3 — Prompt layer -->
|
||||||
|
<rect x="120" y="208" width="840" height="64" fill="#f5f4ed"/>
|
||||||
|
<line x1="120" y1="272" x2="960" y2="272" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="244" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L3</text>
|
||||||
|
<text x="260" y="246" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Prompt layer</text>
|
||||||
|
<text x="940" y="246" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">system, few-shot, caching</text>
|
||||||
|
|
||||||
|
<!-- L2 — SDK -->
|
||||||
|
<rect x="120" y="272" width="840" height="64" fill="#efeee5"/>
|
||||||
|
<line x1="120" y1="336" x2="960" y2="336" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="140" y="308" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L2</text>
|
||||||
|
<text x="260" y="310" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">SDK / client</text>
|
||||||
|
<text x="940" y="310" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">auth, retries, streaming</text>
|
||||||
|
|
||||||
|
<!-- L1 — Model -->
|
||||||
|
<rect x="120" y="336" width="840" height="64" fill="#efeee5"/>
|
||||||
|
<text x="140" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">L1</text>
|
||||||
|
<text x="260" y="374" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif">Model weights</text>
|
||||||
|
<text x="940" y="374" fill="#52534e" font-size="10" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.08em">opus, sonnet, haiku</text>
|
||||||
|
|
||||||
|
<!-- Caption -->
|
||||||
|
<text x="120" y="456" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">FOCAL LAYER</text>
|
||||||
|
<text x="240" y="456" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">The harness is where most product differentiation actually lives — tools, memory, and the loop that stitches model calls into useful work.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>The CLAUDE.md Hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Nested · Diagram Design</p>
|
||||||
|
<h1>The CLAUDE.md Hierarchy</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Level 1: ~/.claude/ (global) — outermost -->
|
||||||
|
<rect x="40" y="60" width="920" height="380" rx="8" fill="rgba(241,239,231,0.015)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<!-- Level 2: ~/vault/ -->
|
||||||
|
<rect x="72" y="96" width="856" height="308" rx="8" fill="rgba(241,239,231,0.02)" stroke="rgba(241,239,231,0.35)" stroke-width="1"/>
|
||||||
|
<!-- Level 3: /business -->
|
||||||
|
<rect x="104" y="132" width="792" height="236" rx="8" fill="rgba(241,239,231,0.025)" stroke="rgba(241,239,231,0.45)" stroke-width="1"/>
|
||||||
|
<!-- Level 4: /marketing -->
|
||||||
|
<rect x="136" y="168" width="728" height="164" rx="8" fill="rgba(241,239,231,0.03)" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<!-- Level 5: /project — innermost, coral focal -->
|
||||||
|
<rect x="168" y="204" width="664" height="92" rx="8" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Level labels on paper-colored masks -->
|
||||||
|
<rect x="56" y="52" width="188" height="16" fill="#1c1a17"/>
|
||||||
|
<text x="64" y="64" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/.claude/ (global)</text>
|
||||||
|
|
||||||
|
<rect x="88" y="88" width="148" height="16" fill="#1c1a17"/>
|
||||||
|
<text x="96" y="100" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/vault/ (notes)</text>
|
||||||
|
|
||||||
|
<rect x="120" y="124" width="96" height="16" fill="#1c1a17"/>
|
||||||
|
<text x="128" y="136" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/business</text>
|
||||||
|
|
||||||
|
<rect x="152" y="160" width="108" height="16" fill="#1c1a17"/>
|
||||||
|
<text x="160" y="172" fill="#f1efe7" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/marketing</text>
|
||||||
|
|
||||||
|
<rect x="184" y="196" width="88" height="16" fill="#1c1a17"/>
|
||||||
|
<text x="192" y="208" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">/project</text>
|
||||||
|
|
||||||
|
<!-- File-icon glyphs (simple rect with folded corner) inside each level -->
|
||||||
|
<g transform="translate(908, 408)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#1c1a17" stroke="rgba(241,239,231,0.35)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(241,239,231,0.35)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(876, 372)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#1c1a17" stroke="rgba(241,239,231,0.40)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(241,239,231,0.40)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(844, 336)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#1c1a17" stroke="rgba(241,239,231,0.50)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(241,239,231,0.50)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(812, 300)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#1c1a17" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Innermost label — human readable -->
|
||||||
|
<text x="500" y="248" fill="#f1efe7" font-size="16" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md</text>
|
||||||
|
<text x="500" y="272" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">inherits every level above</text>
|
||||||
|
|
||||||
|
<!-- Annotation top-right: italic callout with curved arrow -->
|
||||||
|
<text x="904" y="36" fill="#f1efe7" font-size="14" font-style="italic" font-family="'Instrument Serif', serif" text-anchor="end">no imports, no configuration</text>
|
||||||
|
<path d="M 820 44 Q 700 84 520 216" fill="none" stroke="rgba(241,239,231,0.40)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<circle cx="520" cy="216" r="2" fill="#f1efe7"/>
|
||||||
|
|
||||||
|
<!-- Annotation bottom-left: italic callout -->
|
||||||
|
<text x="40" y="484" fill="#a8a69d" font-size="14" font-style="italic" font-family="'Instrument Serif', serif">structure IS the index</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>The CLAUDE.md Hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Nested · Diagram Design</p>
|
||||||
|
<h1>The CLAUDE.md Hierarchy</h1>
|
||||||
|
<p class="subtitle">How Claude Code composes context from every folder level above. Each outer ring is a broader scope; the innermost box is where the work happens — inheriting every instruction above it without a single import statement.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Level 1: ~/.claude/ (global) — outermost -->
|
||||||
|
<rect x="40" y="60" width="920" height="380" rx="8" fill="rgba(11,13,11,0.015)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<!-- Level 2: ~/vault/ -->
|
||||||
|
<rect x="72" y="96" width="856" height="308" rx="8" fill="rgba(11,13,11,0.02)" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
<!-- Level 3: /business -->
|
||||||
|
<rect x="104" y="132" width="792" height="236" rx="8" fill="rgba(11,13,11,0.025)" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
<!-- Level 4: /marketing -->
|
||||||
|
<rect x="136" y="168" width="728" height="164" rx="8" fill="rgba(11,13,11,0.03)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<!-- Level 5: /project — innermost, coral focal -->
|
||||||
|
<rect x="168" y="204" width="664" height="92" rx="8" fill="rgba(247,89,31,0.06)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Level labels on paper-2-colored masks (match diagram-container bg) -->
|
||||||
|
<rect x="56" y="52" width="188" height="16" fill="#efeee5"/>
|
||||||
|
<text x="64" y="64" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/.claude/ (global)</text>
|
||||||
|
|
||||||
|
<rect x="88" y="88" width="148" height="16" fill="#efeee5"/>
|
||||||
|
<text x="96" y="100" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/vault/ (notes)</text>
|
||||||
|
|
||||||
|
<rect x="120" y="124" width="96" height="16" fill="#efeee5"/>
|
||||||
|
<text x="128" y="136" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/business</text>
|
||||||
|
|
||||||
|
<rect x="152" y="160" width="108" height="16" fill="#efeee5"/>
|
||||||
|
<text x="160" y="172" fill="#0b0d0b" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/marketing</text>
|
||||||
|
|
||||||
|
<rect x="184" y="196" width="88" height="16" fill="#efeee5"/>
|
||||||
|
<text x="192" y="208" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">/project</text>
|
||||||
|
|
||||||
|
<!-- File-icon glyphs inside each level -->
|
||||||
|
<g transform="translate(908, 408)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#efeee5" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(876, 372)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#efeee5" stroke="rgba(11,13,11,0.40)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.40)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(844, 336)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#efeee5" stroke="rgba(11,13,11,0.50)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.50)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(812, 300)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#efeee5" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Innermost label — human readable -->
|
||||||
|
<text x="500" y="248" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md</text>
|
||||||
|
<text x="500" y="272" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">inherits every level above</text>
|
||||||
|
|
||||||
|
<!-- Annotation top-right: italic callout with curved arrow -->
|
||||||
|
<text x="904" y="36" fill="#0b0d0b" font-size="14" font-style="italic" font-family="'Instrument Serif', serif" text-anchor="end">no imports, no configuration</text>
|
||||||
|
<path d="M 820 44 Q 700 84 520 216" fill="none" stroke="rgba(11,13,11,0.40)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<circle cx="520" cy="216" r="2" fill="#0b0d0b"/>
|
||||||
|
|
||||||
|
<!-- Annotation bottom-left: italic callout -->
|
||||||
|
<text x="40" y="484" fill="#52534e" font-size="14" font-style="italic" font-family="'Instrument Serif', serif">structure IS the index</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Containment is the config</h3></div>
|
||||||
|
<p>Every CLAUDE.md inside a parent folder implicitly wraps the one below. No manifest, no import graph — the file tree itself declares scope, so renaming a folder is the only migration you'll ever run.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Reads outside-in</h3></div>
|
||||||
|
<ul><li>Global rules load first</li><li>Each parent narrows context</li><li>Innermost file gets the last word</li><li>Conflicts resolve by specificity</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Five levels is the ceiling</h3></div>
|
||||||
|
<p>Past five rings the diagram stops teaching — and so does the filesystem. If you need six levels of instruction, you probably need two projects instead.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>the claude.md hierarchy · nested containment</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>The CLAUDE.md Hierarchy</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Nested · Diagram Design</p>
|
||||||
|
<h1>The CLAUDE.md Hierarchy</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Level 1: ~/.claude/ (global) — outermost -->
|
||||||
|
<rect x="40" y="60" width="920" height="380" rx="8" fill="rgba(11,13,11,0.015)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<!-- Level 2: ~/vault/ -->
|
||||||
|
<rect x="72" y="96" width="856" height="308" rx="8" fill="rgba(11,13,11,0.02)" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
<!-- Level 3: /business -->
|
||||||
|
<rect x="104" y="132" width="792" height="236" rx="8" fill="rgba(11,13,11,0.025)" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
<!-- Level 4: /marketing -->
|
||||||
|
<rect x="136" y="168" width="728" height="164" rx="8" fill="rgba(11,13,11,0.03)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<!-- Level 5: /project — innermost, coral focal -->
|
||||||
|
<rect x="168" y="204" width="664" height="92" rx="8" fill="rgba(247,89,31,0.06)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Level labels on paper-colored masks -->
|
||||||
|
<rect x="56" y="52" width="188" height="16" fill="#f5f4ed"/>
|
||||||
|
<text x="64" y="64" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/.claude/ (global)</text>
|
||||||
|
|
||||||
|
<rect x="88" y="88" width="148" height="16" fill="#f5f4ed"/>
|
||||||
|
<text x="96" y="100" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">~/vault/ (notes)</text>
|
||||||
|
|
||||||
|
<rect x="120" y="124" width="96" height="16" fill="#f5f4ed"/>
|
||||||
|
<text x="128" y="136" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/business</text>
|
||||||
|
|
||||||
|
<rect x="152" y="160" width="108" height="16" fill="#f5f4ed"/>
|
||||||
|
<text x="160" y="172" fill="#0b0d0b" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">/marketing</text>
|
||||||
|
|
||||||
|
<rect x="184" y="196" width="88" height="16" fill="#f5f4ed"/>
|
||||||
|
<text x="192" y="208" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em" font-weight="600">/project</text>
|
||||||
|
|
||||||
|
<!-- File-icon glyphs (simple rect with folded corner) inside each level -->
|
||||||
|
<!-- Glyph in level 1 (bottom-right area of outer ring) -->
|
||||||
|
<g transform="translate(908, 408)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#f5f4ed" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.35)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<!-- Glyph in level 2 ring -->
|
||||||
|
<g transform="translate(876, 372)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#f5f4ed" stroke="rgba(11,13,11,0.40)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.40)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<!-- Glyph in level 3 ring -->
|
||||||
|
<g transform="translate(844, 336)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#f5f4ed" stroke="rgba(11,13,11,0.50)" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="rgba(11,13,11,0.50)" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<!-- Glyph in level 4 ring -->
|
||||||
|
<g transform="translate(812, 300)">
|
||||||
|
<path d="M0 0 L16 0 L20 4 L20 20 L0 20 Z" fill="#f5f4ed" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M16 0 L16 4 L20 4" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Innermost label — human readable -->
|
||||||
|
<text x="500" y="248" fill="#0b0d0b" font-size="16" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CLAUDE.md</text>
|
||||||
|
<text x="500" y="272" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">inherits every level above</text>
|
||||||
|
|
||||||
|
<!-- Annotation top-right: italic callout with curved arrow -->
|
||||||
|
<text x="904" y="36" fill="#0b0d0b" font-size="14" font-style="italic" font-family="'Instrument Serif', serif" text-anchor="end">no imports, no configuration</text>
|
||||||
|
<path d="M 820 44 Q 700 84 520 216" fill="none" stroke="rgba(11,13,11,0.40)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<circle cx="520" cy="216" r="2" fill="#0b0d0b"/>
|
||||||
|
|
||||||
|
<!-- Annotation bottom-left: italic callout -->
|
||||||
|
<text x="40" y="484" fill="#52534e" font-size="14" font-style="italic" font-family="'Instrument Serif', serif">structure IS the index</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content pyramid · what compounds</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Pyramid · Diagram Design</p>
|
||||||
|
<h1>Content pyramid · what compounds</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Left axis: direction of rarity -->
|
||||||
|
<line x1="100" y1="112" x2="100" y2="320" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="96,112 104,112 100,100" fill="rgba(241,239,231,0.45)"/>
|
||||||
|
<text x="80" y="216" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="middle" transform="rotate(-90 80 216)">RARER · FEWER · COMPOUNDS ↑</text>
|
||||||
|
|
||||||
|
<!-- Base layer: Short posts -->
|
||||||
|
<polygon points="240,280 760,280 820,344 180,344" fill="rgba(241,239,231,0.04)" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="308" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Short posts</text>
|
||||||
|
<text x="500" y="324" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">daily · ~200 words</text>
|
||||||
|
<text x="836" y="316" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~240/yr</text>
|
||||||
|
|
||||||
|
<!-- L3: Essays -->
|
||||||
|
<polygon points="300,216 700,216 760,280 240,280" fill="rgba(241,239,231,0.04)" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="244" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Essays</text>
|
||||||
|
<text x="500" y="260" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">weekly · 800–1,500 words</text>
|
||||||
|
<text x="776" y="252" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~48/yr</text>
|
||||||
|
|
||||||
|
<!-- L2: Long-form guides -->
|
||||||
|
<polygon points="360,152 640,152 700,216 300,216" fill="rgba(241,239,231,0.04)" stroke="rgba(241,239,231,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="180" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Long-form guides</text>
|
||||||
|
<text x="500" y="196" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">quarterly · 4,000+ words</text>
|
||||||
|
<text x="716" y="188" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~4/yr</text>
|
||||||
|
|
||||||
|
<!-- Apex: Flagship book — CORAL FOCAL (triangle, pointed) -->
|
||||||
|
<polygon points="500,76 640,152 360,152" fill="rgba(255,106,48,0.10)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="500" y="120" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Flagship book</text>
|
||||||
|
<text x="500" y="136" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">every 3–5 years</text>
|
||||||
|
<text x="656" y="112" fill="#ff6a30" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">the apex</text>
|
||||||
|
|
||||||
|
<!-- Footnote under pyramid -->
|
||||||
|
<text x="500" y="384" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" text-anchor="middle" font-style="italic">The base funds the apex. The apex defines the base.</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="436" x2="960" y2="436" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="452" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="464" width="16" height="12" fill="rgba(255,106,48,0.10)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="64" y="474" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Apex — rarest, highest leverage</text>
|
||||||
|
|
||||||
|
<rect x="280" y="464" width="16" height="12" fill="rgba(241,239,231,0.04)" stroke="rgba(241,239,231,0.25)" stroke-width="1"/>
|
||||||
|
<text x="304" y="474" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Supporting layer — the volume work</text>
|
||||||
|
|
||||||
|
<text x="560" y="474" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Layer width is honest: narrower = rarer shipping cadence.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content pyramid · what compounds</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Pyramid · Diagram Design</p>
|
||||||
|
<h1>Content pyramid · what compounds</h1>
|
||||||
|
<p class="subtitle">Four layers of output, ordered by how rarely they ship and how far they travel. The base keeps you present. The apex defines the body of work — and it's the only layer anyone quotes back a decade later.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Left axis: direction of rarity -->
|
||||||
|
<line x1="100" y1="112" x2="100" y2="320" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="96,112 104,112 100,100" fill="rgba(11,13,11,0.45)"/>
|
||||||
|
<text x="80" y="216" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="middle" transform="rotate(-90 80 216)">RARER · FEWER · COMPOUNDS ↑</text>
|
||||||
|
|
||||||
|
<!-- Base layer: Short posts -->
|
||||||
|
<polygon points="240,280 760,280 820,344 180,344" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Short posts</text>
|
||||||
|
<text x="500" y="324" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">daily · ~200 words</text>
|
||||||
|
<text x="836" y="316" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~240/yr</text>
|
||||||
|
|
||||||
|
<!-- L3: Essays -->
|
||||||
|
<polygon points="300,216 700,216 760,280 240,280" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="244" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Essays</text>
|
||||||
|
<text x="500" y="260" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">weekly · 800–1,500 words</text>
|
||||||
|
<text x="776" y="252" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~48/yr</text>
|
||||||
|
|
||||||
|
<!-- L2: Long-form guides -->
|
||||||
|
<polygon points="360,152 640,152 700,216 300,216" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="180" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Long-form guides</text>
|
||||||
|
<text x="500" y="196" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">quarterly · 4,000+ words</text>
|
||||||
|
<text x="716" y="188" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~4/yr</text>
|
||||||
|
|
||||||
|
<!-- Apex: Flagship book — CORAL FOCAL (triangle, pointed) -->
|
||||||
|
<polygon points="500,76 640,152 360,152" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="500" y="120" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Flagship book</text>
|
||||||
|
<text x="500" y="136" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">every 3–5 years</text>
|
||||||
|
<text x="656" y="112" fill="#f7591f" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">the apex</text>
|
||||||
|
|
||||||
|
<!-- Footnote under pyramid -->
|
||||||
|
<text x="500" y="384" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" text-anchor="middle" font-style="italic">The base funds the apex. The apex defines the base.</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="436" x2="960" y2="436" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="452" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="464" width="16" height="12" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="64" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Apex — rarest, highest leverage</text>
|
||||||
|
|
||||||
|
<rect x="280" y="464" width="16" height="12" fill="#efeee5" stroke="rgba(11,13,11,0.25)" stroke-width="1"/>
|
||||||
|
<text x="304" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Supporting layer — the volume work</text>
|
||||||
|
|
||||||
|
<text x="560" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Layer width is honest: narrower = rarer shipping cadence.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>One coral layer, on purpose</h3></div>
|
||||||
|
<p>A five-colour pyramid is a children's diagram. Reserving coral for the apex makes the whole structure actually say something: this is the rarest thing you'll make, and it's the one the rest of the pyramid is feeding.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Width tells the truth</h3></div>
|
||||||
|
<ul><li>~240 short posts a year</li><li>~48 essays</li><li>~4 long-form guides</li><li>1 flagship every 3–5 years</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Pyramids only work for hierarchy</h3></div>
|
||||||
|
<p>If your four categories don't have a rarity order — if a bullet list would communicate it — use bullets. Pyramids promise you're trading volume for value as you climb.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>content pyramid · what compounds</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content pyramid · what compounds</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Pyramid · Diagram Design</p>
|
||||||
|
<h1>Content pyramid · what compounds</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Left axis: direction of rarity -->
|
||||||
|
<line x1="100" y1="112" x2="100" y2="320" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<polygon points="96,112 104,112 100,100" fill="rgba(11,13,11,0.45)"/>
|
||||||
|
<text x="80" y="216" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="middle" transform="rotate(-90 80 216)">RARER · FEWER · COMPOUNDS ↑</text>
|
||||||
|
|
||||||
|
<!-- Base layer: Short posts -->
|
||||||
|
<polygon points="240,280 760,280 820,344 180,344" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Short posts</text>
|
||||||
|
<text x="500" y="324" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">daily · ~200 words</text>
|
||||||
|
<text x="836" y="316" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~240/yr</text>
|
||||||
|
|
||||||
|
<!-- L3: Essays -->
|
||||||
|
<polygon points="300,216 700,216 760,280 240,280" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="244" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Essays</text>
|
||||||
|
<text x="500" y="260" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">weekly · 800–1,500 words</text>
|
||||||
|
<text x="776" y="252" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~48/yr</text>
|
||||||
|
|
||||||
|
<!-- L2: Long-form guides -->
|
||||||
|
<polygon points="360,152 640,152 700,216 300,216" fill="#efeee5" stroke="rgba(11,13,11,0.12)" stroke-width="1"/>
|
||||||
|
<text x="500" y="180" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Long-form guides</text>
|
||||||
|
<text x="500" y="196" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">quarterly · 4,000+ words</text>
|
||||||
|
<text x="716" y="188" fill="#65655c" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">~4/yr</text>
|
||||||
|
|
||||||
|
<!-- Apex: Flagship book — CORAL FOCAL (triangle, pointed) -->
|
||||||
|
<polygon points="500,76 640,152 360,152" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="500" y="120" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Flagship book</text>
|
||||||
|
<text x="500" y="136" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">every 3–5 years</text>
|
||||||
|
<text x="656" y="112" fill="#f7591f" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.08em">the apex</text>
|
||||||
|
|
||||||
|
<!-- Footnote under pyramid -->
|
||||||
|
<text x="500" y="384" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" text-anchor="middle" font-style="italic">The base funds the apex. The apex defines the base.</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="436" x2="960" y2="436" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="452" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="464" width="16" height="12" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="64" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Apex — rarest, highest leverage</text>
|
||||||
|
|
||||||
|
<rect x="280" y="464" width="16" height="12" fill="#efeee5" stroke="rgba(11,13,11,0.25)" stroke-width="1"/>
|
||||||
|
<text x="304" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Supporting layer — the volume work</text>
|
||||||
|
|
||||||
|
<text x="560" y="474" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Layer width is honest: narrower = rarer shipping cadence.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content ideas · Impact × Effort</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Quadrant · Diagram Design</p>
|
||||||
|
<h1>Content ideas · Impact × Effort</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Quadrant backgrounds (very subtle, only coral on DO FIRST) -->
|
||||||
|
<rect x="120" y="80" width="380" height="170" fill="rgba(255,106,48,0.03)"/>
|
||||||
|
|
||||||
|
<!-- Axis cross -->
|
||||||
|
<line x1="120" y1="250" x2="880" y2="250" stroke="rgba(241,239,231,0.45)" stroke-width="1"/>
|
||||||
|
<line x1="500" y1="80" x2="500" y2="420" stroke="rgba(241,239,231,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Axis end labels -->
|
||||||
|
<text x="880" y="266" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">HIGH EFFORT →</text>
|
||||||
|
<text x="120" y="266" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">← LOW EFFORT</text>
|
||||||
|
<text x="512" y="80" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↑ HIGH IMPACT</text>
|
||||||
|
<text x="512" y="432" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↓ LOW IMPACT</text>
|
||||||
|
|
||||||
|
<!-- Quadrant corner labels -->
|
||||||
|
<text x="140" y="104" fill="#ff6a30" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em" font-weight="600">DO FIRST</text>
|
||||||
|
<text x="860" y="104" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">MAJOR PROJECTS</text>
|
||||||
|
<text x="140" y="412" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">QUICK WINS</text>
|
||||||
|
<text x="860" y="412" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">AVOID</text>
|
||||||
|
|
||||||
|
<!-- Items: TL (Do First) -->
|
||||||
|
<!-- coral focal -->
|
||||||
|
<circle cx="220" cy="140" r="6" fill="#ff6a30"/>
|
||||||
|
<text x="232" y="144" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif">diagram-design</text>
|
||||||
|
|
||||||
|
<circle cx="320" cy="200" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="332" y="204" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Update changelog</text>
|
||||||
|
|
||||||
|
<!-- Items: TR (Major Projects) -->
|
||||||
|
<circle cx="620" cy="140" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="632" y="144" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Design v4 refresh</text>
|
||||||
|
|
||||||
|
<circle cx="760" cy="180" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="772" y="184" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">New publication</text>
|
||||||
|
|
||||||
|
<!-- Items: BL (Quick Wins) -->
|
||||||
|
<circle cx="260" cy="320" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="272" y="324" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Fix footer link</text>
|
||||||
|
|
||||||
|
<circle cx="360" cy="380" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="372" y="384" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Update OG tags</text>
|
||||||
|
|
||||||
|
<!-- Items: BR (Avoid) -->
|
||||||
|
<circle cx="640" cy="380" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="652" y="384" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Rewrite build pipeline</text>
|
||||||
|
|
||||||
|
<circle cx="780" cy="320" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="792" y="324" fill="#a8a69d" font-size="11" font-family="'Geist', sans-serif">Port to Nuxt</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="#ff6a30"/>
|
||||||
|
<text x="68" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Start tomorrow</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="208" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Candidate project</text>
|
||||||
|
|
||||||
|
<text x="336" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Position is the signal. Colour is reserved for the single action item.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content ideas · Impact × Effort</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Quadrant · Diagram Design</p>
|
||||||
|
<h1>Content ideas · Impact × Effort</h1>
|
||||||
|
<p class="subtitle">Eight candidate projects mapped by the pay-off they'd generate against the cost to ship. Position is the signal — color is reserved for the one item worth starting tomorrow.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Quadrant backgrounds (very subtle, only coral on DO FIRST) -->
|
||||||
|
<rect x="120" y="80" width="380" height="170" fill="rgba(247,89,31,0.03)"/>
|
||||||
|
|
||||||
|
<!-- Axis cross -->
|
||||||
|
<line x1="120" y1="250" x2="880" y2="250" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
<line x1="500" y1="80" x2="500" y2="420" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Axis end labels -->
|
||||||
|
<text x="880" y="266" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">HIGH EFFORT →</text>
|
||||||
|
<text x="120" y="266" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">← LOW EFFORT</text>
|
||||||
|
<text x="512" y="80" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↑ HIGH IMPACT</text>
|
||||||
|
<text x="512" y="432" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↓ LOW IMPACT</text>
|
||||||
|
|
||||||
|
<!-- Quadrant corner labels -->
|
||||||
|
<text x="140" y="104" fill="#f7591f" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em" font-weight="600">DO FIRST</text>
|
||||||
|
<text x="860" y="104" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">MAJOR PROJECTS</text>
|
||||||
|
<text x="140" y="412" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">QUICK WINS</text>
|
||||||
|
<text x="860" y="412" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">AVOID</text>
|
||||||
|
|
||||||
|
<!-- Items: TL (Do First) -->
|
||||||
|
<!-- coral focal -->
|
||||||
|
<circle cx="220" cy="140" r="6" fill="#f7591f"/>
|
||||||
|
<text x="232" y="144" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif">diagram-design</text>
|
||||||
|
|
||||||
|
<circle cx="320" cy="200" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="332" y="204" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Update changelog</text>
|
||||||
|
|
||||||
|
<!-- Items: TR (Major Projects) -->
|
||||||
|
<circle cx="620" cy="140" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="632" y="144" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Design v4 refresh</text>
|
||||||
|
|
||||||
|
<circle cx="760" cy="180" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="772" y="184" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">New publication</text>
|
||||||
|
|
||||||
|
<!-- Items: BL (Quick Wins) -->
|
||||||
|
<circle cx="260" cy="320" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="272" y="324" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Fix footer link</text>
|
||||||
|
|
||||||
|
<circle cx="360" cy="380" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="372" y="384" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Update OG tags</text>
|
||||||
|
|
||||||
|
<!-- Items: BR (Avoid) -->
|
||||||
|
<circle cx="640" cy="380" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="652" y="384" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Rewrite build pipeline</text>
|
||||||
|
|
||||||
|
<circle cx="780" cy="320" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="792" y="324" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Port to Nuxt</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="#f7591f"/>
|
||||||
|
<text x="68" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Start tomorrow</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="208" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Candidate project</text>
|
||||||
|
|
||||||
|
<text x="336" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Position is the signal. Colour is reserved for the single action item.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>One coral dot, on purpose</h3></div>
|
||||||
|
<p>A 2×2 with four coloured quadrants is a poster, not a decision tool. Reserving coral for the single item you commit to tomorrow makes the matrix actually prioritise.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Axes labelled at ends</h3></div>
|
||||||
|
<ul><li>HIGH / LOW at the extremes</li><li>Arrows show direction</li><li>No labels at midpoints</li><li>The cross is 1px ink, not a box</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Empty BR isn't wasted</h3></div>
|
||||||
|
<p>Two items in "Avoid" is informative — it says the team considered them and rejected them. A blank quadrant would leave you wondering.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>content ideas · impact × effort</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Content ideas · Impact × Effort</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Quadrant · Diagram Design</p>
|
||||||
|
<h1>Content ideas · Impact × Effort</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Quadrant backgrounds (very subtle, only coral on DO FIRST) -->
|
||||||
|
<rect x="120" y="80" width="380" height="170" fill="rgba(247,89,31,0.03)"/>
|
||||||
|
|
||||||
|
<!-- Axis cross -->
|
||||||
|
<line x1="120" y1="250" x2="880" y2="250" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
<line x1="500" y1="80" x2="500" y2="420" stroke="rgba(11,13,11,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Axis end labels -->
|
||||||
|
<text x="880" y="266" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">HIGH EFFORT →</text>
|
||||||
|
<text x="120" y="266" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">← LOW EFFORT</text>
|
||||||
|
<text x="512" y="80" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↑ HIGH IMPACT</text>
|
||||||
|
<text x="512" y="432" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.14em">↓ LOW IMPACT</text>
|
||||||
|
|
||||||
|
<!-- Quadrant corner labels -->
|
||||||
|
<text x="140" y="104" fill="#f7591f" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em" font-weight="600">DO FIRST</text>
|
||||||
|
<text x="860" y="104" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">MAJOR PROJECTS</text>
|
||||||
|
<text x="140" y="412" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">QUICK WINS</text>
|
||||||
|
<text x="860" y="412" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.18em">AVOID</text>
|
||||||
|
|
||||||
|
<!-- Items: TL (Do First) -->
|
||||||
|
<!-- coral focal -->
|
||||||
|
<circle cx="220" cy="140" r="6" fill="#f7591f"/>
|
||||||
|
<text x="232" y="144" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif">diagram-design</text>
|
||||||
|
|
||||||
|
<circle cx="320" cy="200" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="332" y="204" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Update changelog</text>
|
||||||
|
|
||||||
|
<!-- Items: TR (Major Projects) -->
|
||||||
|
<circle cx="620" cy="140" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="632" y="144" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Design v4 refresh</text>
|
||||||
|
|
||||||
|
<circle cx="760" cy="180" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="772" y="184" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">New publication</text>
|
||||||
|
|
||||||
|
<!-- Items: BL (Quick Wins) -->
|
||||||
|
<circle cx="260" cy="320" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="272" y="324" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Fix footer link</text>
|
||||||
|
|
||||||
|
<circle cx="360" cy="380" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="372" y="384" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Update OG tags</text>
|
||||||
|
|
||||||
|
<!-- Items: BR (Avoid) -->
|
||||||
|
<circle cx="640" cy="380" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="652" y="384" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Rewrite build pipeline</text>
|
||||||
|
|
||||||
|
<circle cx="780" cy="320" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="792" y="324" fill="#52534e" font-size="11" font-family="'Geist', sans-serif">Port to Nuxt</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="#f7591f"/>
|
||||||
|
<text x="68" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Start tomorrow</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="208" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Candidate project</text>
|
||||||
|
|
||||||
|
<text x="336" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Position is the signal. Colour is reserved for the single action item.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article request · Sequence</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Sequence · Diagram Design</p>
|
||||||
|
<h1>Article request, cold cache</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 584" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<!-- Dot grid background -->
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<!-- Arrow markers -->
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#5ba8eb"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Background: paper + dot grid -->
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LIFELINES — dashed vertical lines from each actor, behind everything.
|
||||||
|
Actor lifeline x-coords: 128, 352, 584, 800
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="128" y1="128" x2="128" y2="488" stroke="rgba(241,239,231,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="352" y1="128" x2="352" y2="488" stroke="rgba(241,239,231,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="584" y1="128" x2="584" y2="488" stroke="rgba(241,239,231,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="800" y1="128" x2="800" y2="488" stroke="rgba(241,239,231,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTIVATION BARS — w=8 rects on lifelines showing control duration.
|
||||||
|
Drawn before message arrows so arrows land on their edges.
|
||||||
|
================================================================= -->
|
||||||
|
<!-- Cloudflare activation: receives at y=176, responds at y=408 -->
|
||||||
|
<rect x="348" y="180" width="8" height="232" fill="rgba(241,239,231,0.06)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<!-- Astro activation: called at y=232, returns at y=352 -->
|
||||||
|
<rect x="580" y="236" width="8" height="120" fill="rgba(241,239,231,0.06)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE ARROWS — time flows top→down.
|
||||||
|
Draw before labels so label masks cover the line.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1: Reader → Cloudflare (HTTPS request · link-blue) -->
|
||||||
|
<line x1="128" y1="176" x2="352" y2="176" stroke="#5ba8eb" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
|
||||||
|
<!-- M2: Cloudflare → Astro (cache miss · muted) -->
|
||||||
|
<line x1="352" y1="232" x2="580" y2="232" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M3: Astro self-message (render MDX · muted U-loop) -->
|
||||||
|
<path d="M 588 284 L 624 284 L 624 316 L 588 316" fill="none" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M4: Astro → Cloudflare (return HTML · muted dashed) -->
|
||||||
|
<line x1="580" y1="352" x2="356" y2="352" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M5: Cloudflare → Reader (primary success · coral) -->
|
||||||
|
<line x1="348" y1="408" x2="128" y2="408" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- M6: Reader → Analytics (async beacon · muted dashed) -->
|
||||||
|
<line x1="128" y1="464" x2="800" y2="464" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE LABELS — each with an opaque paper-colored mask.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1 label -->
|
||||||
|
<rect x="188" y="160" width="104" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="240" y="170" fill="#5ba8eb" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">GET /ARTICLES/SLUG</text>
|
||||||
|
|
||||||
|
<!-- M2 label -->
|
||||||
|
<rect x="416" y="216" width="100" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="466" y="226" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CACHE MISS · ORIGIN</text>
|
||||||
|
|
||||||
|
<!-- M3 label (to the right of the self-loop) -->
|
||||||
|
<rect x="632" y="292" width="72" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="668" y="302" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RENDER MDX</text>
|
||||||
|
|
||||||
|
<!-- M4 label -->
|
||||||
|
<rect x="420" y="336" width="96" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="468" y="346" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · HTML + MAX-AGE</text>
|
||||||
|
|
||||||
|
<!-- M5 label (coral · primary response) -->
|
||||||
|
<rect x="192" y="392" width="96" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="240" y="402" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · EDGE-CACHED</text>
|
||||||
|
|
||||||
|
<!-- M6 label (placed between Astro and Analytics lifelines, a clear gap) -->
|
||||||
|
<rect x="648" y="448" width="96" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="696" y="458" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PAGEVIEW BEACON</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTOR BOXES — drawn after arrows/labels.
|
||||||
|
Each actor: 144–160 wide × 56 tall. Centers: 128, 352, 584, 800.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- Actor 1: Reader (external / soft) -->
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="rgba(168,166,157,0.10)" stroke="#8e8c83" stroke-width="1"/>
|
||||||
|
<rect x="64" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(142,140,131,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="78" y="89" fill="#8e8c83" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="128" y="104" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="128" y="119" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Actor 2: Cloudflare (cloud / muted) -->
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="rgba(241,239,231,0.03)" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="288" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="304" y="89" fill="#8e8c83" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="352" y="104" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="352" y="119" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Actor 3: Astro Origin (focal / coral) -->
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<rect x="512" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(255,106,48,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="528" y="89" fill="#ff6a30" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="584" y="104" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="584" y="119" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Actor 4: Analytics (optional / dashed) -->
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="rgba(241,239,231,0.02)" stroke="rgba(241,239,231,0.22)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="736" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="750" y="89" fill="#8e8c83" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ASY</text>
|
||||||
|
<text x="800" y="104" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Analytics</text>
|
||||||
|
<text x="800" y="119" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Beacon · async</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LEGEND — horizontal strip at the bottom.
|
||||||
|
Separator at y=504, eyebrow at y=520, items at y=540–548.
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="56" y1="504" x2="944" y2="504" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="56" y="520" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<!-- Item 1: Actor swatch (coral focal) -->
|
||||||
|
<rect x="56" y="540" width="14" height="10" rx="2" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="76" y="548" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Focal actor</text>
|
||||||
|
|
||||||
|
<!-- Item 2: Activation bar swatch -->
|
||||||
|
<rect x="188" y="536" width="4" height="18" fill="rgba(241,239,231,0.06)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="200" y="548" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Activation</text>
|
||||||
|
|
||||||
|
<!-- Item 3: Request arrow (link-blue) -->
|
||||||
|
<line x1="308" y1="546" x2="336" y2="546" stroke="#5ba8eb" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="344" y="548" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<!-- Item 4: Return / async (muted dashed) -->
|
||||||
|
<line x1="476" y1="546" x2="504" y2="546" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<text x="512" y="548" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
|
||||||
|
|
||||||
|
<!-- Item 5: Primary response (coral) -->
|
||||||
|
<line x1="652" y1="546" x2="680" y2="546" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="688" y="548" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Primary response</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,386 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article Request · Sequence</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-paper-2: #efeee5;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-soft: #65655c;
|
||||||
|
--color-rule: rgba(11,13,11,0.12);
|
||||||
|
--color-rule-solid: rgba(135,139,134,0.25);
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--color-accent-tint: rgba(247,89,31,0.08);
|
||||||
|
--color-link: #1a70c7;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', 'Times New Roman', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
color: var(--color-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
|
||||||
|
.header-eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.1;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--color-muted);
|
||||||
|
max-width: 58ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagram-container {
|
||||||
|
background: var(--color-paper-2);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-rule);
|
||||||
|
padding: 1.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
|
||||||
|
.cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.1fr 1fr 0.9fr;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
.cards { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-rule);
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.5rem;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-bottom: 0.875rem;
|
||||||
|
padding-bottom: 0.875rem;
|
||||||
|
border-bottom: 1px solid rgba(11,13,11,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-dot {
|
||||||
|
width: 7px; height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.card-dot.ink { background: var(--color-ink); }
|
||||||
|
.card-dot.muted { background: var(--color-muted); }
|
||||||
|
.card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card-dot.link { background: var(--color-link); }
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-ink);
|
||||||
|
letter-spacing: -0.005em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card p {
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card ul {
|
||||||
|
list-style: none;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card li {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
padding-left: 0.875rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card li::before {
|
||||||
|
content: '—';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: rgba(11,13,11,0.25);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid rgba(11,13,11,0.10);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--color-soft);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Sequence · Diagram Design</p>
|
||||||
|
<h1>Article request, cold cache</h1>
|
||||||
|
<p class="subtitle">How littlemight.com serves a reader when the requested slug isn't already sitting in Cloudflare's edge cache — origin render, beacon, and back in one round trip.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sequence Diagram -->
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 584" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<!-- Dot grid background -->
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<!-- Arrow markers -->
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#52534e"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#f7591f"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Background: paper + dot grid -->
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LIFELINES — dashed vertical lines from each actor, behind everything.
|
||||||
|
Actor lifeline x-coords: 128, 352, 584, 800
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="128" y1="128" x2="128" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="352" y1="128" x2="352" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="584" y1="128" x2="584" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="800" y1="128" x2="800" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTIVATION BARS — w=8 rects on lifelines showing control duration.
|
||||||
|
Drawn before message arrows so arrows land on their edges.
|
||||||
|
================================================================= -->
|
||||||
|
<!-- Cloudflare activation: receives at y=176, responds at y=408 -->
|
||||||
|
<rect x="348" y="180" width="8" height="232" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<!-- Astro activation: called at y=232, returns at y=352 -->
|
||||||
|
<rect x="580" y="236" width="8" height="120" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE ARROWS — time flows top→down.
|
||||||
|
Draw before labels so label masks cover the line.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1: Reader → Cloudflare (HTTPS request · link-blue) -->
|
||||||
|
<line x1="128" y1="176" x2="352" y2="176" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
|
||||||
|
<!-- M2: Cloudflare → Astro (cache miss · muted) -->
|
||||||
|
<line x1="352" y1="232" x2="580" y2="232" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M3: Astro self-message (render MDX · muted U-loop) -->
|
||||||
|
<path d="M 588 284 L 624 284 L 624 316 L 588 316" fill="none" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M4: Astro → Cloudflare (return HTML · muted dashed) -->
|
||||||
|
<line x1="580" y1="352" x2="356" y2="352" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M5: Cloudflare → Reader (primary success · coral) -->
|
||||||
|
<line x1="348" y1="408" x2="128" y2="408" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- M6: Reader → Analytics (async beacon · muted dashed) -->
|
||||||
|
<line x1="128" y1="464" x2="800" y2="464" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE LABELS — each with an opaque paper-colored mask.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1 label -->
|
||||||
|
<rect x="188" y="160" width="104" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="240" y="170" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">GET /ARTICLES/SLUG</text>
|
||||||
|
|
||||||
|
<!-- M2 label -->
|
||||||
|
<rect x="416" y="216" width="100" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="466" y="226" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CACHE MISS · ORIGIN</text>
|
||||||
|
|
||||||
|
<!-- M3 label (to the right of the self-loop) -->
|
||||||
|
<rect x="632" y="292" width="72" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="668" y="302" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RENDER MDX</text>
|
||||||
|
|
||||||
|
<!-- M4 label -->
|
||||||
|
<rect x="420" y="336" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="468" y="346" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · HTML + MAX-AGE</text>
|
||||||
|
|
||||||
|
<!-- M5 label (coral · primary response) -->
|
||||||
|
<rect x="192" y="392" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="240" y="402" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · EDGE-CACHED</text>
|
||||||
|
|
||||||
|
<!-- M6 label (placed between Astro and Analytics lifelines, a clear gap) -->
|
||||||
|
<rect x="648" y="448" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="696" y="458" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PAGEVIEW BEACON</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTOR BOXES — drawn after arrows/labels.
|
||||||
|
Each actor: 144–160 wide × 56 tall. Centers: 128, 352, 584, 800.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- Actor 1: Reader (external / soft) -->
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<rect x="64" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(101,101,92,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="78" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="128" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="128" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Actor 2: Cloudflare (cloud / muted) -->
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="288" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="304" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="352" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="352" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Actor 3: Astro Origin (focal / coral) -->
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="512" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="528" y="89" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="584" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="584" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Actor 4: Analytics (optional / dashed) -->
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="rgba(11,13,11,0.02)" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="736" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="750" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ASY</text>
|
||||||
|
<text x="800" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Analytics</text>
|
||||||
|
<text x="800" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Beacon · async</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LEGEND — horizontal strip at the bottom.
|
||||||
|
Separator at y=504, eyebrow at y=520, items at y=540–548.
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="56" y1="504" x2="944" y2="504" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="56" y="520" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<!-- Item 1: Actor swatch (coral focal) -->
|
||||||
|
<rect x="56" y="540" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="76" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal actor</text>
|
||||||
|
|
||||||
|
<!-- Item 2: Activation bar swatch -->
|
||||||
|
<rect x="188" y="536" width="4" height="18" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="200" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Activation</text>
|
||||||
|
|
||||||
|
<!-- Item 3: Request arrow (link-blue) -->
|
||||||
|
<line x1="308" y1="546" x2="336" y2="546" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="344" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<!-- Item 4: Return / async (muted dashed) -->
|
||||||
|
<line x1="476" y1="546" x2="504" y2="546" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<text x="512" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
|
||||||
|
|
||||||
|
<!-- Item 5: Primary response (coral) -->
|
||||||
|
<line x1="652" y1="546" x2="680" y2="546" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="688" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary response</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Summary cards -->
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot coral"></span>
|
||||||
|
<h3>Edge handles the hot path</h3>
|
||||||
|
</div>
|
||||||
|
<p>On subsequent reads the whole exchange collapses to M1 → M5. Cloudflare serves the cached HTML without waking the origin. The coral arrow is the only one the reader ever perceives.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot ink"></span>
|
||||||
|
<h3>Origin render on miss</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Astro SSRs MDX on cold cache</li>
|
||||||
|
<li>Returns HTML + cache headers</li>
|
||||||
|
<li>Edge stores the result</li>
|
||||||
|
<li>Next reader skips the origin trip</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot muted"></span>
|
||||||
|
<h3>Analytics is fire-and-forget</h3>
|
||||||
|
</div>
|
||||||
|
<p>The pageview beacon is dashed for a reason: the reader never waits on it, and a failed beacon never breaks the page.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="footer">
|
||||||
|
<span>littlemight.com · article request sequence</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article request · Sequence</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Sequence · Diagram Design</p>
|
||||||
|
<h1>Article request, cold cache</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 584" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<!-- Dot grid background -->
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<!-- Arrow markers -->
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#52534e"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#f7591f"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Background: paper + dot grid -->
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LIFELINES — dashed vertical lines from each actor, behind everything.
|
||||||
|
Actor lifeline x-coords: 128, 352, 584, 800
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="128" y1="128" x2="128" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="352" y1="128" x2="352" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="584" y1="128" x2="584" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<line x1="800" y1="128" x2="800" y2="488" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTIVATION BARS — w=8 rects on lifelines showing control duration.
|
||||||
|
Drawn before message arrows so arrows land on their edges.
|
||||||
|
================================================================= -->
|
||||||
|
<!-- Cloudflare activation: receives at y=176, responds at y=408 -->
|
||||||
|
<rect x="348" y="180" width="8" height="232" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<!-- Astro activation: called at y=232, returns at y=352 -->
|
||||||
|
<rect x="580" y="236" width="8" height="120" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE ARROWS — time flows top→down.
|
||||||
|
Draw before labels so label masks cover the line.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1: Reader → Cloudflare (HTTPS request · link-blue) -->
|
||||||
|
<line x1="128" y1="176" x2="352" y2="176" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
|
||||||
|
<!-- M2: Cloudflare → Astro (cache miss · muted) -->
|
||||||
|
<line x1="352" y1="232" x2="580" y2="232" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M3: Astro self-message (render MDX · muted U-loop) -->
|
||||||
|
<path d="M 588 284 L 624 284 L 624 316 L 588 316" fill="none" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M4: Astro → Cloudflare (return HTML · muted dashed) -->
|
||||||
|
<line x1="580" y1="352" x2="356" y2="352" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- M5: Cloudflare → Reader (primary success · coral) -->
|
||||||
|
<line x1="348" y1="408" x2="128" y2="408" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
|
||||||
|
<!-- M6: Reader → Analytics (async beacon · muted dashed) -->
|
||||||
|
<line x1="128" y1="464" x2="800" y2="464" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
MESSAGE LABELS — each with an opaque paper-colored mask.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- M1 label -->
|
||||||
|
<rect x="188" y="160" width="104" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="240" y="170" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">GET /ARTICLES/SLUG</text>
|
||||||
|
|
||||||
|
<!-- M2 label -->
|
||||||
|
<rect x="416" y="216" width="100" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="466" y="226" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CACHE MISS · ORIGIN</text>
|
||||||
|
|
||||||
|
<!-- M3 label (to the right of the self-loop) -->
|
||||||
|
<rect x="632" y="292" width="72" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="668" y="302" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RENDER MDX</text>
|
||||||
|
|
||||||
|
<!-- M4 label -->
|
||||||
|
<rect x="420" y="336" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="468" y="346" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · HTML + MAX-AGE</text>
|
||||||
|
|
||||||
|
<!-- M5 label (coral · primary response) -->
|
||||||
|
<rect x="192" y="392" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="240" y="402" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">200 · EDGE-CACHED</text>
|
||||||
|
|
||||||
|
<!-- M6 label (placed between Astro and Analytics lifelines, a clear gap) -->
|
||||||
|
<rect x="648" y="448" width="96" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="696" y="458" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PAGEVIEW BEACON</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
ACTOR BOXES — drawn after arrows/labels.
|
||||||
|
Each actor: 144–160 wide × 56 tall. Centers: 128, 352, 584, 800.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- Actor 1: Reader (external / soft) -->
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="56" y="72" width="144" height="56" rx="6" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<rect x="64" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(101,101,92,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="78" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<text x="128" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Reader</text>
|
||||||
|
<text x="128" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser</text>
|
||||||
|
|
||||||
|
<!-- Actor 2: Cloudflare (cloud / muted) -->
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="280" y="72" width="144" height="56" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="288" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="304" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
|
||||||
|
<text x="352" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Cloudflare</text>
|
||||||
|
<text x="352" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Pages · cache</text>
|
||||||
|
|
||||||
|
<!-- Actor 3: Astro Origin (focal / coral) -->
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="504" y="72" width="160" height="56" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="512" y="80" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="528" y="89" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORIG</text>
|
||||||
|
<text x="584" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Astro Origin</text>
|
||||||
|
<text x="584" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">SSR + MDX</text>
|
||||||
|
|
||||||
|
<!-- Actor 4: Analytics (optional / dashed) -->
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="728" y="72" width="144" height="56" rx="6" fill="rgba(11,13,11,0.02)" stroke="rgba(11,13,11,0.22)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<rect x="736" y="80" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="750" y="89" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ASY</text>
|
||||||
|
<text x="800" y="104" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Analytics</text>
|
||||||
|
<text x="800" y="119" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Beacon · async</text>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LEGEND — horizontal strip at the bottom.
|
||||||
|
Separator at y=504, eyebrow at y=520, items at y=540–548.
|
||||||
|
================================================================= -->
|
||||||
|
<line x1="56" y1="504" x2="944" y2="504" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="56" y="520" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<!-- Item 1: Actor swatch (coral focal) -->
|
||||||
|
<rect x="56" y="540" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="76" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal actor</text>
|
||||||
|
|
||||||
|
<!-- Item 2: Activation bar swatch -->
|
||||||
|
<rect x="188" y="536" width="4" height="18" fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="200" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Activation</text>
|
||||||
|
|
||||||
|
<!-- Item 3: Request arrow (link-blue) -->
|
||||||
|
<line x1="308" y1="546" x2="336" y2="546" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="344" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">HTTP request</text>
|
||||||
|
|
||||||
|
<!-- Item 4: Return / async (muted dashed) -->
|
||||||
|
<line x1="476" y1="546" x2="504" y2="546" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<text x="512" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
|
||||||
|
|
||||||
|
<!-- Item 5: Primary response (coral) -->
|
||||||
|
<line x1="652" y1="546" x2="680" y2="546" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="688" y="548" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Primary response</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article lifecycle · State machine</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">State machine · Diagram Design</p>
|
||||||
|
<h1>Article lifecycle</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 460" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Transitions (drawn first) -->
|
||||||
|
<!-- Start → Draft -->
|
||||||
|
<line x1="68" y1="200" x2="120" y2="200" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Draft → In Review -->
|
||||||
|
<line x1="280" y1="200" x2="340" y2="200" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Published (coral — happy path) -->
|
||||||
|
<line x1="500" y1="200" x2="560" y2="200" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- Published → Archived (down) -->
|
||||||
|
<line x1="640" y1="240" x2="640" y2="300" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Archived → End (down) -->
|
||||||
|
<line x1="640" y1="400" x2="640" y2="432" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Draft (curved back up-and-left) -->
|
||||||
|
<path d="M 420 160 C 420 96, 200 96, 200 160" fill="none" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Transition labels -->
|
||||||
|
<rect x="172" y="184" width="48" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="196" y="194" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CREATE</text>
|
||||||
|
|
||||||
|
<rect x="288" y="184" width="48" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="312" y="194" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SUBMIT</text>
|
||||||
|
|
||||||
|
<rect x="500" y="184" width="60" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="530" y="194" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">APPROVE</text>
|
||||||
|
|
||||||
|
<rect x="612" y="264" width="56" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="640" y="274" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXPIRE</text>
|
||||||
|
|
||||||
|
<rect x="620" y="416" width="40" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="640" y="426" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PURGE</text>
|
||||||
|
|
||||||
|
<!-- Reject label on curved path -->
|
||||||
|
<rect x="276" y="92" width="80" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="316" y="102" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">REJECT · REVISE</text>
|
||||||
|
|
||||||
|
<!-- Start dot (filled ink) -->
|
||||||
|
<circle cx="60" cy="200" r="6" fill="#f1efe7"/>
|
||||||
|
|
||||||
|
<!-- State: Draft -->
|
||||||
|
<rect x="120" y="160" width="160" height="80" rx="8" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="128" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="148" y="177" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="200" y="208" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft</text>
|
||||||
|
<text x="200" y="224" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">unpublished</text>
|
||||||
|
|
||||||
|
<!-- State: In Review -->
|
||||||
|
<rect x="340" y="160" width="160" height="80" rx="8" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="348" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="368" y="177" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="420" y="208" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">In Review</text>
|
||||||
|
<text x="420" y="224" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">awaiting approval</text>
|
||||||
|
|
||||||
|
<!-- State: Published (focal coral) -->
|
||||||
|
<rect x="560" y="160" width="160" height="80" rx="8" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<rect x="568" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(255,106,48,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="177" fill="#ff6a30" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="208" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Published</text>
|
||||||
|
<text x="640" y="224" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">live on site</text>
|
||||||
|
|
||||||
|
<!-- State: Archived -->
|
||||||
|
<rect x="560" y="300" width="160" height="100" rx="8" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<rect x="568" y="308" width="40" height="12" rx="2" fill="transparent" stroke="rgba(168,166,157,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="317" fill="#a8a69d" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="348" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Archived</text>
|
||||||
|
<text x="640" y="364" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">noindex · hidden</text>
|
||||||
|
<text x="640" y="382" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">redirect retained</text>
|
||||||
|
|
||||||
|
<!-- End ring dot -->
|
||||||
|
<circle cx="640" cy="440" r="8" fill="none" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<circle cx="640" cy="440" r="5" fill="#f1efe7"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article lifecycle · state machine</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">State machine · Diagram Design</p>
|
||||||
|
<h1>Article lifecycle</h1>
|
||||||
|
<p class="subtitle">The four states a post passes through from draft to archive, with the rejection loop made explicit. Published is the state the team optimizes for — hence coral.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 460" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Transitions (drawn first) -->
|
||||||
|
<!-- Start → Draft -->
|
||||||
|
<line x1="68" y1="200" x2="120" y2="200" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Draft → In Review -->
|
||||||
|
<line x1="280" y1="200" x2="340" y2="200" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Published (coral — happy path) -->
|
||||||
|
<line x1="500" y1="200" x2="560" y2="200" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- Published → Archived (down) -->
|
||||||
|
<line x1="640" y1="240" x2="640" y2="300" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Archived → End (down) -->
|
||||||
|
<line x1="640" y1="400" x2="640" y2="432" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Draft (curved back up-and-left) -->
|
||||||
|
<path d="M 420 160 C 420 96, 200 96, 200 160" fill="none" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Transition labels -->
|
||||||
|
<rect x="172" y="184" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="196" y="194" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CREATE</text>
|
||||||
|
|
||||||
|
<rect x="288" y="184" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="312" y="194" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SUBMIT</text>
|
||||||
|
|
||||||
|
<rect x="500" y="184" width="60" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="530" y="194" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">APPROVE</text>
|
||||||
|
|
||||||
|
<rect x="612" y="264" width="56" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="640" y="274" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXPIRE</text>
|
||||||
|
|
||||||
|
<rect x="620" y="416" width="40" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="640" y="426" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PURGE</text>
|
||||||
|
|
||||||
|
<!-- Reject label on curved path -->
|
||||||
|
<rect x="276" y="92" width="80" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="316" y="102" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">REJECT · REVISE</text>
|
||||||
|
|
||||||
|
<!-- Start dot (filled ink) -->
|
||||||
|
<circle cx="60" cy="200" r="6" fill="#0b0d0b"/>
|
||||||
|
|
||||||
|
<!-- State: Draft -->
|
||||||
|
<rect x="120" y="160" width="160" height="80" rx="8" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="128" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="148" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="200" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft</text>
|
||||||
|
<text x="200" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">unpublished</text>
|
||||||
|
|
||||||
|
<!-- State: In Review -->
|
||||||
|
<rect x="340" y="160" width="160" height="80" rx="8" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="348" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="368" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="420" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">In Review</text>
|
||||||
|
<text x="420" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">awaiting approval</text>
|
||||||
|
|
||||||
|
<!-- State: Published (focal coral) -->
|
||||||
|
<rect x="560" y="160" width="160" height="80" rx="8" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="568" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="177" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Published</text>
|
||||||
|
<text x="640" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">live on site</text>
|
||||||
|
|
||||||
|
<!-- State: Archived -->
|
||||||
|
<rect x="560" y="300" width="160" height="100" rx="8" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<rect x="568" y="308" width="40" height="12" rx="2" fill="transparent" stroke="rgba(82,83,78,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="317" fill="#52534e" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="348" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Archived</text>
|
||||||
|
<text x="640" y="364" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">noindex · hidden</text>
|
||||||
|
<text x="640" y="382" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">redirect retained</text>
|
||||||
|
|
||||||
|
<!-- End ring dot -->
|
||||||
|
<circle cx="640" cy="440" r="8" fill="none" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<circle cx="640" cy="440" r="5" fill="#0b0d0b"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>Published is the win state</h3></div>
|
||||||
|
<p>Everything flows toward Published. The rejection loop is dashed because it's a detour, not a failure — the article is still in-progress.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Reject is a loop, not a dead-end</h3></div>
|
||||||
|
<ul><li>Dashed to signal detour</li><li>Returns to Draft, not a new state</li><li>Review feedback is the action</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Archive keeps the URL</h3></div>
|
||||||
|
<p>Archived preserves the redirect map so inbound links survive. Only Purge removes the record entirely.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>article lifecycle · state machine</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Article lifecycle · State machine</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">State machine · Diagram Design</p>
|
||||||
|
<h1>Article lifecycle</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 460" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Transitions (drawn first) -->
|
||||||
|
<!-- Start → Draft -->
|
||||||
|
<line x1="68" y1="200" x2="120" y2="200" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Draft → In Review -->
|
||||||
|
<line x1="280" y1="200" x2="340" y2="200" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Published (coral — happy path) -->
|
||||||
|
<line x1="500" y1="200" x2="560" y2="200" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- Published → Archived (down) -->
|
||||||
|
<line x1="640" y1="240" x2="640" y2="300" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- Archived → End (down) -->
|
||||||
|
<line x1="640" y1="400" x2="640" y2="432" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- In Review → Draft (curved back up-and-left) -->
|
||||||
|
<path d="M 420 160 C 420 96, 200 96, 200 160" fill="none" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Transition labels -->
|
||||||
|
<rect x="172" y="184" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="196" y="194" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CREATE</text>
|
||||||
|
|
||||||
|
<rect x="288" y="184" width="48" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="312" y="194" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SUBMIT</text>
|
||||||
|
|
||||||
|
<rect x="500" y="184" width="60" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="530" y="194" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">APPROVE</text>
|
||||||
|
|
||||||
|
<rect x="612" y="264" width="56" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="640" y="274" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXPIRE</text>
|
||||||
|
|
||||||
|
<rect x="620" y="416" width="40" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="640" y="426" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PURGE</text>
|
||||||
|
|
||||||
|
<!-- Reject label on curved path -->
|
||||||
|
<rect x="276" y="92" width="80" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="316" y="102" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">REJECT · REVISE</text>
|
||||||
|
|
||||||
|
<!-- Start dot (filled ink) -->
|
||||||
|
<circle cx="60" cy="200" r="6" fill="#0b0d0b"/>
|
||||||
|
|
||||||
|
<!-- State: Draft -->
|
||||||
|
<rect x="120" y="160" width="160" height="80" rx="8" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="128" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="148" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="200" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft</text>
|
||||||
|
<text x="200" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">unpublished</text>
|
||||||
|
|
||||||
|
<!-- State: In Review -->
|
||||||
|
<rect x="340" y="160" width="160" height="80" rx="8" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="348" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="368" y="177" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="420" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">In Review</text>
|
||||||
|
<text x="420" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">awaiting approval</text>
|
||||||
|
|
||||||
|
<!-- State: Published (focal coral) -->
|
||||||
|
<rect x="560" y="160" width="160" height="80" rx="8" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="568" y="168" width="40" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="177" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="208" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Published</text>
|
||||||
|
<text x="640" y="224" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">live on site</text>
|
||||||
|
|
||||||
|
<!-- State: Archived -->
|
||||||
|
<rect x="560" y="300" width="160" height="100" rx="8" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<rect x="568" y="308" width="40" height="12" rx="2" fill="transparent" stroke="rgba(82,83,78,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="588" y="317" fill="#52534e" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STATE</text>
|
||||||
|
<text x="640" y="348" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Archived</text>
|
||||||
|
<text x="640" y="364" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">noindex · hidden</text>
|
||||||
|
<text x="640" y="382" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">redirect retained</text>
|
||||||
|
|
||||||
|
<!-- End ring dot -->
|
||||||
|
<circle cx="640" cy="440" r="8" fill="none" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<circle cx="640" cy="440" r="5" fill="#0b0d0b"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Publishing an article · Swimlane</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Swimlane · Diagram Design</p>
|
||||||
|
<h1>Publishing an article</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Lane dividers -->
|
||||||
|
<line x1="40" y1="80" x2="960" y2="80" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="160" x2="960" y2="160" stroke="rgba(241,239,231,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="240" x2="960" y2="240" stroke="rgba(241,239,231,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="320" x2="960" y2="320" stroke="rgba(241,239,231,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="400" x2="960" y2="400" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Actor column divider -->
|
||||||
|
<line x1="160" y1="80" x2="160" y2="400" stroke="rgba(241,239,231,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Lane labels -->
|
||||||
|
<text x="60" y="124" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">AUTHOR</text>
|
||||||
|
<text x="60" y="204" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">REVIEWER</text>
|
||||||
|
<text x="60" y="284" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">EDITOR</text>
|
||||||
|
<text x="60" y="364" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">CI / CD</text>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first) -->
|
||||||
|
<!-- 1. Draft → Open PR (within Author) -->
|
||||||
|
<line x1="300" y1="120" x2="340" y2="120" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 2. Open PR → Review (Author → Reviewer handoff, diagonal down-right) -->
|
||||||
|
<line x1="460" y1="144" x2="500" y2="176" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 3. Review → Polish (Reviewer → Editor, vertical down) -->
|
||||||
|
<line x1="570" y1="224" x2="570" y2="256" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 4. Polish → Approve (within Editor) -->
|
||||||
|
<line x1="640" y1="280" x2="680" y2="280" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 5. Approve → Build (Editor → CI/CD, coral handoff) -->
|
||||||
|
<line x1="750" y1="304" x2="720" y2="336" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- 6. Build → Deploy (within CI/CD) -->
|
||||||
|
<line x1="760" y1="360" x2="800" y2="360" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="452" y="140" width="60" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="482" y="150" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">HANDOFF</text>
|
||||||
|
|
||||||
|
<rect x="548" y="228" width="52" height="12" rx="2" fill="#2a2723"/>
|
||||||
|
<text x="574" y="238" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">REVISE</text>
|
||||||
|
|
||||||
|
<rect x="712" y="308" width="80" height="12" rx="2" fill="#1c1a17"/>
|
||||||
|
<text x="752" y="318" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">DEPLOY TRIGGER</text>
|
||||||
|
|
||||||
|
<!-- Steps -->
|
||||||
|
<!-- Author: Draft MDX -->
|
||||||
|
<rect x="180" y="96" width="120" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="240" y="118" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft MDX</text>
|
||||||
|
<text x="240" y="132" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/…</text>
|
||||||
|
|
||||||
|
<!-- Author: Open PR -->
|
||||||
|
<rect x="340" y="96" width="120" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="400" y="118" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Open PR</text>
|
||||||
|
<text x="400" y="132" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">gh pr create</text>
|
||||||
|
|
||||||
|
<!-- Reviewer: Review content -->
|
||||||
|
<rect x="500" y="176" width="140" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="570" y="198" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Review content</text>
|
||||||
|
<text x="570" y="212" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">fact-check · voice</text>
|
||||||
|
|
||||||
|
<!-- Editor: Polish copy -->
|
||||||
|
<rect x="500" y="256" width="140" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="570" y="278" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Polish copy</text>
|
||||||
|
<text x="570" y="292" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">style · line edits</text>
|
||||||
|
|
||||||
|
<!-- Editor: Approve merge -->
|
||||||
|
<rect x="680" y="256" width="140" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="750" y="278" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Approve merge</text>
|
||||||
|
<text x="750" y="292" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">squash · main</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Build -->
|
||||||
|
<rect x="680" y="336" width="80" height="48" rx="6" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="720" y="358" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Build</text>
|
||||||
|
<text x="720" y="372" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">astro build</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Deploy (coral focal) -->
|
||||||
|
<rect x="800" y="336" width="120" height="48" rx="6" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="860" y="358" fill="#f1efe7" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Deploy</text>
|
||||||
|
<text x="860" y="372" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">cloudflare pages</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="428" x2="960" y2="428" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="444" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="460" width="14" height="10" rx="2" fill="#2a2723" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="60" y="468" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Step</text>
|
||||||
|
|
||||||
|
<rect x="132" y="460" width="14" height="10" rx="2" fill="rgba(255,106,48,0.08)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="152" y="468" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Focal outcome</text>
|
||||||
|
|
||||||
|
<line x1="268" y1="466" x2="296" y2="466" stroke="#a8a69d" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="304" y="468" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Within-lane step</text>
|
||||||
|
|
||||||
|
<line x1="444" y1="466" x2="472" y2="466" stroke="#a8a69d" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
|
||||||
|
<text x="480" y="468" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Revision loop</text>
|
||||||
|
|
||||||
|
<line x1="608" y1="466" x2="636" y2="466" stroke="#ff6a30" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="644" y="468" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Critical handoff</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Publishing an article · Swimlane</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Swimlane · Diagram Design</p>
|
||||||
|
<h1>Publishing an article</h1>
|
||||||
|
<p class="subtitle">Four actors, seven steps, three handoffs. The step each person owns lives in their lane — arrows crossing lanes are where coordination happens.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Lane dividers -->
|
||||||
|
<line x1="40" y1="80" x2="960" y2="80" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="160" x2="960" y2="160" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="240" x2="960" y2="240" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="320" x2="960" y2="320" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="400" x2="960" y2="400" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Actor column divider -->
|
||||||
|
<line x1="160" y1="80" x2="160" y2="400" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Lane labels -->
|
||||||
|
<text x="60" y="124" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">AUTHOR</text>
|
||||||
|
<text x="60" y="204" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">REVIEWER</text>
|
||||||
|
<text x="60" y="284" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">EDITOR</text>
|
||||||
|
<text x="60" y="364" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">CI / CD</text>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first) -->
|
||||||
|
<!-- 1. Draft → Open PR (within Author) -->
|
||||||
|
<line x1="300" y1="120" x2="340" y2="120" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 2. Open PR → Review (Author → Reviewer handoff, diagonal down-right) -->
|
||||||
|
<line x1="460" y1="144" x2="500" y2="176" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 3. Review → Polish (Reviewer → Editor, vertical down) -->
|
||||||
|
<line x1="570" y1="224" x2="570" y2="256" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 4. Polish → Approve (within Editor) -->
|
||||||
|
<line x1="640" y1="280" x2="680" y2="280" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 5. Approve → Build (Editor → CI/CD, coral handoff) -->
|
||||||
|
<line x1="750" y1="304" x2="720" y2="336" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- 6. Build → Deploy (within CI/CD) -->
|
||||||
|
<line x1="760" y1="360" x2="800" y2="360" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="452" y="140" width="60" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="482" y="150" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">HANDOFF</text>
|
||||||
|
|
||||||
|
<rect x="548" y="228" width="52" height="12" rx="2" fill="#efeee5"/>
|
||||||
|
<text x="574" y="238" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">REVISE</text>
|
||||||
|
|
||||||
|
<rect x="712" y="308" width="80" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="752" y="318" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">DEPLOY TRIGGER</text>
|
||||||
|
|
||||||
|
<!-- Steps -->
|
||||||
|
<!-- Author: Draft MDX -->
|
||||||
|
<rect x="180" y="96" width="120" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="240" y="118" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft MDX</text>
|
||||||
|
<text x="240" y="132" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/…</text>
|
||||||
|
|
||||||
|
<!-- Author: Open PR -->
|
||||||
|
<rect x="340" y="96" width="120" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="400" y="118" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Open PR</text>
|
||||||
|
<text x="400" y="132" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">gh pr create</text>
|
||||||
|
|
||||||
|
<!-- Reviewer: Review content -->
|
||||||
|
<rect x="500" y="176" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="570" y="198" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Review content</text>
|
||||||
|
<text x="570" y="212" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">fact-check · voice</text>
|
||||||
|
|
||||||
|
<!-- Editor: Polish copy -->
|
||||||
|
<rect x="500" y="256" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="570" y="278" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Polish copy</text>
|
||||||
|
<text x="570" y="292" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">style · line edits</text>
|
||||||
|
|
||||||
|
<!-- Editor: Approve merge -->
|
||||||
|
<rect x="680" y="256" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="750" y="278" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Approve merge</text>
|
||||||
|
<text x="750" y="292" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">squash · main</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Build -->
|
||||||
|
<rect x="680" y="336" width="80" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="720" y="358" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Build</text>
|
||||||
|
<text x="720" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">astro build</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Deploy (coral focal) -->
|
||||||
|
<rect x="800" y="336" width="120" height="48" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="860" y="358" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Deploy</text>
|
||||||
|
<text x="860" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">cloudflare pages</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="428" x2="960" y2="428" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="444" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="460" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="60" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Step</text>
|
||||||
|
|
||||||
|
<rect x="132" y="460" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="152" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal outcome</text>
|
||||||
|
|
||||||
|
<line x1="268" y1="466" x2="296" y2="466" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="304" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Within-lane step</text>
|
||||||
|
|
||||||
|
<line x1="444" y1="466" x2="472" y2="466" stroke="#52534e" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
|
||||||
|
<text x="480" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Revision loop</text>
|
||||||
|
|
||||||
|
<line x1="608" y1="466" x2="636" y2="466" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="644" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Critical handoff</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>The deploy trigger is the risky edge</h3></div>
|
||||||
|
<p>Every other step lives inside one person's head. The Editor→CI/CD handoff is where automation takes over and humans lose the ability to undo — hence coral.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>One owner per step</h3></div>
|
||||||
|
<ul><li>No step crosses two lanes</li><li>Handoffs are arrows, not shared steps</li><li>Dashed arrow = revision detour</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Uneven step counts are fine</h3></div>
|
||||||
|
<p>The Reviewer lane has one step; CI/CD has two. Lanes don't need to match — they exist to make ownership unambiguous.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>publishing an article · swimlane</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Publishing an article · Swimlane</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Swimlane · Diagram Design</p>
|
||||||
|
<h1>Publishing an article</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Lane dividers -->
|
||||||
|
<line x1="40" y1="80" x2="960" y2="80" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="160" x2="960" y2="160" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="240" x2="960" y2="240" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="320" x2="960" y2="320" stroke="rgba(11,13,11,0.10)" stroke-width="1"/>
|
||||||
|
<line x1="40" y1="400" x2="960" y2="400" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Actor column divider -->
|
||||||
|
<line x1="160" y1="80" x2="160" y2="400" stroke="rgba(11,13,11,0.22)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Lane labels -->
|
||||||
|
<text x="60" y="124" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">AUTHOR</text>
|
||||||
|
<text x="60" y="204" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">REVIEWER</text>
|
||||||
|
<text x="60" y="284" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">EDITOR</text>
|
||||||
|
<text x="60" y="364" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.18em">CI / CD</text>
|
||||||
|
|
||||||
|
<!-- Arrows (drawn first) -->
|
||||||
|
<!-- 1. Draft → Open PR (within Author) -->
|
||||||
|
<line x1="300" y1="120" x2="340" y2="120" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 2. Open PR → Review (Author → Reviewer handoff, diagonal down-right) -->
|
||||||
|
<line x1="460" y1="144" x2="500" y2="176" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 3. Review → Polish (Reviewer → Editor, vertical down) -->
|
||||||
|
<line x1="570" y1="224" x2="570" y2="256" stroke="#52534e" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 4. Polish → Approve (within Editor) -->
|
||||||
|
<line x1="640" y1="280" x2="680" y2="280" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<!-- 5. Approve → Build (Editor → CI/CD, coral handoff) -->
|
||||||
|
<line x1="750" y1="304" x2="720" y2="336" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<!-- 6. Build → Deploy (within CI/CD) -->
|
||||||
|
<line x1="760" y1="360" x2="800" y2="360" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Arrow labels -->
|
||||||
|
<rect x="452" y="140" width="60" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="482" y="150" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">HANDOFF</text>
|
||||||
|
|
||||||
|
<rect x="548" y="228" width="52" height="12" rx="2" fill="#efeee5"/>
|
||||||
|
<text x="574" y="238" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">REVISE</text>
|
||||||
|
|
||||||
|
<rect x="712" y="308" width="80" height="12" rx="2" fill="#f5f4ed"/>
|
||||||
|
<text x="752" y="318" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.12em">DEPLOY TRIGGER</text>
|
||||||
|
|
||||||
|
<!-- Steps -->
|
||||||
|
<!-- Author: Draft MDX -->
|
||||||
|
<rect x="180" y="96" width="120" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="240" y="118" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Draft MDX</text>
|
||||||
|
<text x="240" y="132" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">src/content/…</text>
|
||||||
|
|
||||||
|
<!-- Author: Open PR -->
|
||||||
|
<rect x="340" y="96" width="120" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="400" y="118" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Open PR</text>
|
||||||
|
<text x="400" y="132" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">gh pr create</text>
|
||||||
|
|
||||||
|
<!-- Reviewer: Review content -->
|
||||||
|
<rect x="500" y="176" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="570" y="198" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Review content</text>
|
||||||
|
<text x="570" y="212" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">fact-check · voice</text>
|
||||||
|
|
||||||
|
<!-- Editor: Polish copy -->
|
||||||
|
<rect x="500" y="256" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="570" y="278" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Polish copy</text>
|
||||||
|
<text x="570" y="292" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">style · line edits</text>
|
||||||
|
|
||||||
|
<!-- Editor: Approve merge -->
|
||||||
|
<rect x="680" y="256" width="140" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="750" y="278" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Approve merge</text>
|
||||||
|
<text x="750" y="292" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">squash · main</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Build -->
|
||||||
|
<rect x="680" y="336" width="80" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="720" y="358" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Build</text>
|
||||||
|
<text x="720" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">astro build</text>
|
||||||
|
|
||||||
|
<!-- CI/CD: Deploy (coral focal) -->
|
||||||
|
<rect x="800" y="336" width="120" height="48" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="860" y="358" fill="#0b0d0b" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Deploy</text>
|
||||||
|
<text x="860" y="372" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">cloudflare pages</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="428" x2="960" y2="428" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="444" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="460" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="60" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Step</text>
|
||||||
|
|
||||||
|
<rect x="132" y="460" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="152" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Focal outcome</text>
|
||||||
|
|
||||||
|
<line x1="268" y1="466" x2="296" y2="466" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="304" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Within-lane step</text>
|
||||||
|
|
||||||
|
<line x1="444" y1="466" x2="472" y2="466" stroke="#52534e" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
|
||||||
|
<text x="480" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Revision loop</text>
|
||||||
|
|
||||||
|
<line x1="608" y1="466" x2="636" y2="466" stroke="#f7591f" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="644" y="468" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Critical handoff</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight milestones · Timeline</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Timeline · Diagram Design</p>
|
||||||
|
<h1>littlemight, fourteen months</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 420" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Year markers (background) -->
|
||||||
|
<text x="60" y="340" fill="rgba(241,239,231,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2025</text>
|
||||||
|
<text x="700" y="340" fill="rgba(241,239,231,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2026</text>
|
||||||
|
|
||||||
|
<!-- Baseline -->
|
||||||
|
<line x1="80" y1="240" x2="920" y2="240" stroke="rgba(150,146,138,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Year boundary tick (between 2025 and 2026) -->
|
||||||
|
<line x1="680" y1="232" x2="680" y2="248" stroke="rgba(241,239,231,0.20)" stroke-width="1"/>
|
||||||
|
<text x="680" y="260" fill="#8e8c83" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN '26</text>
|
||||||
|
|
||||||
|
<!-- Start / end caps -->
|
||||||
|
<line x1="80" y1="232" x2="80" y2="248" stroke="rgba(241,239,231,0.20)" stroke-width="1"/>
|
||||||
|
<line x1="920" y1="232" x2="920" y2="248" stroke="rgba(241,239,231,0.20)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Event 1: FEB 2025 · First post (below) -->
|
||||||
|
<line x1="100" y1="240" x2="100" y2="296" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="100" cy="240" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="100" y="312" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">FEB 2025</text>
|
||||||
|
<text x="100" y="328" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">First post</text>
|
||||||
|
|
||||||
|
<!-- Event 2: APR 2025 · Design v1 (above) -->
|
||||||
|
<line x1="240" y1="184" x2="240" y2="240" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="240" cy="240" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="240" y="156" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2025</text>
|
||||||
|
<text x="240" y="172" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v1</text>
|
||||||
|
|
||||||
|
<!-- Event 3: SEP 2025 · Design v2 (below) -->
|
||||||
|
<line x1="500" y1="240" x2="500" y2="296" stroke="rgba(241,239,231,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="500" cy="240" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="500" y="312" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">SEP 2025</text>
|
||||||
|
<text x="500" y="328" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v2</text>
|
||||||
|
<text x="500" y="344" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">typography pass</text>
|
||||||
|
|
||||||
|
<!-- Event 4: JAN 2026 · Design v3 (above, coral major) -->
|
||||||
|
<line x1="740" y1="160" x2="740" y2="240" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<circle cx="740" cy="240" r="6" fill="#ff6a30"/>
|
||||||
|
<text x="740" y="128" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN 2026</text>
|
||||||
|
<text x="740" y="148" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v3</text>
|
||||||
|
<text x="740" y="164" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">complexity budget</text>
|
||||||
|
|
||||||
|
<!-- Event 5: APR 2026 · now · Diagram Design skill (below, coral) -->
|
||||||
|
<line x1="900" y1="240" x2="900" y2="296" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<circle cx="900" cy="240" r="6" fill="#ff6a30"/>
|
||||||
|
<text x="900" y="312" fill="#ff6a30" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2026 · NOW</text>
|
||||||
|
<text x="900" y="328" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">diagram-design</text>
|
||||||
|
<text x="900" y="344" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">eight diagram types</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="376" x2="960" y2="376" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="392" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="408" r="4" fill="#f1efe7"/>
|
||||||
|
<text x="68" y="412" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Event</text>
|
||||||
|
|
||||||
|
<circle cx="148" cy="408" r="6" fill="#ff6a30"/>
|
||||||
|
<text x="164" y="412" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Major milestone</text>
|
||||||
|
|
||||||
|
<text x="296" y="412" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Spacing is proportional to real elapsed time.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight milestones · Timeline</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Timeline · Diagram Design</p>
|
||||||
|
<h1>littlemight, fourteen months</h1>
|
||||||
|
<p class="subtitle">Five design moments from the first post to the current diagram-design overhaul. Spacing is proportional to calendar time — the five-month gap between v1 and v2 is genuinely wider on the page.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 420" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Year markers (background) -->
|
||||||
|
<text x="60" y="340" fill="rgba(11,13,11,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2025</text>
|
||||||
|
<text x="700" y="340" fill="rgba(11,13,11,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2026</text>
|
||||||
|
|
||||||
|
<!-- Baseline -->
|
||||||
|
<line x1="80" y1="240" x2="920" y2="240" stroke="rgba(135,139,134,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Year boundary tick (between 2025 and 2026) -->
|
||||||
|
<line x1="680" y1="232" x2="680" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
<text x="680" y="260" fill="#65655c" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN '26</text>
|
||||||
|
|
||||||
|
<!-- Start / end caps -->
|
||||||
|
<line x1="80" y1="232" x2="80" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
<line x1="920" y1="232" x2="920" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Event 1: FEB 2025 · First post (below) -->
|
||||||
|
<line x1="100" y1="240" x2="100" y2="296" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="100" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="100" y="312" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">FEB 2025</text>
|
||||||
|
<text x="100" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">First post</text>
|
||||||
|
|
||||||
|
<!-- Event 2: APR 2025 · Design v1 (above) -->
|
||||||
|
<line x1="240" y1="184" x2="240" y2="240" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="240" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="240" y="156" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2025</text>
|
||||||
|
<text x="240" y="172" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v1</text>
|
||||||
|
|
||||||
|
<!-- Event 3: SEP 2025 · Design v2 (below) -->
|
||||||
|
<line x1="500" y1="240" x2="500" y2="296" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="500" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="500" y="312" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">SEP 2025</text>
|
||||||
|
<text x="500" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v2</text>
|
||||||
|
<text x="500" y="344" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">typography pass</text>
|
||||||
|
|
||||||
|
<!-- Event 4: JAN 2026 · Design v3 (above, coral major) -->
|
||||||
|
<line x1="740" y1="160" x2="740" y2="240" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<circle cx="740" cy="240" r="6" fill="#f7591f"/>
|
||||||
|
<text x="740" y="128" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN 2026</text>
|
||||||
|
<text x="740" y="148" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v3</text>
|
||||||
|
<text x="740" y="164" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">complexity budget</text>
|
||||||
|
|
||||||
|
<!-- Event 5: APR 2026 · now · Diagram Design skill (below, coral) -->
|
||||||
|
<line x1="900" y1="240" x2="900" y2="296" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<circle cx="900" cy="240" r="6" fill="#f7591f"/>
|
||||||
|
<text x="900" y="312" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2026 · NOW</text>
|
||||||
|
<text x="900" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">diagram-design</text>
|
||||||
|
<text x="900" y="344" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">eight diagram types</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="376" x2="960" y2="376" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="392" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="408" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="68" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Event</text>
|
||||||
|
|
||||||
|
<circle cx="148" cy="408" r="6" fill="#f7591f"/>
|
||||||
|
<text x="164" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Major milestone</text>
|
||||||
|
|
||||||
|
<text x="296" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Spacing is proportional to real elapsed time.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>v3 set the complexity budget</h3></div>
|
||||||
|
<p>January's design overhaul is the pivot — the rule that no diagram exceeds nine components comes from there, and it's why this skill exists in the form it does.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Honest spacing</h3></div>
|
||||||
|
<ul><li>5 months between v1 and v2</li><li>4 months between v2 and v3</li><li>3 months between v3 and now</li><li>Cadence is tightening, not gaming the axis</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Alternate label placement</h3></div>
|
||||||
|
<p>Above / below flipping prevents label collision without forcing a second row. Five events on one baseline stays legible.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>littlemight · design lineage</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>littlemight milestones · Timeline</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Timeline · Diagram Design</p>
|
||||||
|
<h1>littlemight, fourteen months</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 420" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Year markers (background) -->
|
||||||
|
<text x="60" y="340" fill="rgba(11,13,11,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2025</text>
|
||||||
|
<text x="700" y="340" fill="rgba(11,13,11,0.06)" font-size="72" font-weight="600" font-family="'Geist Mono', monospace">2026</text>
|
||||||
|
|
||||||
|
<!-- Baseline -->
|
||||||
|
<line x1="80" y1="240" x2="920" y2="240" stroke="rgba(135,139,134,0.45)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Year boundary tick (between 2025 and 2026) -->
|
||||||
|
<line x1="680" y1="232" x2="680" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
<text x="680" y="260" fill="#65655c" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN '26</text>
|
||||||
|
|
||||||
|
<!-- Start / end caps -->
|
||||||
|
<line x1="80" y1="232" x2="80" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
<line x1="920" y1="232" x2="920" y2="248" stroke="rgba(11,13,11,0.20)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Event 1: FEB 2025 · First post (below) -->
|
||||||
|
<line x1="100" y1="240" x2="100" y2="296" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="100" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="100" y="312" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">FEB 2025</text>
|
||||||
|
<text x="100" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">First post</text>
|
||||||
|
|
||||||
|
<!-- Event 2: APR 2025 · Design v1 (above) -->
|
||||||
|
<line x1="240" y1="184" x2="240" y2="240" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="240" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="240" y="156" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2025</text>
|
||||||
|
<text x="240" y="172" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v1</text>
|
||||||
|
|
||||||
|
<!-- Event 3: SEP 2025 · Design v2 (below) -->
|
||||||
|
<line x1="500" y1="240" x2="500" y2="296" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<circle cx="500" cy="240" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="500" y="312" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">SEP 2025</text>
|
||||||
|
<text x="500" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v2</text>
|
||||||
|
<text x="500" y="344" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">typography pass</text>
|
||||||
|
|
||||||
|
<!-- Event 4: JAN 2026 · Design v3 (above, coral major) -->
|
||||||
|
<line x1="740" y1="160" x2="740" y2="240" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<circle cx="740" cy="240" r="6" fill="#f7591f"/>
|
||||||
|
<text x="740" y="128" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">JAN 2026</text>
|
||||||
|
<text x="740" y="148" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design v3</text>
|
||||||
|
<text x="740" y="164" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">complexity budget</text>
|
||||||
|
|
||||||
|
<!-- Event 5: APR 2026 · now · Diagram Design skill (below, coral) -->
|
||||||
|
<line x1="900" y1="240" x2="900" y2="296" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<circle cx="900" cy="240" r="6" fill="#f7591f"/>
|
||||||
|
<text x="900" y="312" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">APR 2026 · NOW</text>
|
||||||
|
<text x="900" y="328" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">diagram-design</text>
|
||||||
|
<text x="900" y="344" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">eight diagram types</text>
|
||||||
|
|
||||||
|
<!-- Legend -->
|
||||||
|
<line x1="40" y1="376" x2="960" y2="376" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="392" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="408" r="4" fill="#0b0d0b"/>
|
||||||
|
<text x="68" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Event</text>
|
||||||
|
|
||||||
|
<circle cx="148" cy="408" r="6" fill="#f7591f"/>
|
||||||
|
<text x="164" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Major milestone</text>
|
||||||
|
|
||||||
|
<text x="296" y="412" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Spacing is proportional to real elapsed time.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Claude Code skill taxonomy · Tree</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Tree · Diagram Design</p>
|
||||||
|
<h1>Claude Code skill taxonomy</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Tier tags (left margin) -->
|
||||||
|
<text x="40" y="108" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">TIER 0 · ROOT</text>
|
||||||
|
<text x="40" y="224" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 1</text>
|
||||||
|
<text x="40" y="324" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 2</text>
|
||||||
|
|
||||||
|
<!-- Connectors drawn first (behind nodes) -->
|
||||||
|
<!-- Root → Tier 1 bus -->
|
||||||
|
<path d="M 500 128 L 500 168 L 220 168 L 220 208" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 500 208" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 780 168 L 780 208" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Design → leaves -->
|
||||||
|
<path d="M 220 256 L 220 296 L 140 296 L 140 336" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<path d="M 220 296 L 300 296 L 300 336" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Engineering → leaves -->
|
||||||
|
<path d="M 500 256 L 500 296 L 480 296 L 480 336" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<path d="M 500 296 L 660 296 L 660 336" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Research → single leaf -->
|
||||||
|
<path d="M 780 256 L 780 296 L 840 296 L 840 336" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Root node: Skills (coral focal) -->
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="rgba(255,106,48,0.10)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<rect x="428" y="88" width="32" height="12" rx="2" fill="transparent" stroke="rgba(255,106,48,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="444" y="97" fill="#ff6a30" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ROOT</text>
|
||||||
|
<text x="500" y="118" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Skills</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Design -->
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="rgba(241,239,231,0.04)" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="148" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="162" y="225" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="220" y="240" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design</text>
|
||||||
|
<text x="220" y="252" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ui · visual · ux</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Engineering -->
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="rgba(241,239,231,0.04)" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="428" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="442" y="225" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="500" y="240" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Engineering</text>
|
||||||
|
<text x="500" y="252" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ship · review · test</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Research -->
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="rgba(241,239,231,0.04)" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<rect x="708" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(241,239,231,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="722" y="225" fill="#f1efe7" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="780" y="240" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Research</text>
|
||||||
|
<text x="780" y="252" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">investigate · analyze</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: polish -->
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="140" y="360" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">polish</text>
|
||||||
|
<text x="140" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">align · space · rhythm</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: critique -->
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="300" y="360" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">critique</text>
|
||||||
|
<text x="300" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">hierarchy · density</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: review -->
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="480" y="360" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">review</text>
|
||||||
|
<text x="480" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">pre-land diff · sql</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: ship -->
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="660" y="360" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">ship</text>
|
||||||
|
<text x="660" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">merge · deploy · verify</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: investigate -->
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="#1c1a17"/>
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="840" y="360" fill="#f1efe7" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">investigate</text>
|
||||||
|
<text x="840" y="372" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">root cause · evidence</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="412" x2="960" y2="412" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="428" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="444" width="14" height="10" rx="2" fill="rgba(255,106,48,0.10)" stroke="#ff6a30" stroke-width="1"/>
|
||||||
|
<text x="60" y="452" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Root · focal</text>
|
||||||
|
|
||||||
|
<rect x="180" y="444" width="14" height="10" rx="2" fill="rgba(241,239,231,0.04)" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<text x="200" y="452" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Category branch</text>
|
||||||
|
|
||||||
|
<rect x="340" y="444" width="14" height="10" rx="2" fill="rgba(241,239,231,0.05)" stroke="#a8a69d" stroke-width="0.8"/>
|
||||||
|
<text x="360" y="452" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Leaf skill</text>
|
||||||
|
|
||||||
|
<text x="500" y="452" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Orthogonal connectors only. Coral marks the root — every branch descends from one idea.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Claude Code skill taxonomy · Tree</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Tree · Diagram Design</p>
|
||||||
|
<h1>Claude Code skill taxonomy</h1>
|
||||||
|
<p class="subtitle">Three tiers, one root. The full skill library fans out from a single idea — and every leaf descends through exactly one category. Orthogonal connectors, one coral accent, nothing else.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Tier tags (left margin) -->
|
||||||
|
<text x="40" y="108" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">TIER 0 · ROOT</text>
|
||||||
|
<text x="40" y="224" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 1</text>
|
||||||
|
<text x="40" y="324" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 2</text>
|
||||||
|
|
||||||
|
<!-- Connectors drawn first (behind nodes) -->
|
||||||
|
<!-- Root → Tier 1 bus -->
|
||||||
|
<path d="M 500 128 L 500 168 L 220 168 L 220 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 500 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 780 168 L 780 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Design → leaves -->
|
||||||
|
<path d="M 220 256 L 220 296 L 140 296 L 140 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 220 296 L 300 296 L 300 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Engineering → leaves -->
|
||||||
|
<path d="M 500 256 L 500 296 L 480 296 L 480 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 296 L 660 296 L 660 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Research → single leaf -->
|
||||||
|
<path d="M 780 256 L 780 296 L 840 296 L 840 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Root node: Skills (coral focal) -->
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="428" y="88" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="444" y="97" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ROOT</text>
|
||||||
|
<text x="500" y="118" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Skills</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Design -->
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="148" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="162" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="220" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design</text>
|
||||||
|
<text x="220" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ui · visual · ux</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Engineering -->
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="428" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="442" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="500" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Engineering</text>
|
||||||
|
<text x="500" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ship · review · test</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Research -->
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="708" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="722" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="780" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Research</text>
|
||||||
|
<text x="780" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">investigate · analyze</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: polish -->
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="140" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">polish</text>
|
||||||
|
<text x="140" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">align · space · rhythm</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: critique -->
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="300" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">critique</text>
|
||||||
|
<text x="300" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">hierarchy · density</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: review -->
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="480" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">review</text>
|
||||||
|
<text x="480" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">pre-land diff · sql</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: ship -->
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="660" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">ship</text>
|
||||||
|
<text x="660" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">merge · deploy · verify</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: investigate -->
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="840" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">investigate</text>
|
||||||
|
<text x="840" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">root cause · evidence</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="412" x2="960" y2="412" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="428" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="444" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Root · focal</text>
|
||||||
|
|
||||||
|
<rect x="180" y="444" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Category branch</text>
|
||||||
|
|
||||||
|
<rect x="340" y="444" width="14" height="10" rx="2" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="360" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Leaf skill</text>
|
||||||
|
|
||||||
|
<text x="500" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Orthogonal connectors only. Coral marks the root — every branch descends from one idea.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>One root, one coral dot</h3></div>
|
||||||
|
<p>A taxonomy is a promise: every child has exactly one parent. Coral lives on the root because that's the only node the tree is actually about — everything else is a descent.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>Connectors are orthogonal</h3></div>
|
||||||
|
<ul><li>Parent drops vertical</li><li>Horizontal bus connects siblings</li><li>Each child drops into its top edge</li><li>No diagonals, no curves</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>Uneven breadth is honest</h3></div>
|
||||||
|
<p>Design and Engineering fan to two leaves; Research drops to one. The layout reflects the real shape of the library, not a forced symmetry.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>claude code skill taxonomy</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Claude Code skill taxonomy · Tree</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Tree · Diagram Design</p>
|
||||||
|
<h1>Claude Code skill taxonomy</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 480" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Tier tags (left margin) -->
|
||||||
|
<text x="40" y="108" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">TIER 0 · ROOT</text>
|
||||||
|
<text x="40" y="224" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 1</text>
|
||||||
|
<text x="40" y="324" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="start">TIER 2</text>
|
||||||
|
|
||||||
|
<!-- Connectors drawn first (behind nodes) -->
|
||||||
|
<!-- Root → Tier 1 bus -->
|
||||||
|
<path d="M 500 128 L 500 168 L 220 168 L 220 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 500 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 168 L 780 168 L 780 208" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Design → leaves -->
|
||||||
|
<path d="M 220 256 L 220 296 L 140 296 L 140 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 220 296 L 300 296 L 300 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Engineering → leaves -->
|
||||||
|
<path d="M 500 256 L 500 296 L 480 296 L 480 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<path d="M 500 296 L 660 296 L 660 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Research → single leaf -->
|
||||||
|
<path d="M 780 256 L 780 296 L 840 296 L 840 336" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Root node: Skills (coral focal) -->
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="420" y="80" width="160" height="48" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="428" y="88" width="32" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="444" y="97" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ROOT</text>
|
||||||
|
<text x="500" y="118" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Skills</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Design -->
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="140" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="148" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="162" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="220" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Design</text>
|
||||||
|
<text x="220" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ui · visual · ux</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Engineering -->
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="420" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="428" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="442" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="500" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Engineering</text>
|
||||||
|
<text x="500" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">ship · review · test</text>
|
||||||
|
|
||||||
|
<!-- Tier 1: Research -->
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="700" y="208" width="160" height="48" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="708" y="216" width="28" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="722" y="225" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CAT</text>
|
||||||
|
<text x="780" y="240" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Research</text>
|
||||||
|
<text x="780" y="252" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">investigate · analyze</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: polish -->
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="60" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="140" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">polish</text>
|
||||||
|
<text x="140" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">align · space · rhythm</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: critique -->
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="220" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="300" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">critique</text>
|
||||||
|
<text x="300" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">hierarchy · density</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: review -->
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="400" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="480" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">review</text>
|
||||||
|
<text x="480" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">pre-land diff · sql</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: ship -->
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="580" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="660" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">ship</text>
|
||||||
|
<text x="660" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">merge · deploy · verify</text>
|
||||||
|
|
||||||
|
<!-- Tier 2: investigate -->
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="760" y="336" width="160" height="48" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="840" y="360" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">investigate</text>
|
||||||
|
<text x="840" y="372" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">root cause · evidence</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="412" x2="960" y2="412" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="428" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="40" y="444" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="60" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Root · focal</text>
|
||||||
|
|
||||||
|
<rect x="180" y="444" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="200" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Category branch</text>
|
||||||
|
|
||||||
|
<rect x="340" y="444" width="14" height="10" rx="2" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
<text x="360" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Leaf skill</text>
|
||||||
|
|
||||||
|
<text x="500" y="452" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Orthogonal connectors only. Coral marks the root — every branch descends from one idea.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Good design · Desirable × Feasible × Viable</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17;
|
||||||
|
--color-ink: #f1efe7;
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Venn · Diagram Design</p>
|
||||||
|
<h1>Good design · Desirable × Feasible × Viable</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<clipPath id="clip-center">
|
||||||
|
<circle cx="500" cy="180" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
<clipPath id="clip-center-2">
|
||||||
|
<circle cx="428" cy="320" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Three set circles (stroke + very-low-opacity tint; tints compound in overlaps) -->
|
||||||
|
<!-- Desirable — top, ink -->
|
||||||
|
<circle cx="500" cy="180" r="140" fill="rgba(241,239,231,0.04)" stroke="#f1efe7" stroke-width="1"/>
|
||||||
|
<!-- Feasible — bottom-left, muted -->
|
||||||
|
<circle cx="428" cy="320" r="140" fill="rgba(168,166,157,0.05)" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<!-- Viable — bottom-right, soft -->
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(142,140,131,0.05)" stroke="#8e8c83" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Coral focal tint on the all-three intersection -->
|
||||||
|
<g clip-path="url(#clip-center)">
|
||||||
|
<g clip-path="url(#clip-center-2)">
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(255,106,48,0.14)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Set labels -->
|
||||||
|
<text x="500" y="20" fill="#f1efe7" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Desirable</text>
|
||||||
|
<text x="500" y="36" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">PEOPLE WANT IT</text>
|
||||||
|
|
||||||
|
<text x="152" y="400" fill="#a8a69d" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Feasible</text>
|
||||||
|
<text x="152" y="416" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="start" letter-spacing="0.14em">WE CAN BUILD IT</text>
|
||||||
|
|
||||||
|
<text x="848" y="400" fill="#8e8c83" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Viable</text>
|
||||||
|
<text x="848" y="416" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">BUSINESS SUSTAINS</text>
|
||||||
|
|
||||||
|
<!-- Pairwise intersection labels -->
|
||||||
|
<text x="360" y="232" fill="#a8a69d" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Prototype</text>
|
||||||
|
<text x="360" y="248" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">no business model</text>
|
||||||
|
|
||||||
|
<text x="640" y="232" fill="#a8a69d" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Vaporware</text>
|
||||||
|
<text x="640" y="248" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">can't build it</text>
|
||||||
|
|
||||||
|
<text x="500" y="368" fill="#a8a69d" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Internal tool</text>
|
||||||
|
<text x="500" y="384" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">nobody wants it</text>
|
||||||
|
|
||||||
|
<!-- Focal: all-three center -->
|
||||||
|
<text x="500" y="260" fill="#ff6a30" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Shippable</text>
|
||||||
|
<text x="500" y="276" fill="#a8a69d" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">THE SWEET SPOT</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(241,239,231,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#a8a69d" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="none" stroke="#ff6a30" stroke-width="1.2"/>
|
||||||
|
<text x="68" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">All three — ship it</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="6" fill="none" stroke="#a8a69d" stroke-width="1"/>
|
||||||
|
<text x="208" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif">Two of three — incomplete</text>
|
||||||
|
|
||||||
|
<text x="380" y="492" fill="#a8a69d" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Coral marks the intersection that earns the work. The others name the traps.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Good design · Desirable × Feasible × Viable</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root { --color-paper:#f5f4ed; --color-paper-2:#efeee5; --color-ink:#0b0d0b; --color-muted:#52534e; --color-soft:#65655c; --color-rule:rgba(11,13,11,0.12); --color-accent:#f7591f; --color-link:#1a70c7; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
|
||||||
|
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
|
||||||
|
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
|
||||||
|
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
|
||||||
|
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
|
||||||
|
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
|
||||||
|
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
|
||||||
|
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
|
||||||
|
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(11,13,11,0.08); }
|
||||||
|
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
|
||||||
|
.card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); } .card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||||
|
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
|
||||||
|
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
|
||||||
|
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(11,13,11,0.25); font-size: 0.75rem; }
|
||||||
|
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(11,13,11,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Venn · Diagram Design</p>
|
||||||
|
<h1>Good design · Desirable × Feasible × Viable</h1>
|
||||||
|
<p class="subtitle">Three tests every product has to pass before it earns the word "shippable." Miss one and you get a prototype, a vaporware demo, or an internal tool nobody asked for. Coral marks the intersection worth the work.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<clipPath id="clip-center">
|
||||||
|
<circle cx="500" cy="180" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
<clipPath id="clip-center-2">
|
||||||
|
<circle cx="428" cy="320" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Three set circles (stroke + very-low-opacity tint; tints compound in overlaps) -->
|
||||||
|
<!-- Desirable — top, ink -->
|
||||||
|
<circle cx="500" cy="180" r="140" fill="rgba(11,13,11,0.04)" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<!-- Feasible — bottom-left, muted -->
|
||||||
|
<circle cx="428" cy="320" r="140" fill="rgba(82,83,78,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<!-- Viable — bottom-right, soft -->
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(101,101,92,0.05)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Coral focal tint on the all-three intersection -->
|
||||||
|
<g clip-path="url(#clip-center)">
|
||||||
|
<g clip-path="url(#clip-center-2)">
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(247,89,31,0.10)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Set labels -->
|
||||||
|
<text x="500" y="20" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Desirable</text>
|
||||||
|
<text x="500" y="36" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">PEOPLE WANT IT</text>
|
||||||
|
|
||||||
|
<text x="152" y="400" fill="#52534e" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Feasible</text>
|
||||||
|
<text x="152" y="416" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="start" letter-spacing="0.14em">WE CAN BUILD IT</text>
|
||||||
|
|
||||||
|
<text x="848" y="400" fill="#65655c" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Viable</text>
|
||||||
|
<text x="848" y="416" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">BUSINESS SUSTAINS</text>
|
||||||
|
|
||||||
|
<!-- Pairwise intersection labels -->
|
||||||
|
<text x="360" y="232" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Prototype</text>
|
||||||
|
<text x="360" y="248" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">no business model</text>
|
||||||
|
|
||||||
|
<text x="640" y="232" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Vaporware</text>
|
||||||
|
<text x="640" y="248" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">can't build it</text>
|
||||||
|
|
||||||
|
<text x="500" y="368" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Internal tool</text>
|
||||||
|
<text x="500" y="384" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">nobody wants it</text>
|
||||||
|
|
||||||
|
<!-- Focal: all-three center -->
|
||||||
|
<text x="500" y="260" fill="#f7591f" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Shippable</text>
|
||||||
|
<text x="500" y="276" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">THE SWEET SPOT</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="none" stroke="#f7591f" stroke-width="1.2"/>
|
||||||
|
<text x="68" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">All three — ship it</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="6" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<text x="208" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Two of three — incomplete</text>
|
||||||
|
|
||||||
|
<text x="380" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Coral marks the intersection that earns the work. The others name the traps.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<p class="eyebrow">THE HEADLINE</p>
|
||||||
|
<div class="card-header"><span class="card-dot coral"></span><h3>One sweet spot, in coral</h3></div>
|
||||||
|
<p>Three overlapping circles create seven regions. Six of them are diagnostic — they name the failure mode. Only the center earns the coral. If every region is colored, the diagram stops prioritizing anything.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot ink"></span><h3>The three tests</h3></div>
|
||||||
|
<ul><li>Desirable — someone pulls for it</li><li>Feasible — the team can actually build it</li><li>Viable — the economics hold up over time</li><li>All three, or you don't ship</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><span class="card-dot muted"></span><h3>The named traps</h3></div>
|
||||||
|
<p>Prototype (loved, buildable, no model). Vaporware (loved, profitable, un-buildable). Internal tool (buildable, profitable, unloved). Labeling each one makes the map more useful than the center alone.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<span>good design · desirable × feasible × viable</span>
|
||||||
|
<span>example · diagram-design</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Good design · Desirable × Feasible × Viable</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">Venn · Diagram Design</p>
|
||||||
|
<h1>Good design · Desirable × Feasible × Viable</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<!-- Clip to the all-three intersection (centroid region) for the coral tint -->
|
||||||
|
<clipPath id="clip-center">
|
||||||
|
<circle cx="500" cy="180" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
<clipPath id="clip-center-2">
|
||||||
|
<circle cx="428" cy="320" r="140"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Three set circles (stroke + very-low-opacity tint; tints compound in overlaps) -->
|
||||||
|
<!-- Desirable — top, ink -->
|
||||||
|
<circle cx="500" cy="180" r="140" fill="rgba(11,13,11,0.04)" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<!-- Feasible — bottom-left, muted -->
|
||||||
|
<circle cx="428" cy="320" r="140" fill="rgba(82,83,78,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<!-- Viable — bottom-right, soft -->
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(101,101,92,0.05)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Coral focal tint on the all-three intersection -->
|
||||||
|
<g clip-path="url(#clip-center)">
|
||||||
|
<g clip-path="url(#clip-center-2)">
|
||||||
|
<circle cx="572" cy="320" r="140" fill="rgba(247,89,31,0.10)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Set labels (outside circles, pinned to far side) -->
|
||||||
|
<!-- Desirable -->
|
||||||
|
<text x="500" y="20" fill="#0b0d0b" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Desirable</text>
|
||||||
|
<text x="500" y="36" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">PEOPLE WANT IT</text>
|
||||||
|
|
||||||
|
<!-- Feasible -->
|
||||||
|
<text x="152" y="400" fill="#52534e" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Feasible</text>
|
||||||
|
<text x="152" y="416" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="start" letter-spacing="0.14em">WE CAN BUILD IT</text>
|
||||||
|
|
||||||
|
<!-- Viable -->
|
||||||
|
<text x="848" y="400" fill="#65655c" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Viable</text>
|
||||||
|
<text x="848" y="416" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="end" letter-spacing="0.14em">BUSINESS SUSTAINS</text>
|
||||||
|
|
||||||
|
<!-- Pairwise intersection labels (inside overlap regions) -->
|
||||||
|
<!-- Desirable ∩ Feasible (upper-left overlap): "Prototype" — between top and BL -->
|
||||||
|
<text x="360" y="232" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Prototype</text>
|
||||||
|
<text x="360" y="248" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">no business model</text>
|
||||||
|
|
||||||
|
<!-- Desirable ∩ Viable (upper-right overlap): "Vaporware" -->
|
||||||
|
<text x="640" y="232" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Vaporware</text>
|
||||||
|
<text x="640" y="248" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">can't build it</text>
|
||||||
|
|
||||||
|
<!-- Feasible ∩ Viable (lower overlap): "Internal tool" -->
|
||||||
|
<text x="500" y="368" fill="#52534e" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Internal tool</text>
|
||||||
|
<text x="500" y="384" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">nobody wants it</text>
|
||||||
|
|
||||||
|
<!-- Focal: all-three center -->
|
||||||
|
<text x="500" y="260" fill="#f7591f" font-size="14" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Shippable</text>
|
||||||
|
<text x="500" y="276" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.14em">THE SWEET SPOT</text>
|
||||||
|
|
||||||
|
<!-- Legend strip -->
|
||||||
|
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
|
||||||
|
<text x="40" y="472" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
||||||
|
|
||||||
|
<circle cx="52" cy="488" r="6" fill="none" stroke="#f7591f" stroke-width="1.2"/>
|
||||||
|
<text x="68" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">All three — ship it</text>
|
||||||
|
|
||||||
|
<circle cx="192" cy="488" r="6" fill="none" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<text x="208" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif">Two of three — incomplete</text>
|
||||||
|
|
||||||
|
<text x="380" y="492" fill="#52534e" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">Coral marks the intersection that earns the work. The others name the traps.</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Diagram Design · Gallery</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--paper: #f5f4ed;
|
||||||
|
--paper-2: #efeee5;
|
||||||
|
--ink: #0b0d0b;
|
||||||
|
--muted: #52534e;
|
||||||
|
--soft: #65655c;
|
||||||
|
--rule: rgba(11,13,11,0.12);
|
||||||
|
--rule-strong: rgba(11,13,11,0.25);
|
||||||
|
--accent: #f7591f;
|
||||||
|
--sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--serif: 'Instrument Serif', serif;
|
||||||
|
--mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
html, body { height: 100%; }
|
||||||
|
body {
|
||||||
|
font-family: var(--sans);
|
||||||
|
background: var(--paper);
|
||||||
|
color: var(--ink);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar {
|
||||||
|
padding: 1rem 1.25rem 0.625rem;
|
||||||
|
border-bottom: 1px solid var(--rule);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
.title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.title-row h1 {
|
||||||
|
font-family: var(--serif);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.title-row .meta {
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
.title-row .open-raw {
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--muted);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.3rem 0.625rem;
|
||||||
|
border: 1px solid var(--rule);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: color 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
.open-raw:hover { color: var(--accent); border-color: var(--accent); }
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.375rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.tabs.variants {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--rule);
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
font-family: var(--sans);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted);
|
||||||
|
border: 1px solid var(--rule);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.12s;
|
||||||
|
letter-spacing: -0.005em;
|
||||||
|
}
|
||||||
|
.tab:hover {
|
||||||
|
color: var(--ink);
|
||||||
|
border-color: var(--rule-strong);
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--paper);
|
||||||
|
border-color: var(--ink);
|
||||||
|
}
|
||||||
|
.tab .eyebrow {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 0.56rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: inherit;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.tab.new::after {
|
||||||
|
content: 'NEW';
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 0.35rem;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 0.54rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
color: var(--accent);
|
||||||
|
vertical-align: 1px;
|
||||||
|
}
|
||||||
|
.tab.active.new::after { color: var(--paper); }
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
position: relative;
|
||||||
|
background: var(--paper-2);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
background: var(--paper);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="topbar">
|
||||||
|
<div class="title-row">
|
||||||
|
<h1>Diagram Design <span class="meta" style="font-family: var(--mono); font-size: 0.66rem; margin-left: 0.5rem;">· gallery</span></h1>
|
||||||
|
<a class="open-raw" id="open-raw" href="#" target="_blank" rel="noopener">open in new tab ↗</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs" id="type-tabs" role="tablist" aria-label="Diagram type">
|
||||||
|
<button class="tab active" data-type="architecture"><span class="eyebrow">01</span>Architecture</button>
|
||||||
|
<button class="tab" data-type="flowchart"><span class="eyebrow">02</span>Flowchart</button>
|
||||||
|
<button class="tab" data-type="sequence"><span class="eyebrow">03</span>Sequence</button>
|
||||||
|
<button class="tab" data-type="state"><span class="eyebrow">04</span>State</button>
|
||||||
|
<button class="tab" data-type="er"><span class="eyebrow">05</span>ER</button>
|
||||||
|
<button class="tab" data-type="timeline"><span class="eyebrow">06</span>Timeline</button>
|
||||||
|
<button class="tab" data-type="swimlane"><span class="eyebrow">07</span>Swimlane</button>
|
||||||
|
<button class="tab" data-type="quadrant"><span class="eyebrow">08</span>Quadrant</button>
|
||||||
|
<button class="tab new" data-type="nested"><span class="eyebrow">09</span>Nested</button>
|
||||||
|
<button class="tab new" data-type="tree"><span class="eyebrow">10</span>Tree</button>
|
||||||
|
<button class="tab new" data-type="layers"><span class="eyebrow">11</span>Layers</button>
|
||||||
|
<button class="tab new" data-type="venn"><span class="eyebrow">12</span>Venn</button>
|
||||||
|
<button class="tab new" data-type="pyramid"><span class="eyebrow">13</span>Pyramid</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs variants" id="variant-tabs" role="tablist" aria-label="Variant">
|
||||||
|
<button class="tab active" data-variant="">Minimal light</button>
|
||||||
|
<button class="tab" data-variant="-dark">Minimal dark</button>
|
||||||
|
<button class="tab" data-variant="-full">Full editorial</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<iframe id="preview" src="example-architecture.html" title="Preview"></iframe>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const state = { type: 'architecture', variant: '' };
|
||||||
|
const iframe = document.getElementById('preview');
|
||||||
|
const openRaw = document.getElementById('open-raw');
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const src = `example-${state.type}${state.variant}.html`;
|
||||||
|
iframe.src = src;
|
||||||
|
openRaw.href = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindTabs(containerId, key) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
container.addEventListener('click', (e) => {
|
||||||
|
const btn = e.target.closest('.tab');
|
||||||
|
if (!btn) return;
|
||||||
|
container.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
state[key] = btn.dataset[key];
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindTabs('type-tabs', 'type');
|
||||||
|
bindTabs('variant-tabs', 'variant');
|
||||||
|
update();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Diagram</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #1c1a17; /* deep warm charcoal */
|
||||||
|
--color-ink: #f1efe7; /* warm off-white */
|
||||||
|
--color-muted: #a8a69d;
|
||||||
|
--color-accent: #ff6a30;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">[Type] · Diagram Design</p>
|
||||||
|
<h1>[Diagram title]</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(241,239,231,0.08)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#a8a69d"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#ff6a30"/></marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#5ba8eb"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#1c1a17"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.6"/>
|
||||||
|
|
||||||
|
<!-- Draw arrows first, then nodes. Replace with your content. -->
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,403 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>[PROJECT NAME] Architecture</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
/* ── Tokens ─────────────────────────────────────────────────── */
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-paper-2: #efeee5;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-soft: #65655c;
|
||||||
|
--color-rule: rgba(11,13,11,0.12);
|
||||||
|
--color-rule-solid: rgba(135,139,134,0.25);
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--color-accent-tint: rgba(247,89,31,0.08);
|
||||||
|
--color-link: #1a70c7;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', 'Times New Roman', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
color: var(--color-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
|
|
||||||
|
/* ── Header ─────────────────────────────────────────────────── */
|
||||||
|
.header { margin-bottom: 2.5rem; }
|
||||||
|
|
||||||
|
.header-eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.1;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--color-muted);
|
||||||
|
max-width: 52ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Diagram container ────────────────────────────────────────── */
|
||||||
|
.diagram-container {
|
||||||
|
background: var(--color-paper-2);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-rule);
|
||||||
|
padding: 1.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
|
||||||
|
/* ── Info cards ───────────────────────────────────────────────── */
|
||||||
|
.cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-rule);
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-bottom: 0.875rem;
|
||||||
|
padding-bottom: 0.875rem;
|
||||||
|
border-bottom: 1px solid rgba(11,13,11,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-dot {
|
||||||
|
width: 7px; height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.card-dot.ink { background: var(--color-ink); }
|
||||||
|
.card-dot.muted { background: var(--color-muted); }
|
||||||
|
.card-dot.coral { background: var(--color-accent); }
|
||||||
|
.card-dot.link { background: var(--color-link); }
|
||||||
|
.card-dot.soft { background: var(--color-soft); }
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-ink);
|
||||||
|
letter-spacing: -0.005em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card ul {
|
||||||
|
list-style: none;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card li {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
padding-left: 0.875rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card li::before {
|
||||||
|
content: '—';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: rgba(11,13,11,0.25);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Footer ─────────────────────────────────────────────────── */
|
||||||
|
.footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid rgba(11,13,11,0.10);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--color-soft);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<p class="header-eyebrow">Architecture · [Date]</p>
|
||||||
|
<h1>[Project Name]</h1>
|
||||||
|
<p class="subtitle">[One-sentence description of what this diagram shows.]</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Diagram -->
|
||||||
|
<div class="diagram-container">
|
||||||
|
<svg viewBox="0 0 1000 700" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
|
||||||
|
<!-- Dot grid background (matches site .hero-terminal pattern) -->
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<!-- Arrow: muted (default — generic / internal connections) -->
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#52534e"/>
|
||||||
|
</marker>
|
||||||
|
|
||||||
|
<!-- Arrow: coral (accent — primary / highlighted flows) -->
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#f7591f"/>
|
||||||
|
</marker>
|
||||||
|
|
||||||
|
<!-- Arrow: link-blue (#1a70c7 — HTTP/API / external calls) -->
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
|
<polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/>
|
||||||
|
</marker>
|
||||||
|
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Background: paper + dot grid -->
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.6"/>
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
DRAW ARROWS FIRST — they paint behind boxes (SVG document order).
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- HTTP/API arrow (link-blue) with label -->
|
||||||
|
<line x1="130" y1="305" x2="195" y2="305" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="162" y="297" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">HTTPS</text>
|
||||||
|
|
||||||
|
<!-- Generic internal arrow -->
|
||||||
|
<line x1="310" y1="305" x2="356" y2="305" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Vertical arrow -->
|
||||||
|
<line x1="253" y1="332" x2="253" y2="376" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
|
||||||
|
<!-- Auth / security flow (dashed coral) -->
|
||||||
|
<path d="M 80 140 L 80 200 Q 80 218 98 218 L 200 218 Q 218 218 218 236 L 218 276"
|
||||||
|
fill="none" stroke="#f7591f" stroke-width="1.2" stroke-dasharray="5,4"
|
||||||
|
marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="148" y="212" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">JWT</text>
|
||||||
|
|
||||||
|
<!-- Primary data flow (coral) -->
|
||||||
|
<line x1="620" y1="305" x2="696" y2="305" stroke="#f7591f" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="655" y="297" fill="#f7591f" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">TLS</text>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
COMPONENT BOXES
|
||||||
|
Pattern: opaque paper rect first (masks arrows), then styled rect.
|
||||||
|
Mono type labels use Geist Mono — mono is dominant in tool/data.
|
||||||
|
================================================================= -->
|
||||||
|
|
||||||
|
<!-- ── External / User ──────────────────────────────────────── -->
|
||||||
|
<rect x="30" y="278" width="100" height="54" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="30" y="278" width="100" height="54" rx="6" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<!-- Rectangular type tag (not rounded — brand rule) -->
|
||||||
|
<rect x="38" y="284" width="26" height="12" rx="2" fill="transparent" stroke="rgba(101,101,92,0.4)" stroke-width="0.8"/>
|
||||||
|
<text x="51" y="293" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
|
||||||
|
<!-- Main label -->
|
||||||
|
<text x="80" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Users</text>
|
||||||
|
<text x="80" y="323" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Browser / Mobile</text>
|
||||||
|
|
||||||
|
<!-- ── Security / Auth ──────────────────────────────────────── -->
|
||||||
|
<rect x="30" y="78" width="100" height="56" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="30" y="78" width="100" height="56" rx="6" fill="rgba(247,89,31,0.06)" stroke="rgba(247,89,31,0.50)" stroke-width="1"/>
|
||||||
|
<rect x="38" y="84" width="28" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="52" y="93" fill="rgba(247,89,31,0.8)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">AUTH</text>
|
||||||
|
<text x="80" y="110" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Auth Provider</text>
|
||||||
|
<text x="80" y="125" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">OAuth 2.0</text>
|
||||||
|
|
||||||
|
<!-- ── Region boundary (dashed) ─────────────────────────────── -->
|
||||||
|
<rect x="158" y="38" width="822" height="624" rx="8" fill="transparent" stroke="rgba(11,13,11,0.18)" stroke-width="1" stroke-dasharray="6,4"/>
|
||||||
|
<text x="170" y="55" fill="#52534e" font-size="8" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">AWS REGION · US-WEST-2</text>
|
||||||
|
|
||||||
|
<!-- ── Cloud / CDN (with faded watermark numeral — slide card pattern) -->
|
||||||
|
<rect x="198" y="278" width="112" height="54" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="198" y="278" width="112" height="54" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<!-- Faded mono watermark numeral (background) -->
|
||||||
|
<text x="294" y="323" fill="rgba(11,13,11,0.06)" font-size="40" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">01</text>
|
||||||
|
<rect x="206" y="284" width="24" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="218" y="293" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">CDN</text>
|
||||||
|
<text x="254" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">CloudFront</text>
|
||||||
|
<text x="254" y="323" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">AWS CDN</text>
|
||||||
|
|
||||||
|
<!-- ── Cloud / Storage ───────────────────────────────────────── -->
|
||||||
|
<rect x="198" y="378" width="112" height="102" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="198" y="378" width="112" height="102" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="294" y="468" fill="rgba(11,13,11,0.06)" font-size="40" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="end">02</text>
|
||||||
|
<rect x="206" y="384" width="22" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="217" y="393" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">S3</text>
|
||||||
|
<text x="254" y="408" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S3 Buckets</text>
|
||||||
|
<text x="254" y="426" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">bucket-assets</text>
|
||||||
|
<text x="254" y="440" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">bucket-uploads</text>
|
||||||
|
<text x="254" y="466" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">OAI PROTECTED</text>
|
||||||
|
|
||||||
|
<!-- ── Security group boundary ───────────────────────────────── -->
|
||||||
|
<rect x="348" y="264" width="120" height="80" rx="6" fill="transparent" stroke="rgba(247,89,31,0.45)" stroke-width="1" stroke-dasharray="4,4"/>
|
||||||
|
<text x="356" y="278" fill="rgba(247,89,31,0.7)" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.08em">SG · :443</text>
|
||||||
|
|
||||||
|
<!-- ── Load Balancer (inside security group) ──────────────────── -->
|
||||||
|
<rect x="358" y="282" width="100" height="54" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="358" y="282" width="100" height="54" rx="6" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<rect x="366" y="288" width="22" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.22)" stroke-width="0.8"/>
|
||||||
|
<text x="377" y="297" fill="#65655c" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">LB</text>
|
||||||
|
<text x="408" y="311" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Load Balancer</text>
|
||||||
|
<text x="408" y="326" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">HTTPS :443</text>
|
||||||
|
|
||||||
|
<!-- ── Backend / API ─────────────────────────────────────────── -->
|
||||||
|
<rect x="508" y="278" width="112" height="54" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="508" y="278" width="112" height="54" rx="6" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<rect x="516" y="284" width="26" height="12" rx="2" fill="transparent" stroke="rgba(11,13,11,0.40)" stroke-width="0.8"/>
|
||||||
|
<text x="529" y="293" fill="#0b0d0b" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">API</text>
|
||||||
|
<text x="564" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">API Server</text>
|
||||||
|
<text x="564" y="323" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">FastAPI :8000</text>
|
||||||
|
|
||||||
|
<!-- ── Database ───────────────────────────────────────────────── -->
|
||||||
|
<rect x="698" y="278" width="120" height="54" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="698" y="278" width="120" height="54" rx="6" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<rect x="706" y="284" width="22" height="12" rx="2" fill="transparent" stroke="rgba(82,83,78,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="717" y="293" fill="#52534e" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DB</text>
|
||||||
|
<text x="758" y="308" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Database</text>
|
||||||
|
<text x="758" y="323" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">PostgreSQL</text>
|
||||||
|
|
||||||
|
<!-- ── Frontend / UI ─────────────────────────────────────────── -->
|
||||||
|
<rect x="198" y="518" width="200" height="112" rx="6" fill="#f5f4ed"/>
|
||||||
|
<rect x="198" y="518" width="200" height="112" rx="6" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<rect x="206" y="524" width="22" height="12" rx="2" fill="transparent" stroke="rgba(247,89,31,0.50)" stroke-width="0.8"/>
|
||||||
|
<text x="217" y="533" fill="#f7591f" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">UI</text>
|
||||||
|
<text x="298" y="550" fill="#0b0d0b" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Web App</text>
|
||||||
|
<text x="298" y="567" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">React + TypeScript</text>
|
||||||
|
<text x="298" y="581" fill="#52534e" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">Astro SSG</text>
|
||||||
|
<!-- Link-blue URL label -->
|
||||||
|
<text x="298" y="614" fill="#1a70c7" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.04em">app.example.com</text>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =================================================================
|
||||||
|
LEGEND — OUTSIDE all boundary boxes.
|
||||||
|
Region boundary ends at y=662 → legend starts at y=682.
|
||||||
|
================================================================= -->
|
||||||
|
<text x="510" y="544" fill="#0b0d0b" font-size="9" font-weight="500" font-family="'Geist Mono', monospace" letter-spacing="0.12em">LEGEND</text>
|
||||||
|
|
||||||
|
<rect x="510" y="558" width="14" height="10" rx="2" fill="rgba(247,89,31,0.08)" stroke="#f7591f" stroke-width="1"/>
|
||||||
|
<text x="530" y="567" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Frontend / UI</text>
|
||||||
|
|
||||||
|
<rect x="510" y="576" width="14" height="10" rx="2" fill="#ffffff" stroke="#0b0d0b" stroke-width="1"/>
|
||||||
|
<text x="530" y="585" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Backend / API</text>
|
||||||
|
|
||||||
|
<rect x="510" y="594" width="14" height="10" rx="2" fill="rgba(11,13,11,0.03)" stroke="rgba(11,13,11,0.30)" stroke-width="1"/>
|
||||||
|
<text x="530" y="603" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Cloud / Service</text>
|
||||||
|
|
||||||
|
<rect x="510" y="612" width="14" height="10" rx="2" fill="rgba(11,13,11,0.05)" stroke="#52534e" stroke-width="1"/>
|
||||||
|
<text x="530" y="621" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Database</text>
|
||||||
|
|
||||||
|
<rect x="510" y="630" width="14" height="10" rx="2" fill="rgba(82,83,78,0.10)" stroke="#65655c" stroke-width="1"/>
|
||||||
|
<text x="530" y="639" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">External / User</text>
|
||||||
|
|
||||||
|
<!-- Arrow types -->
|
||||||
|
<line x1="510" y1="655" x2="524" y2="655" stroke="#1a70c7" stroke-width="1.2" marker-end="url(#arrow-link)"/>
|
||||||
|
<text x="530" y="659" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">HTTP / API call</text>
|
||||||
|
|
||||||
|
<line x1="510" y1="671" x2="524" y2="671" stroke="#f7591f" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrow-accent)"/>
|
||||||
|
<text x="530" y="675" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Auth flow</text>
|
||||||
|
|
||||||
|
<line x1="510" y1="687" x2="524" y2="687" stroke="#52534e" stroke-width="1.2" marker-end="url(#arrow)"/>
|
||||||
|
<text x="530" y="691" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Internal connection</text>
|
||||||
|
|
||||||
|
<rect x="510" y="698" width="14" height="10" rx="2" fill="transparent" stroke="rgba(247,89,31,0.45)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
<text x="530" y="707" fill="#52534e" font-size="9" font-family="'Geist', sans-serif">Security group</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Info Cards -->
|
||||||
|
<div class="cards">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot coral"></span>
|
||||||
|
<h3>Card Title 1</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Item one</li>
|
||||||
|
<li>Item two</li>
|
||||||
|
<li>Item three</li>
|
||||||
|
<li>Item four</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot ink"></span>
|
||||||
|
<h3>Card Title 2</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Item one</li>
|
||||||
|
<li>Item two</li>
|
||||||
|
<li>Item three</li>
|
||||||
|
<li>Item four</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-dot muted"></span>
|
||||||
|
<h3>Card Title 3</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Item one</li>
|
||||||
|
<li>Item two</li>
|
||||||
|
<li>Item three</li>
|
||||||
|
<li>Item four</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="footer">
|
||||||
|
<span>[Project Name] · Architecture Overview</span>
|
||||||
|
<span>[Date]</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Diagram</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
:root {
|
||||||
|
--color-paper: #f5f4ed;
|
||||||
|
--color-ink: #0b0d0b;
|
||||||
|
--color-muted: #52534e;
|
||||||
|
--color-accent: #f7591f;
|
||||||
|
--font-sans: 'Geist', system-ui, sans-serif;
|
||||||
|
--font-serif: 'Instrument Serif', serif;
|
||||||
|
--font-mono: 'Geist Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background: var(--color-paper);
|
||||||
|
color: var(--color-ink);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame { max-width: 1200px; width: 100%; }
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: var(--color-ink);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg { width: 100%; min-width: 900px; display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="frame">
|
||||||
|
<p class="eyebrow">[Type] · Diagram Design</p>
|
||||||
|
<h1>[Diagram title]</h1>
|
||||||
|
|
||||||
|
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#52534e"/></marker>
|
||||||
|
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f7591f"/></marker>
|
||||||
|
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="100%" height="100%" fill="#f5f4ed"/>
|
||||||
|
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
|
||||||
|
|
||||||
|
<!-- Draw arrows first, then nodes. Replace with your content. -->
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,139 @@
|
|||||||
|
# Onboarding — generate your skin from a website
|
||||||
|
|
||||||
|
**Goal:** point the skill at a site you like (your own, a competitor, a reference), and have it extract the palette + typography, then rewrite `style-guide.md` so every future diagram inherits that skin.
|
||||||
|
|
||||||
|
Takes about 60 seconds.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The flow
|
||||||
|
|
||||||
|
```
|
||||||
|
URL you provide
|
||||||
|
↓
|
||||||
|
[1] fetch homepage (headless browser or HTTP)
|
||||||
|
↓
|
||||||
|
[2] extract dominant colors + fonts
|
||||||
|
↓
|
||||||
|
[3] map to semantic roles (paper, ink, muted, accent, …)
|
||||||
|
↓
|
||||||
|
[4] propose a style-guide.md diff
|
||||||
|
↓
|
||||||
|
[5] write the diff (with your approval)
|
||||||
|
↓
|
||||||
|
future diagrams use your tokens
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Invocation
|
||||||
|
|
||||||
|
Ask the skill:
|
||||||
|
|
||||||
|
> *"Onboard diagram-design to my site — `https://example.com`"*
|
||||||
|
|
||||||
|
Or run it with an explicit URL and the skill reads this file and executes the steps below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1 — fetch the page
|
||||||
|
|
||||||
|
Use `agent-browser` (preferred) or a plain `fetch`. If the site has multiple pages worth sampling (landing + blog + product), fetch 2–3 and merge the palette signals.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser navigate https://example.com --screenshot out.png --html out.html
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2 — extract colors and fonts
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
Parse the rendered CSS and screenshot:
|
||||||
|
|
||||||
|
- **Background color** of `<body>` or the dominant large region → `paper`
|
||||||
|
- **Primary text color** (body text) → `ink`
|
||||||
|
- **Secondary text color** (captions, meta) → `muted`
|
||||||
|
- **Most-used brand color** (CTA button, link, heading accent) → `accent`
|
||||||
|
- **Container / card background** slightly darker than paper → `paper-2`
|
||||||
|
- **Border / hairline color** → `rule` (convert to rgba of ink at ~0.12 opacity)
|
||||||
|
|
||||||
|
Prefer CSS custom properties when the site exposes them (`:root { --accent: …; }`). Otherwise pull via rendered `getComputedStyle` samples or a color-histogram pass over the screenshot.
|
||||||
|
|
||||||
|
### Fonts
|
||||||
|
|
||||||
|
Read the rendered `font-family` stack of:
|
||||||
|
- `<h1>` → `title` family
|
||||||
|
- `<body>` → `node-name` family
|
||||||
|
- `<code>`, `<pre>`, or any mono-styled element → `sublabel` family
|
||||||
|
|
||||||
|
If the site has only one family, keep the diagram-design defaults for the missing roles (Instrument Serif for title, Geist Mono for mono). Don't force-pick a mono font that isn't on the site.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3 — map to semantic roles
|
||||||
|
|
||||||
|
Propose a diff by filling this table:
|
||||||
|
|
||||||
|
| Role | Detected | Confidence |
|
||||||
|
|---|---|---|
|
||||||
|
| paper | `#f8f6f0` | high |
|
||||||
|
| ink | `#111111` | high |
|
||||||
|
| muted | `#6b6b68` | medium |
|
||||||
|
| accent | `#c73a2b` | high |
|
||||||
|
| … | … | … |
|
||||||
|
|
||||||
|
Flag low-confidence guesses so the user can correct before applying.
|
||||||
|
|
||||||
|
### Constraint checks
|
||||||
|
|
||||||
|
Before writing, validate:
|
||||||
|
|
||||||
|
- **AA contrast**: `ink` on `paper` ≥ 4.5:1. `muted` on `paper` ≥ 4.5:1 for body text.
|
||||||
|
- **Accent is the most saturated color**: not muted-ish, not near-grey.
|
||||||
|
- **paper ≠ pure white**: if the site uses `#ffffff`, fall back to `#fafaf7` to preserve diagram-design's warm-neutral feel — or ask the user to confirm pure-white is intentional.
|
||||||
|
|
||||||
|
If any check fails, propose an adjusted value and explain why.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4 — preview the diff
|
||||||
|
|
||||||
|
Show the user what will change in `style-guide.md`. Only the tokens table — everything else stays the same.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
-| `paper` | `#f5f4ed` | `#1c1a17` |
|
||||||
|
-| `ink` | `#0b0d0b` | `#f1efe7` |
|
||||||
|
-| `accent` | `#f7591f` | `#ff6a30` |
|
||||||
|
+| `paper` | `#f8f6f0` | `#1a1815` |
|
||||||
|
+| `ink` | `#111111` | `#efeee7` |
|
||||||
|
+| `accent` | `#c73a2b` | `#e05440` |
|
||||||
|
```
|
||||||
|
|
||||||
|
Also regenerate the dark variant via the inversion rule (`rgba(11,13,11, X)` → `rgba(ink-rgb, X)`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5 — apply
|
||||||
|
|
||||||
|
Write the new tokens to `style-guide.md`. Suggest running the `/regenerate-examples` flow (if it exists) or rebuilding one example to verify the new skin reads cleanly.
|
||||||
|
|
||||||
|
After onboarding, the user should:
|
||||||
|
1. Open `assets/index.html` (gallery) and confirm the new palette feels coherent across all 13 types.
|
||||||
|
2. If any type looks off, they usually need to tune `muted` (often too dark or too light against the new `paper`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When onboarding fails
|
||||||
|
|
||||||
|
- **Site uses webfonts you can't replicate** (custom-hosted, paid): keep the diagram-design defaults for typography and skin only the colors.
|
||||||
|
- **Brand has 6+ colors** and you can't identify a clear hierarchy: pick one as `accent`, demote the rest to `muted` variants or ignore them. The diagram grammar only uses 5–7 roles.
|
||||||
|
- **Site is dark-mode first**: flip the inversion — treat their dark paper as the default `paper`, and generate a light variant via inversion.
|
||||||
|
- **Homepage is all imagery, no text**: ask for a blog or docs URL instead — text-heavy pages expose the type hierarchy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future: per-project skins
|
||||||
|
|
||||||
|
If the user wants multiple skins (one per project), duplicate `style-guide.md` as `style-guides/<project>.md` and add a header comment pointing the build to the active one. That's a v1.1 feature — for now, one skin per skill install.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Annotation Callout (italic-serif aside)
|
||||||
|
|
||||||
|
Use for editorial asides — the "italic pointer" that marks a detail without competing with the primary diagram grammar. Think marginalia: *"structure IS the index"*, *"no imports, no configuration"*.
|
||||||
|
|
||||||
|
## Grammar
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<!-- 1. Italic Instrument Serif text -->
|
||||||
|
<text x="904" y="36" fill="#0b0d0b" font-size="14" font-style="italic"
|
||||||
|
font-family="'Instrument Serif', serif" text-anchor="end">no imports, no configuration</text>
|
||||||
|
<!-- 2. Dashed Bézier leader -->
|
||||||
|
<path d="M 820 44 Q 700 84 520 216" fill="none"
|
||||||
|
stroke="rgba(11,13,11,0.40)" stroke-width="1" stroke-dasharray="4,3"/>
|
||||||
|
<!-- 3. Landing dot -->
|
||||||
|
<circle cx="520" cy="216" r="2" fill="#0b0d0b"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
- Italic + serif together signal "editorial voice" against the diagram's sans/mono body. Don't substitute italic sans or italic mono — the combination is load-bearing.
|
||||||
|
- Dashed path (`stroke-dasharray="4,3"`) distinguishes the callout leader from primary arrows (which are solid).
|
||||||
|
- Place callouts in margins (top-right, bottom-left). Never inside the active diagram area.
|
||||||
|
- Max 2 callouts per diagram. More becomes commentary, not signal.
|
||||||
|
|
||||||
|
## Colors
|
||||||
|
|
||||||
|
| Intent | Text | Leader |
|
||||||
|
|---|---|---|
|
||||||
|
| Neutral aside | ink `#0b0d0b` | `rgba(11,13,11,0.40)` |
|
||||||
|
| Focal / accent | coral `#f7591f` | `rgba(247,89,31,0.50)` |
|
||||||
|
| Tertiary (muted) | muted `#52534e` | `rgba(11,13,11,0.30)` |
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Solid arrow leader (reads as a flow arrow).
|
||||||
|
- Italic sans or italic mono — the serif is load-bearing.
|
||||||
|
- Callouts crossing primary arrows / lifelines — offset to a clear margin.
|
||||||
|
- Using a callout to label something the diagram should label directly — put the label on the element.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Sketchy Filter (hand-drawn variant)
|
||||||
|
|
||||||
|
Optional displacement filter that wobbles every stroke and edge slightly — turns any minimal variant into a hand-drawn "editorial" register without changing layout. Use when the diagram accompanies an essay rather than technical docs.
|
||||||
|
|
||||||
|
## Grammar
|
||||||
|
|
||||||
|
```svg
|
||||||
|
<defs>
|
||||||
|
<filter id="sketchy" x="-2%" y="-2%" width="104%" height="104%">
|
||||||
|
<feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="2" seed="4"/>
|
||||||
|
<feDisplacementMap in="SourceGraphic" scale="1.5"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Apply to a group wrapping shapes — NOT text -->
|
||||||
|
<g filter="url(#sketchy)">
|
||||||
|
<!-- rects, paths, circles, lines go here -->
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Text sits OUTSIDE the filtered group — legibility stays crisp -->
|
||||||
|
<text ...>Labels go here</text>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tuning
|
||||||
|
|
||||||
|
| Parameter | Range | Effect |
|
||||||
|
|---|---|---|
|
||||||
|
| `baseFrequency` | 0.01–0.04 | Lower = lazy wavy lines; higher = jittery. 0.02 default. |
|
||||||
|
| `numOctaves` | 1–3 | More = more noise detail. 2 is plenty. |
|
||||||
|
| `scale` | 1–6 | 1 barely-there, 1.5 default, 2 visible, 4+ cartoon. |
|
||||||
|
| `seed` | integer | Swap for a different random pattern. |
|
||||||
|
|
||||||
|
## Critical rule
|
||||||
|
Filter shapes, NOT text. Displacement-mapped text becomes illegible. Structure your SVG so text is in a sibling group outside the filtered group.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
- Essay / blog post / newsletter where the diagram is the hero of a narrative page.
|
||||||
|
- "Working sketch" register — showing something is mid-thought, not final architecture.
|
||||||
|
|
||||||
|
## When not to use
|
||||||
|
- Technical documentation (precision matters).
|
||||||
|
- Diagrams with dense labels or tight alignments (filter reads as noise).
|
||||||
|
- Dark variants — wobble reads as artifact on dark backgrounds. Test first.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Style Guide
|
||||||
|
|
||||||
|
**The single source of truth for colors, typography, and tokens.** Every diagram draws from this — not from hex values inlined in other reference files. If you want to change the visual skin of diagram-design, change this file.
|
||||||
|
|
||||||
|
Default skin is a neutral editorial palette — warm stone paper, deep charcoal ink, rust accent. It's designed to look good out of the box for any open-source user without matching any specific brand. Swap these values (or run [`onboarding.md`](onboarding.md)) and every new diagram inherits the new skin without touching any type-specific logic.
|
||||||
|
|
||||||
|
To generate your own from a website URL, see [`onboarding.md`](onboarding.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tokens
|
||||||
|
|
||||||
|
### Semantic roles
|
||||||
|
|
||||||
|
Every token is referred to by **semantic role**, not by its hex value. Type references (`type-*.md`) and SKILL.md say `accent`, not `#f7591f`.
|
||||||
|
|
||||||
|
| Role | Purpose | Default (light) | Default (dark) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `paper` | Page background, default node fill | `#faf7f2` | `#1c1917` |
|
||||||
|
| `paper-2` | Diagram container bg, secondary fill | `#f2ede4` | `#292524` |
|
||||||
|
| `ink` | Primary text, primary stroke | `#1c1917` | `#faf7f2` |
|
||||||
|
| `muted` | Secondary text, default arrow stroke | `#57534e` | `#a8a29e` |
|
||||||
|
| `soft` | Sublabels, boundary labels | `#78716c` | `#8e8680` |
|
||||||
|
| `rule` | Hairline borders | `rgba(28,25,23,0.12)` | `rgba(250,247,242,0.12)` |
|
||||||
|
| `rule-solid` | Stronger borders, baselines | `rgba(120,113,108,0.25)` | `rgba(168,162,158,0.25)` |
|
||||||
|
| `accent` | Focal / 1–2 max per diagram | `#b5523a` | `#d6724a` |
|
||||||
|
| `accent-tint` | Fill for accent-bordered boxes | `rgba(181,82,58,0.08)` | `rgba(214,114,74,0.10)` |
|
||||||
|
| `link` | HTTP/API calls, external arrows | `#2563eb` | `#60a5fa` |
|
||||||
|
|
||||||
|
> **Note:** The pre-baked example HTML files in `assets/` were built under an earlier skin. Regenerating them against the current `style-guide.md` is a v1.1 task. New diagrams the skill produces will use the tokens above.
|
||||||
|
|
||||||
|
### Inversion rule (light → dark)
|
||||||
|
|
||||||
|
Any `rgba(28,25,23, X)` in light becomes `rgba(250,247,242, X)` in dark. Same opacities, RGB flipped. The accent gets a slight hue-shift brighter to read on dark paper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
| Role | Family | Size | Weight | Usage |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `title` | Instrument Serif | 1.75rem | 400 | Page H1 |
|
||||||
|
| `node-name` | Geist (sans) | 12px | 600 | Human-readable labels |
|
||||||
|
| `sublabel` | Geist Mono | 9px | 400 | Port, protocol, URL, field type |
|
||||||
|
| `eyebrow` | Geist Mono | 7–8px | 500, tracked 0.18em, uppercase | Type tags, axis labels |
|
||||||
|
| `arrow-label` | Geist Mono | 8px | 400, tracked 0.06em | Arrow annotations |
|
||||||
|
| `callout` | Instrument Serif *italic* | 14px | 400 | Editorial asides only |
|
||||||
|
|
||||||
|
### Font stack
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
```
|
||||||
|
|
||||||
|
**Load-bearing rule:** Mono is for *technical* content (ports, commands, URLs, field types). Names go in Geist sans. Page title is Instrument Serif. Italic Instrument Serif is reserved for annotation callouts (see [primitive-annotation.md](primitive-annotation.md)). **Never JetBrains Mono** as a blanket "dev" font.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stroke, radius, spacing
|
||||||
|
|
||||||
|
| Token | Value | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `stroke-thin` | `0.8` | Tag-box outlines, leaf nodes |
|
||||||
|
| `stroke-default` | `1` | Most strokes |
|
||||||
|
| `stroke-strong` | `1.2` | Emphasis strokes |
|
||||||
|
| `radius-sm` | `4` | Small tags |
|
||||||
|
| `radius-md` | `6` | Node boxes |
|
||||||
|
| `radius-lg` | `8` | Containers, rings |
|
||||||
|
| `grid` | `4` | Every coord, size, and gap is divisible by 4 (hard rule) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Node type → treatment
|
||||||
|
|
||||||
|
Semantic role combinations — reference these by name in type specs.
|
||||||
|
|
||||||
|
| Type | Fill | Stroke |
|
||||||
|
|---|---|---|
|
||||||
|
| `focal` (1–2 max) | `accent-tint` | `accent` |
|
||||||
|
| `backend` | `#ffffff` (white) | `ink` |
|
||||||
|
| `store` | `ink @ 0.05` | `muted` |
|
||||||
|
| `external` | `ink @ 0.03` | `ink @ 0.30` |
|
||||||
|
| `input` | `muted @ 0.10` | `soft` |
|
||||||
|
| `optional` | `ink @ 0.02` | `ink @ 0.20` dashed `4,3` |
|
||||||
|
| `security` | `accent @ 0.05` | `accent @ 0.50` dashed `4,4` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Customizing the skin
|
||||||
|
|
||||||
|
Three options:
|
||||||
|
|
||||||
|
1. **Run onboarding** — see [`onboarding.md`](onboarding.md). Drop a URL; the skill extracts the palette + fonts and rewrites this file.
|
||||||
|
2. **Edit by hand** — change the hex values in the tables above. Run the pre-output taste gate afterward to verify the accent still reads as "focal" against the new paper color.
|
||||||
|
3. **Brand handoff** — paste your existing design-token JSON into a new section here and map its tokens to the semantic roles above.
|
||||||
|
|
||||||
|
### Constraints (don't break these)
|
||||||
|
|
||||||
|
- **Contrast**: `ink` must hit WCAG AA on `paper`. `muted` must hit AA on `paper` for 11px+ text.
|
||||||
|
- **One accent**: pick one color for `accent`. Two accents erases the focal signal.
|
||||||
|
- **No rainbow palette**: if your brand ships 8 colors, pick 3 (paper, ink, accent). The rest become `muted` variants.
|
||||||
|
- **Serif + sans + mono**: three families, not more. If brand typography is all sans, keep Instrument Serif for `title` and `callout` anyway — the contrast is load-bearing.
|
||||||
|
- **Paper is warm-neutral, not pure white**: pure white turns the design sterile. Pick a cream, bone, or light grey with a hint of warmth.
|
||||||
|
- **Dot pattern survives**: the 22×22 dot pattern on the diagram background needs to sit at ~10% opacity of `ink` on `paper` — verify it's visible but quiet after a skin change.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
**Best for:** system overviews, data-flow diagrams, integration maps, infra topology.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Group components by tier or trust boundary (frontend → backend → data; public → private).
|
||||||
|
- Primary flow runs left→right or top→down. Pick one and hold it.
|
||||||
|
- Draw arrows before boxes so z-order puts connections behind components.
|
||||||
|
- 1–2 coral focal nodes: the primary integration point, the primary data store, or the key decision node.
|
||||||
|
- Dashed boundary rectangles mark regions (VPC, security group, trust zone); labels sit on a paper-colored mask over the boundary line.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Every box in coral ("this is important too") — hierarchy collapses.
|
||||||
|
- Bidirectional arrow when one direction is obvious from context.
|
||||||
|
- Legend floating inside the diagram area.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-architecture.html` — minimal light
|
||||||
|
- `assets/example-architecture-dark.html` — minimal dark
|
||||||
|
- `assets/example-architecture-full.html` — full editorial
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# ER / Data Model
|
||||||
|
|
||||||
|
**Best for:** database schemas, API resource relationships, domain models.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Each entity is a two-section box:
|
||||||
|
- **Header**: type tag (`ENTITY`) + entity name in Geist.
|
||||||
|
- **Body**: field list in Geist Mono, one per line. PK prefixed with `#`, FK prefixed with `→`.
|
||||||
|
- Relationships: lines between entities with cardinality at each end:
|
||||||
|
- `1`, `N`, `0..1`, `1..*` in Geist Mono, 8px, placed 10–12px from the entity edge.
|
||||||
|
- Optional relationship label ("has", "belongs to") centered on the line.
|
||||||
|
- Group related entities close; lay out so most relationships are straight lines, not tangles.
|
||||||
|
- Coral on the aggregate root or central entity of the model.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Drawing an arrow for every FK on a model with dozens — lay out by cluster instead.
|
||||||
|
- Inconsistent cardinality notation between ends of the same relationship.
|
||||||
|
- Fields padded to equal-height boxes — natural height by content is fine.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-er.html` — minimal light
|
||||||
|
- `assets/example-er-dark.html` — minimal dark
|
||||||
|
- `assets/example-er-full.html` — full editorial
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Flowchart
|
||||||
|
|
||||||
|
**Best for:** decision logic, algorithms, user-facing branching flows ("Should I…?"), onboarding routing, support-triage trees.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Shape carries type, not color:
|
||||||
|
- **Oval** (`rx=20`) — start / end
|
||||||
|
- **Rectangle** (`rx=6`) — step / action
|
||||||
|
- **Diamond** — decision (≤3 exits)
|
||||||
|
- **Small filled ink dot** (`r=4`) — merge point where branches rejoin
|
||||||
|
- Flow runs top→down. From a diamond, conventional exits: Yes to the right, No below — but label every outgoing arrow regardless.
|
||||||
|
- Use coral on the happy path *or* on the single most consequential decision — never on every decision.
|
||||||
|
- If two arrows must cross, use a small arc jump on one so the crossing is readable.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Using fill color to signal node type (shape does that).
|
||||||
|
- Decision diamond with 4+ exits — refactor into nested diamonds.
|
||||||
|
- Unlabeled decision branches.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-flowchart.html` — minimal light
|
||||||
|
- `assets/example-flowchart-dark.html` — minimal dark
|
||||||
|
- `assets/example-flowchart-full.html` — full editorial
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Layer Stack
|
||||||
|
|
||||||
|
**Best for:** OSI model, CSS cascade, context hierarchy, tech stack, abstraction layers, memory hierarchy.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Horizontal bands stacked vertically. Each layer is a full-width rectangle (same x, same width). 4–6 layers total.
|
||||||
|
- Layer height 56–72px, width typically 800–880px inside a 1000px viewBox.
|
||||||
|
- Each row contains (left→right):
|
||||||
|
1. **Index tag** on the far left (`L3`, `07`, `APPLICATION`) — Geist Mono 8–9px eyebrow.
|
||||||
|
2. **Layer name** slightly right of center-left — Geist 14–16px 600.
|
||||||
|
3. **Sublabel / note** on the far right — Geist Mono 9–10px muted.
|
||||||
|
- Border between layers: 1px hairline `rgba(11,13,11,0.12)`. Outer silhouette 1px ink or muted.
|
||||||
|
- Fills: either alternating subtle shades (paper / paper-2) OR all paper with hairline dividers. Pick one and hold it.
|
||||||
|
- Direction indicator on the LEFT margin (outside the stack): small up/down arrow + Geist Mono label (`abstraction ↑`, `packets ↓`).
|
||||||
|
- Coral on **one** focal layer (stroke + subtle tint fill) — the bottleneck, the pay-rent layer, the one under discussion.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Layers that aren't actually hierarchical (use swimlane or architecture).
|
||||||
|
- Skipped numbering (missing L4 between L3 and L5 without explanation).
|
||||||
|
- Every layer a different color — hierarchy invisible.
|
||||||
|
- Inconsistent layer heights without reason.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-layers.html` — minimal light
|
||||||
|
- `assets/example-layers-dark.html` — minimal dark
|
||||||
|
- `assets/example-layers-full.html` — full editorial
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Nested Containment
|
||||||
|
|
||||||
|
**Best for:** hierarchy through containment — scope boundaries, CLAUDE.md cascade, trust zones, folder nesting, blast radius. Outer = broader, inner = more specific.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- 3–5 rounded rectangles (`rx=8`), nested with consistent inset padding (24–32px horizontal, 32–36px vertical recommended).
|
||||||
|
- Each level labeled at the top-left in Geist Mono eyebrow style (7–8px, letter-spacing 0.14em). Labels sit on a paper-colored mask rect over the ring's top border.
|
||||||
|
- Stroke hierarchy: outer rings faint (`rgba(..,0.30–0.45)`), progressing to muted, to ink, to coral at the innermost focal.
|
||||||
|
- Fills step up in opacity from outer to inner: `rgba(..,0.015)` → `rgba(..,0.025)` → accent-tint on the innermost.
|
||||||
|
- Optional file-icon glyph (folded-corner rect) inside each level hints at scope content.
|
||||||
|
- Italic Instrument Serif callouts (see `references/primitive-annotation.md`) — 1–2 max.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- More than 6 levels (information disappears inward).
|
||||||
|
- Irregular padding between levels — unaligned nesting looks accidental.
|
||||||
|
- Content inside rings that isn't part of the hierarchy — use a sibling diagram.
|
||||||
|
- Coral on multiple levels — hierarchy collapses.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-nested.html` — minimal light
|
||||||
|
- `assets/example-nested-dark.html` — minimal dark
|
||||||
|
- `assets/example-nested-full.html` — full editorial
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Pyramid / Funnel
|
||||||
|
|
||||||
|
**Best for:** hierarchy of needs, prioritization ranks, value pyramids, conversion funnels, content importance stacks.
|
||||||
|
|
||||||
|
## Two orientations — pick one
|
||||||
|
- **Pyramid** (point up) — narrow apex = most important / rarest / most valuable. Base is broadest / foundational.
|
||||||
|
- **Funnel** (point down) — narrow end = conversion (smallest group). Top is widest / audience.
|
||||||
|
|
||||||
|
Don't mix orientations on one diagram.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- 4–6 layers. Each layer is a trapezoid built from an SVG `<polygon>` with 4 points.
|
||||||
|
- Consistent layer height (56–72px).
|
||||||
|
- Widths decrease linearly from base to apex (pyramid) or top to bottom (funnel). When showing real funnel data, widths must be honest (proportional to count/percentage).
|
||||||
|
- Each layer has:
|
||||||
|
- **Name label** centered inside the trapezoid — Geist 12–14px 600.
|
||||||
|
- **Sublabel** below or beside the name — Geist Mono 9–10px.
|
||||||
|
- **Side annotation** (right or left) — optional. For funnels: drop-off percentage here (`−40%`).
|
||||||
|
- Fill: subtle graded tints OR all paper-2 with hairline dividers (cleaner). Pick one.
|
||||||
|
- Stroke: 1px hairline between layers; outer silhouette 1px muted or ink.
|
||||||
|
- **Coral on ONE layer only**: apex of pyramid, conversion layer of funnel, or critical bottleneck.
|
||||||
|
- Optional left-margin axis arrow + Geist Mono label (`rarer ↑`, `drop-off ↓`).
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- 7+ layers (illegible — compress or split).
|
||||||
|
- Pyramid for non-hierarchical data (use a tree or bar chart).
|
||||||
|
- Dishonest widths (fake equal spacing when drops are unequal).
|
||||||
|
- Coral on the base layer (dilutes the "apex = rare" signal).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-pyramid.html` — minimal light
|
||||||
|
- `assets/example-pyramid-dark.html` — minimal dark
|
||||||
|
- `assets/example-pyramid-full.html` — full editorial
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Quadrant
|
||||||
|
|
||||||
|
**Best for:** prioritization (Impact × Effort), positioning (Reach × Frequency), portfolio maps, 2×2 decision frames.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- 2×2 grid. Axis lines: 1px ink cross through the center.
|
||||||
|
- Axis labels at the *ends* of each axis (e.g., `HIGH IMPACT →` on the right, `LOW IMPACT ←` on the left — Geist Mono eyebrow). Never label at the midpoint.
|
||||||
|
- Items: small labeled dots (`r=4`) positioned in the quadrants. Labels 8–10px away; don't let labels cross axis lines.
|
||||||
|
- Coral on the "do first" item (typically top-right).
|
||||||
|
- Limit to ~12 items; cluster or split beyond that.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Four filled quadrants in different colors — position + label does the work; color noise weakens it.
|
||||||
|
- Items placed on axis lines (ambiguous quadrant).
|
||||||
|
- Missing axis names.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-quadrant.html` — minimal light
|
||||||
|
- `assets/example-quadrant-dark.html` — minimal dark
|
||||||
|
- `assets/example-quadrant-full.html` — full editorial
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Sequence
|
||||||
|
|
||||||
|
**Best for:** request/response flows, protocol exchanges, multi-actor interactions over time, API call traces, incident reconstructions.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Actors as boxes in a horizontal row at the top.
|
||||||
|
- **Lifelines**: dashed vertical lines descending from each actor to the bottom.
|
||||||
|
- Messages: horizontal arrows between lifelines; time flows top→down.
|
||||||
|
- **Activation bar**: narrow rectangle (`w=8`, muted fill, 0.8 hairline stroke) on a lifeline spanning the interval that actor holds control. Stack for nested calls.
|
||||||
|
- Self-messages: short U-shaped loop returning to the same lifeline; label right of the loop.
|
||||||
|
- Return messages: dashed line in the same color as the originating call.
|
||||||
|
- Coral on the primary success response or headline message — one, maybe two.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Message arrow pointing *upward* (reverses time — never).
|
||||||
|
- Activation bars that never close.
|
||||||
|
- Labels sitting over another lifeline — shorten or shift y into a gap.
|
||||||
|
- Swimlane-style lanes instead of lifelines (different grammar).
|
||||||
|
|
||||||
|
## Lifeline primitive
|
||||||
|
```svg
|
||||||
|
<line x1="CX" y1="TOP" x2="CX" y2="BOTTOM"
|
||||||
|
stroke="rgba(11,13,11,0.20)" stroke-width="1" stroke-dasharray="3,3"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Activation bar primitive
|
||||||
|
```svg
|
||||||
|
<rect x="CX-4" y="TOP" width="8" height="H"
|
||||||
|
fill="rgba(11,13,11,0.06)" stroke="#52534e" stroke-width="0.8"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-sequence.html` — minimal light
|
||||||
|
- `assets/example-sequence-dark.html` — minimal dark
|
||||||
|
- `assets/example-sequence-full.html` — full editorial
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# State Machine
|
||||||
|
|
||||||
|
**Best for:** finite state logic — order status, auth state, connection lifecycle, form wizard, job queue status.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- States are rounded rectangles (`rx=8`), labeled in Geist.
|
||||||
|
- **Start**: filled ink dot (`r=6`). **End**: ringed dot (outer `r=8` outline, inner filled `r=5`).
|
||||||
|
- Transitions: curved arrows labeled in Geist Mono as `event [guard] / action` (omit sections you don't need).
|
||||||
|
- Self-loops curve above the state.
|
||||||
|
- Orient along the dominant flow direction (left→right or top→down); rearrange before crossing transitions.
|
||||||
|
- Coral on the state the reader should notice — typically the error state, or "happy completion".
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- More transitions than states × 2 → likely two state machines.
|
||||||
|
- "From any state" transitions drawn from every state — use a single annotation (`* → Error on timeout`) instead.
|
||||||
|
- Unlabeled transitions (the whole point is *what triggers this*).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-state.html` — minimal light
|
||||||
|
- `assets/example-state-dark.html` — minimal dark
|
||||||
|
- `assets/example-state-full.html` — full editorial
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Swimlane
|
||||||
|
|
||||||
|
**Best for:** cross-functional processes, RACI-style flows, vendor handoffs, multi-team shipping workflows.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Horizontal lanes (or vertical columns) — one per actor/team. Label each lane in the left margin (or top) with a Geist Mono eyebrow.
|
||||||
|
- Lane dividers: 1px hairlines.
|
||||||
|
- Process steps are rectangles placed inside the lane of the actor performing them; arrows show flow.
|
||||||
|
- Handoffs (arrows crossing lane boundaries) are the most important edges — consider coral on the handoff that introduces the most coupling or latency.
|
||||||
|
- Don't force equal step count per lane; a lane with one step is fine.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Lanes without labels.
|
||||||
|
- A step drawn across two lanes (pick one owner).
|
||||||
|
- Arrows that snake back and forth — reorder steps so the flow is mostly straight.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-swimlane.html` — minimal light
|
||||||
|
- `assets/example-swimlane-dark.html` — minimal dark
|
||||||
|
- `assets/example-swimlane-full.html` — full editorial
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Timeline
|
||||||
|
|
||||||
|
**Best for:** release history, project milestones, incident timelines, roadmaps, changelog visualizations.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Horizontal hairline baseline across the middle (`stroke-width=1`).
|
||||||
|
- Tick marks at time boundaries (quarters, months, sprints) with date labels below in Geist Mono.
|
||||||
|
- Events: small filled circles (`r=4`) on the baseline. Labels alternate above and below to prevent collision, connected to the circle with a 1px hairline drop.
|
||||||
|
- Major milestones: coral circle (`r=6`) + bold Geist label.
|
||||||
|
- Time scale must be honest: if intervals are non-equal, space the circles non-equally. Don't fake linear spacing for aesthetics. Break the axis visibly if a region is too dense.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Equal-spacing events that aren't equally spaced in time.
|
||||||
|
- Missing axis labels ("what unit is this?").
|
||||||
|
- Crowded labels without vertical offset — illegible.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-timeline.html` — minimal light
|
||||||
|
- `assets/example-timeline-dark.html` — minimal dark
|
||||||
|
- `assets/example-timeline-full.html` — full editorial
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Tree / Hierarchy
|
||||||
|
|
||||||
|
**Best for:** org charts, dependency trees, taxonomy, file trees, decision breakdowns, skill trees.
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- Root at top, children fan out below (or root at left, children to right).
|
||||||
|
- Nodes are small labeled rectangles (`rx=6`), Geist 12px 600 name + optional Geist Mono 9px sublabel. Width 120–180px, height 40–52px.
|
||||||
|
- **Connectors are orthogonal (elbow-style), never diagonal.** Parent drops a short vertical line, then a horizontal bus connects siblings, then each child has a short vertical drop into its top edge. 1px muted stroke.
|
||||||
|
- Leaf indicator: thinner stroke (0.8) or different fill — OR let terminal position do the work.
|
||||||
|
- Max depth: 4 (root + 3 tiers). Max breadth per level: 5.
|
||||||
|
- Coral on **one** node: root OR critical leaf. Not both.
|
||||||
|
- Draw connectors before nodes.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Tree 5+ levels deep on a single page (illegible — split).
|
||||||
|
- Nodes of wildly varying widths — pick 2 widths max.
|
||||||
|
- Diagonal connector lines.
|
||||||
|
- Skipped levels (parent connected to grandchild with no middle).
|
||||||
|
- Coral on root AND a leaf.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-tree.html` — minimal light
|
||||||
|
- `assets/example-tree-dark.html` — minimal dark
|
||||||
|
- `assets/example-tree-full.html` — full editorial
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Venn / Set Overlap
|
||||||
|
|
||||||
|
**Best for:** intersection of concepts/domains, shared attributes between categories, "where A meets B", ikigai-style frames (desirable × feasible × viable).
|
||||||
|
|
||||||
|
## Layout conventions
|
||||||
|
- **Prefer 2 or 3 circles.** Avoid 4+ (unreadable — use a matrix instead).
|
||||||
|
- Circle stroke: 1px hairline, color per-set (ink, muted, soft).
|
||||||
|
- Circle fill: very low-opacity tint — `rgba(11,13,11,0.04)` for ink set, `rgba(82,83,78,0.05)` for muted. Tints compound naturally in overlap regions.
|
||||||
|
- Radii: equal when sets are comparable in size; proportional when sets are meaningfully different. Don't fake equal sizes for aesthetics.
|
||||||
|
- **Set labels** placed outside the circle, NEVER crossing the stroke. Geist 12–14px 600 for the set name, optional Geist Mono 9px sublabel.
|
||||||
|
- **Intersection labels** placed inside the overlap region, Geist 12px 600, centered. For small overlaps, use a leader line to a label in clear space.
|
||||||
|
- **Coral accent** on the ONE focal intersection — the "sweet spot". Either coral label stroke OR clipPath-bounded coral fill tint (`rgba(247,89,31,0.10)`).
|
||||||
|
- Circle centers and radii divisible by 4.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
- Unlabeled regions — reader can't tell which set is which.
|
||||||
|
- Circles that don't overlap when overlap is the point.
|
||||||
|
- Equal-sized circles when sets are obviously different (dishonest).
|
||||||
|
- Coral on multiple overlap regions (focal signal dies).
|
||||||
|
- Labels sitting on top of circle strokes (illegible).
|
||||||
|
- 4+ circles where 2–3 would do.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
- `assets/example-venn.html` — minimal light
|
||||||
|
- `assets/example-venn-dark.html` — minimal dark
|
||||||
|
- `assets/example-venn-full.html` — full editorial
|
||||||