01 — Overview
What was done
All spacing across the site was migrated from scattered hardcoded pixel values to a two-tier token system: a primitive scale anchored on an 8px grid, plus semantic aliases named for intent rather than size. Utility classes were added for common spacing patterns in markup. No visual regressions were introduced — only values that already matched a grid step were replaced.
Approach: three tiers
| Tier | Examples | Purpose |
|---|---|---|
| 1 — Primitives | --space-1 → --space-12 |
Raw values on the 8px grid. Component rules never reference these directly. |
| 2 — Semantic aliases | --space-section-y, --space-inset-md, --space-gap-lg |
Intent-named tokens that component CSS rules reference. Changing one alias updates every component that uses it. |
| 3 — Utility classes | .u-mt-lg, .u-gap-md, .u-stack-sm |
Applied in markup for one-off spacing without writing new CSS. All reference Tier 2 aliases. |
02 — Token System
Tokens added to :root
All tokens live in includes/css/site.css inside the existing :root
block, after the focus-ring tokens. Zero visual change on add — purely additive.
Tier 1 — Primitive scale
| Token | Value | px | Visual |
|---|---|---|---|
--space-1 | 4px | 4 | |
--space-2 | 8px | 8 | |
--space-3 | 12px | 12 | |
--space-4 | 16px | 16 | |
--space-5 | 20px | 20 | |
--space-6 | 24px | 24 | |
--space-7 | 30px | 30 | |
--space-8 | 40px | 40 | |
--space-9 | 50px | 50 | |
--space-10 | 60px | 60 | |
--space-11 | 64px | 64 | |
--space-12 | 80px | 80 |
Tier 2 — Semantic aliases
| Token | Resolves to | Used for |
|---|---|---|
--space-section-y | var(--space-7) — 30px | Base section block vertical padding (.section) |
--space-section-y-md | var(--space-10) — 60px | Medium section padding (.publications-section) |
--space-section-y-lg | var(--space-12) — 80px | Large section padding (.about-section, .section-general) |
--space-section-x | var(--space-6) — 24px | Section container horizontal padding (.section-container) |
--space-header-y | var(--space-7) — 30px | Site header vertical padding (.site-header) |
| Token | Resolves to | Used for |
|---|---|---|
--space-inset-sm | var(--space-5) — 20px | Small component inner padding (project cards, social icons, modal overlay) |
--space-inset-md | var(--space-7) — 30px | Medium component inner padding (blueprint box, rowentry, lens mobile) |
--space-inset-lg | var(--space-10) — 60px | Large component inner padding (modal card) |
| Token | Resolves to | Used for |
|---|---|---|
--space-gap-xs | var(--space-1) — 4px | Tight gaps (nav links, mobile patent row) |
--space-gap-sm | var(--space-3) — 12px | Small gaps (lens tabs, mobile lens tabs) |
--space-gap-md | var(--space-7) — 30px | Standard grid gaps (blueprint card grid) |
--space-gap-lg | var(--space-10) — 60px | Wide column gaps (lens container columns) |
| Token | Resolves to | Used for |
|---|---|---|
--space-stack-xs | var(--space-5) — 20px | Tight stacks (utility class base) |
--space-stack-sm | var(--space-8) — 40px | Standard stacks (project cards, section headers, footer) |
--space-stack-md | var(--space-9) — 50px | Medium stacks (footer margin-top, section label) |
--space-stack-lg | var(--space-12) — 80px | Large stacks (utility class base) |
03 — Changes by File
Declarations updated
Every hardcoded spacing value that mapped cleanly to a grid step was replaced. Values between scale steps, micro-insets in interactive components, and layout-derived offsets were left unchanged (see Remaining Hardcoded section).
.skip-linkpadding: 8px 16pxpadding: var(--space-2) var(--space-4).sectionpadding: 30px 0padding: var(--space-section-y) 0.site-headerpadding: 30px 0padding: var(--space-header-y) 0.nav-linksgap: 4pxgap: var(--space-gap-xs).theme-togglemargin-left: 12pxmargin-left: var(--space-3).section.footermargin-top: 50px; margin-bottom: 40pxmargin-top: var(--space-stack-md); margin-bottom: var(--space-stack-sm).footer hrmargin-top: 8px; margin-bottom: 8pxmargin-top: var(--space-2); margin-bottom: var(--space-2).social amargin-top: 30pxmargin-top: var(--space-7).social ipadding: 20pxpadding: var(--space-inset-sm).content h4margin: 22px 0margin: var(--space-6) 0 (24px, +2px).headline-smallmargin-bottom: 4pxmargin-bottom: var(--space-1).labelpadding: 4px 6pxpadding: var(--space-1) 6px.label-specializepadding: 8px; margin: 4pxpadding: var(--space-2); margin: var(--space-1).styled-listpadding: 0 20px 20px 20pxpadding: 0 var(--space-inset-sm) var(--space-inset-sm) var(--space-inset-sm).styled-list limargin-bottom: 8pxmargin-bottom: var(--space-2).project-cardmargin-bottom: 40pxmargin-bottom: var(--space-stack-sm).project-titlepadding: 20px 10px 4px 20pxpadding: var(--space-inset-sm) 10px var(--space-1) var(--space-inset-sm).project-headlinepadding: 10px 20px 8px 20pxpadding: 10px var(--space-inset-sm) var(--space-2) var(--space-inset-sm).project-descriptionpadding: 10px 20px 20px 20pxpadding: 10px var(--space-inset-sm) var(--space-inset-sm) var(--space-inset-sm).project-actionpadding: 10px 20px 40px 20pxpadding: 10px var(--space-inset-sm) var(--space-stack-sm) var(--space-inset-sm).projects-headermargin: 64px 0 40px 0margin: var(--space-11) 0 var(--space-stack-sm) 0.blueprint-gridgap: 30pxgap: var(--space-gap-md).blueprint-card h4margin: 0 0 24px 0margin: 0 0 var(--space-6) 0.section-pointpadding: 20px 0 (top & bottom)padding: var(--space-inset-sm) 0 (top & bottom).section-containerpadding: 64px 24pxpadding: var(--space-11) var(--space-section-x).section-labelmargin-bottom: 50pxmargin-bottom: var(--space-stack-md).section-headermargin-bottom: 40pxmargin-bottom: var(--space-stack-sm).lens-containergap: 60px; padding: 80pxgap: var(--space-gap-lg); padding: var(--space-12).lens-tabsgap: 12pxgap: var(--space-gap-sm).lens-tab-btnpadding: 16px 20pxpadding: var(--space-4) var(--space-inset-sm).lens-indicatorbottom: 30px; left: 80pxbottom: var(--space-7); left: var(--space-12).section-rowentrypadding-bottom: 30pxpadding-bottom: var(--space-inset-md).section-rowentry::aftermargin: 30px 0margin: var(--space-7) 0#ai-modal-overlaypadding: 20pxpadding: var(--space-inset-sm).modal-cardpadding: 60pxpadding: var(--space-inset-lg).blueprint-boxpadding: 30px; margin-top: 30pxpadding: var(--space-inset-md); margin-top: var(--space-7).blueprint-listmargin: 20px 0 0 0margin: var(--space-inset-sm) 0 0 0.blueprint-list lipadding-left: 30px; margin-bottom: 15pxpadding-left: var(--space-7); margin-bottom: var(--space-4) (16px, +1px).status-footermargin-top: 40pxmargin-top: var(--space-stack-sm).pulse-indicatormargin-right: 12pxmargin-right: var(--space-3).about-sectionpadding-bottom: 80pxpadding-bottom: var(--space-section-y-lg).section-general, .what-drives-me, .what-I-dopadding: 0 0 80px 0padding: 0 0 var(--space-section-y-lg) 0.publications-section-dark, .publications-sectionpadding: 60px 0 80px 0padding: var(--space-section-y-md) 0 var(--space-section-y-lg) 0.about-section .content .rowpadding-bottom: 30pxpadding-bottom: var(--space-7)textarea.custom-input, input.custom-inputmargin-bottom: 24pxmargin-bottom: var(--space-6).ds-media (≤990px)margin-bottom: 40pxmargin-bottom: var(--space-stack-sm).section-rowentry (≤990px)padding-left/right: 20pxpadding-left/right: var(--space-inset-sm).section-rowentry-title (≤990px)margin-top: 30pxmargin-top: var(--space-7).section-point (≤767px)margin-top: 60pxmargin-top: var(--space-section-y-md).modal-card (≤767px)padding: 40px 20pxpadding: var(--space-stack-sm) var(--space-inset-sm)#drives-me (≤767px)padding: 40px 20pxpadding: var(--space-stack-sm) var(--space-inset-sm).lens-container (≤767px)padding: 30px 20pxpadding: var(--space-inset-md) var(--space-inset-sm).lens-tabs (≤767px)gap: 12pxgap: var(--space-gap-sm).lens-tab-btn (≤767px)padding: 10px 20pxpadding: 10px var(--space-inset-sm).lens-content h3 (≤767px)margin-bottom: 12pxmargin-bottom: var(--space-3).p3-itempadding-bottom: 30pxpadding-bottom: var(--space-7).p3-rowgap: 16pxgap: var(--space-4).p3-togglepadding: 30px 0 10px 0padding: var(--space-7) 0 10px 0.p3-row (≤560px)gap: 4pxgap: var(--space-gap-xs).p3-viewlink (≤560px)padding: 0 0 12px 0padding: 0 0 var(--space-3) 0display:none states (publications3.php, intentional JS mechanism),
SVG animation-delay attributes (index.php, not spacing), SVG decorative-element position tokens
(about.php, component-scoped), and a Bootstrap column centering helper in contact.php.
04 — Utility Classes
Spacing utilities added
16 utility classes added to the Utilities section of site.css. All reference
Tier 2 semantic aliases — changing a semantic token updates both component rules and any
markup using the utility class.
.u-stack-sm > * + * { margin-top: var(--space-stack-xs) }
.u-stack-md > * + * { margin-top: var(--space-stack-sm) }
.u-stack-lg > * + * { margin-top: var(--space-stack-lg) }
Apply to a parent container to add consistent margin-top between all direct children.
.u-mt-sm { margin-top: var(--space-inset-sm) }
.u-mt-md { margin-top: var(--space-inset-md) }
.u-mt-lg { margin-top: var(--space-stack-sm) }
.u-mt-section { margin-top: var(--space-section-y-lg) }
.u-mb-sm { margin-bottom: var(--space-inset-sm) }
.u-mb-md { margin-bottom: var(--space-inset-md) }
.u-mb-lg { margin-bottom: var(--space-stack-sm) }
.u-gap-xs { gap: var(--space-gap-xs) }
.u-gap-sm { gap: var(--space-gap-sm) }
.u-gap-md { gap: var(--space-gap-md) }
.u-gap-lg { gap: var(--space-gap-lg) }
.u-p-sm { padding: var(--space-inset-sm) }
.u-p-md { padding: var(--space-inset-md) }
.u-p-lg { padding: var(--space-inset-lg) }
05 — Remaining Hardcoded
Values intentionally left as-is
Not every pixel value should be a token. The following were left hardcoded deliberately, grouped by reason.
Off-scale design values
Values that fall between grid steps and represent deliberate design choices. Replacing them with the nearest token would cause a visible change.
--space-8 (40px) and --space-9 (50px). This is the deliberate density feel of the about-page tilt cards — neither scale step produces the same visual weight. If the card padding ever needs to change globally, add a component token: --space-blueprint-card: 45px.
Micro-insets in interactive components
Padding on buttons, nav links, and toggles. These are touch-target sized and intentionally not on the 8px grid — they need to feel tight relative to their text, not spatially regular.
--space-1 (4px) or --space-2 (8px) would make the pill visibly too flat or too tall.
--space-2 (8px) and --space-3 (12px). It was considered for a --space-2b token but rejected — adding a token for a single intermediate value adds noise without benefit.
Layout-derived body offsets
These values are calculated from header height, not chosen as spacing units. They move in lockstep with header size changes, not with the spacing scale.
--header-height would be the right abstraction if header-height needs to be reused elsewhere — but that's a layout token, not a spacing token.
Structural / technique values
.row must exactly cancel the column padding — these three values are structurally linked and must stay equal. Wrapping in a token is possible but the gutter system is self-contained and not used elsewhere.
--space-6 (24px) and --space-7 (30px). Not shared with any other component.
--space-gap-sm (12px) and --space-gap-md (30px). A one-off responsive value; neither neighbor produces the right visual breathing room on small screens.