Context
Before shipping any content, the site needed a visual language. I had strong opinions on what that should be:
- Minimal: No hero sections, skills grids, or decoration. Content first.
- Monochromatic: Grayscale foundation with semantic color for state (healthy/warning/error).
- System fonts: No Google Fonts requests. Native typefaces for speed.
- Token-based: All values (colors, spacing, sizes) as CSS variables, never inline.
- Light-first: CSS defaults to light mode; dark mode is a media query remap.
The goal wasn’t to look “modern”—it was to look clear, read well, and avoid the overhead of a CSS framework.
Options considered
-
Use a CSS framework (Tailwind, Open Props, Pico CSS)
- Pros: Faster setup, pre-built components
- Cons: Adds overhead, forces decisions I didn’t want, cargo-cult classes
-
Roll custom CSS with a design system
- Pros: Full control, minimal footprint, teaches constraints
- Cons: Requires upfront documentation, more thought
-
Use styled-components or CSS-in-JS
- Pros: Component-scoped styles
- Cons: JavaScript overhead, overkill for a static site
I chose option 2: custom CSS + documented design system.
What I chose and why
A token-based design system documented in three files:
DESIGN.md— Principles, color palette, typography scale, component patterns. Read before any style change.src/styles/tokens.css— All values as CSS custom properties. Light mode defaults, dark mode via@media (prefers-color-scheme: dark).src/styles/global.css— Reset and base element styles.
The color palette
Light mode uses warm grays that don’t strain the eyes:
- Text:
#1a1a1a(very dark gray, not pure black) - Backgrounds:
#ffffff,#f8f8f8,#f0f0f0(graduated levels) - Borders:
#e0e0e0(subtle dividers) - Semantic: Green (
#2d7a4a), amber (#b8860b), red (#a94444), blue (#4a6fa5)
Dark mode inverts these by remapping the same CSS variables—no duplicate CSS, just different values.
Typography
System font stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, ...
Font sizes are fluid using clamp():
--font-base: clamp(1rem, 1.4vw, 1.125rem);
This scales smoothly from mobile to desktop without media queries.
Layout
A simple Layout.astro component provides:
- Header with nav (site title + links to Home, About, Colophon)
- Main content area
- Footer with contact info and links
Max-width of 900px keeps text readable. Responsive by default via flexbox.
What I delegated
I asked Claude to:
- Create DESIGN.md with complete design system documentation
- Create DESIGN.md with complete design system documentation
- Create and implement tokens.css with CSS custom properties
- Create and implement global.css with base element styles
- Build Layout.astro with header, nav, and footer
- Style all three pages (home, about, colophon)
- Update the home page with proper semantic structure
I did not ask Claude to make design decisions—I specified the constraints and let Claude implement them.
How I verified
- Dev server running — No build errors,
http://localhost:4321responds - Visual inspection — Opened the site in light and dark modes, checked readability
- Contrast ratios — All text meets WCAG AA (4.5:1 minimum)
- Responsive — Tested mobile width (320px) and desktop; nav stacks correctly
- Links and focus states — All interactive elements have visible focus outlines
- No external requests —
curl localhost:4321shows zero Google Fonts requests
What I’d revisit
- Font sizes on mobile: Currently using
clamp()to scale fluidly, but I haven’t user-tested the readability on a real iPhone yet. - Color contrast on semantic colors: The warning color (
#b8860b) is borderline on contrast. Might need to darken it if accessibility tests fail. - Print styles: Not implemented yet. Could be useful for archival.
- Code block styling: Currently uses
--color-bg-secondarybackground. Might want syntax highlighting if I add a lot of code snippets.