Every visible thing on this site has a checkable contract behind it.
A colophon is where a book tells you what it's made of. This one tells you the same — plus how it's tested, what it refuses to do, and which trade-offs are the load-bearing ones.
Honestly? I built this page because I got tired of designers (myself included) saying “our system is great” without a way to prove it. A claim you can't check is just marketing.
The stack, in one table
| Layer | Choice | Why this, not that |
|---|---|---|
| Markup | Static HTML | No build step. Frameworks rewrite themselves every ~3 years. HTML doesn't. I picked the longer half-life on purpose. |
| Styling | CSS Cascade Layers + tokens | Six layers in declared order — reset, tokens, base, components, utilities, overrides. No specificity wars. No !important. Ever. |
| Tokens | W3C DTCG 2025.10 schema | Three tiers: primitive → semantic → component. Components can't skip levels. The build script enforces it. |
| Color | OKLCH everywhere | Same lightness number means the same visual weight, no matter the hue. HEX and HSL would lie about contrast — I'd rather not be lied to. |
| Theme | light-dark() function |
One declaration per token. color-scheme: light dark follows the OS. [data-theme] overrides explicitly. Half the lines of the old dual-block setup. |
| Typography | Instrument Serif · Geist · Geist Mono | Fluid scale via clamp() with viewport units in the middle term. Nine steps, no surprises between phone and desktop. |
| Motion | Tokenised duration + easing | Three durations (fast / base / slow), eight named easings. prefers-reduced-motion respected at the token layer, not patched in per component. |
| Navigation | Cross-document View Transitions + Speculation Rules | Same-origin links prerender on hover. Transitions are native. SPA feel, MPA bones. Best of both. |
| Hosting | Netlify (for now) | Deploys in 30 seconds. I'll move to Cloudflare Pages soon — better carbon footprint, unlimited bandwidth. |
| Analytics | None — yet | If anything, Plausible self-hosted. No cookie banner. Numbers aren't the point of this site. |
What CI checks before any change ships
Every push runs eight checks. Any one of them failing blocks the merge. No exceptions, no “just this once.”
- tokens:check JSON shape, references, layer direction, color format, fluid type validity. If the token source and the generated CSS drift, the build fails.
-
ds:audit:check
Scans every HTML file (including inline
style="") for hardcoded colors, font-sizes, spacing, radius, motion. Existing debt is grandfathered. New violations fail. -
ds:contrast:check
Resolves every
tokenPairingsin the MCP manifest. Computes WCAG contrast from OKLCH. Fails if any pair drops below its declared target. Caught a real 3.30:1 violation on the success token last week. -
ds:layers:check
Every component's
<style>block must be wrapped in@layer ds.components.{primitives|composites}. Composites must declarecontainer-type. - ds:metrics:check Token coverage per category — color, type, spacing, radius, motion — has to stay above per-page minimums. Regressions fail.
- Lighthouse CI LCP < 1.2s. INP < 100ms. CLS < 0.05. Performance, a11y, SEO, best-practices each ≥ 0.95.
- Pa11y WCAG 2.2 AA on every published page. Errors block the merge.
- site:check Sitemap, RSS feed, and llms.txt are regenerated from page metadata. Drift fails the build.
Things I'm willing to defend
Make the manifest mean something
A design system that declares rules but doesn't enforce them is theatre. Every claim in my MCP manifest — token pairings, forbidden patterns, layer contracts — has a CI script behind it. Closing that gap was the single most useful day of work I did this year.
Hardcoded values are a tell
A loose border-radius: 8px means the writer didn't know
which token to reach for, or didn't trust the system to have one.
Either way, that's a system bug, not a styling bug. Coverage
metrics make that bug visible. You can't fix what you can't see.
Web standards over framework conveniences
Container queries, :has(), light-dark(),
view transitions, anchor positioning, popover — all
native, all stable, all here in five years. A framework can give me
one of these in a custom shape. The platform gives me all of them in
a shape that doesn't need maintaining.
Machine-readable is the new accessibility
llms.txt. Schema.org JSON-LD. An Atom feed. A published
MCP manifest. Source markdown linked from each essay. Built so an
LLM, a search crawler, a screen reader, and a person with low
bandwidth all read the same intent. This is what 2026 looks like.
People who shaped how I think
Sara Soueidan on rigour about contrast and tokens. Maggie Appleton on digital gardens. Robin Rendle on personal websites being the soul of the web. Jeremy Keith on indieweb pragmatism. Rauno Freiberg on interaction details. Lynn Fisher on yearly redesigns. Jakub Krehel and Emil Kowalski on typographic instinct. None of them wrote this site. All of them made it better.
Source code: github.com/daniasyrofi/portfolio. Code is MIT. Writing is CC BY-NC 4.0. Brand is mine. The full text is in LICENSE.
If you read this far and want to ask about anything specific — hello@daniasyrofi.com. I read everything. I reply to specifics.