Refactor Report

Spacing System — danielschwartz.com

File: danielschwartz-com-streamline/includes/css/site.css  ·  Date: May 2026  ·  Status: Complete

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.

12
Primitive tokens added
19
Semantic aliases added
65+
Declarations tokenized
16
Utility classes added
2
Files updated

Approach: three tiers

Architecture
TierExamplesPurpose
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.

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

TokenValuepxVisual
--space-14px4
--space-28px8
--space-312px12
--space-416px16
--space-520px20
--space-624px24
--space-730px30
--space-840px40
--space-950px50
--space-1060px60
--space-1164px64
--space-1280px80

Tier 2 — Semantic aliases

Section rhythm
TokenResolves toUsed for
--space-section-yvar(--space-7) — 30pxBase section block vertical padding (.section)
--space-section-y-mdvar(--space-10) — 60pxMedium section padding (.publications-section)
--space-section-y-lgvar(--space-12) — 80pxLarge section padding (.about-section, .section-general)
--space-section-xvar(--space-6) — 24pxSection container horizontal padding (.section-container)
--space-header-yvar(--space-7) — 30pxSite header vertical padding (.site-header)
Component insets (padding)
TokenResolves toUsed for
--space-inset-smvar(--space-5) — 20pxSmall component inner padding (project cards, social icons, modal overlay)
--space-inset-mdvar(--space-7) — 30pxMedium component inner padding (blueprint box, rowentry, lens mobile)
--space-inset-lgvar(--space-10) — 60pxLarge component inner padding (modal card)
Flex / grid gaps
TokenResolves toUsed for
--space-gap-xsvar(--space-1) — 4pxTight gaps (nav links, mobile patent row)
--space-gap-smvar(--space-3) — 12pxSmall gaps (lens tabs, mobile lens tabs)
--space-gap-mdvar(--space-7) — 30pxStandard grid gaps (blueprint card grid)
--space-gap-lgvar(--space-10) — 60pxWide column gaps (lens container columns)
Vertical stack margins
TokenResolves toUsed for
--space-stack-xsvar(--space-5) — 20pxTight stacks (utility class base)
--space-stack-smvar(--space-8) — 40pxStandard stacks (project cards, section headers, footer)
--space-stack-mdvar(--space-9) — 50pxMedium stacks (footer margin-top, section label)
--space-stack-lgvar(--space-12) — 80pxLarge stacks (utility class base)

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).

includes/css/site.css 65+ changes
Selector
Before
After
.skip-link
padding: 8px 16px
padding: var(--space-2) var(--space-4)
.section
padding: 30px 0
padding: var(--space-section-y) 0
.site-header
padding: 30px 0
padding: var(--space-header-y) 0
.nav-links
gap: 4px
gap: var(--space-gap-xs)
.theme-toggle
margin-left: 12px
margin-left: var(--space-3)
.section.footer
margin-top: 50px; margin-bottom: 40px
margin-top: var(--space-stack-md); margin-bottom: var(--space-stack-sm)
.footer hr
margin-top: 8px; margin-bottom: 8px
margin-top: var(--space-2); margin-bottom: var(--space-2)
.social a
margin-top: 30px
margin-top: var(--space-7)
.social i
padding: 20px
padding: var(--space-inset-sm)
.content h4
margin: 22px 0
margin: var(--space-6) 0 (24px, +2px)
.headline-small
margin-bottom: 4px
margin-bottom: var(--space-1)
.label
padding: 4px 6px
padding: var(--space-1) 6px
.label-specialize
padding: 8px; margin: 4px
padding: var(--space-2); margin: var(--space-1)
.styled-list
padding: 0 20px 20px 20px
padding: 0 var(--space-inset-sm) var(--space-inset-sm) var(--space-inset-sm)
.styled-list li
margin-bottom: 8px
margin-bottom: var(--space-2)
.project-card
margin-bottom: 40px
margin-bottom: var(--space-stack-sm)
.project-title
padding: 20px 10px 4px 20px
padding: var(--space-inset-sm) 10px var(--space-1) var(--space-inset-sm)
.project-headline
padding: 10px 20px 8px 20px
padding: 10px var(--space-inset-sm) var(--space-2) var(--space-inset-sm)
.project-description
padding: 10px 20px 20px 20px
padding: 10px var(--space-inset-sm) var(--space-inset-sm) var(--space-inset-sm)
.project-action
padding: 10px 20px 40px 20px
padding: 10px var(--space-inset-sm) var(--space-stack-sm) var(--space-inset-sm)
.projects-header
margin: 64px 0 40px 0
margin: var(--space-11) 0 var(--space-stack-sm) 0
.blueprint-grid
gap: 30px
gap: var(--space-gap-md)
.blueprint-card h4
margin: 0 0 24px 0
margin: 0 0 var(--space-6) 0
.section-point
padding: 20px 0 (top & bottom)
padding: var(--space-inset-sm) 0 (top & bottom)
.section-container
padding: 64px 24px
padding: var(--space-11) var(--space-section-x)
.section-label
margin-bottom: 50px
margin-bottom: var(--space-stack-md)
.section-header
margin-bottom: 40px
margin-bottom: var(--space-stack-sm)
.lens-container
gap: 60px; padding: 80px
gap: var(--space-gap-lg); padding: var(--space-12)
.lens-tabs
gap: 12px
gap: var(--space-gap-sm)
.lens-tab-btn
padding: 16px 20px
padding: var(--space-4) var(--space-inset-sm)
.lens-indicator
bottom: 30px; left: 80px
bottom: var(--space-7); left: var(--space-12)
.section-rowentry
padding-bottom: 30px
padding-bottom: var(--space-inset-md)
.section-rowentry::after
margin: 30px 0
margin: var(--space-7) 0
#ai-modal-overlay
padding: 20px
padding: var(--space-inset-sm)
.modal-card
padding: 60px
padding: var(--space-inset-lg)
.blueprint-box
padding: 30px; margin-top: 30px
padding: var(--space-inset-md); margin-top: var(--space-7)
.blueprint-list
margin: 20px 0 0 0
margin: var(--space-inset-sm) 0 0 0
.blueprint-list li
padding-left: 30px; margin-bottom: 15px
padding-left: var(--space-7); margin-bottom: var(--space-4) (16px, +1px)
.status-footer
margin-top: 40px
margin-top: var(--space-stack-sm)
.pulse-indicator
margin-right: 12px
margin-right: var(--space-3)
.about-section
padding-bottom: 80px
padding-bottom: var(--space-section-y-lg)
.section-general, .what-drives-me, .what-I-do
padding: 0 0 80px 0
padding: 0 0 var(--space-section-y-lg) 0
.publications-section-dark, .publications-section
padding: 60px 0 80px 0
padding: var(--space-section-y-md) 0 var(--space-section-y-lg) 0
.about-section .content .row
padding-bottom: 30px
padding-bottom: var(--space-7)
textarea.custom-input, input.custom-input
margin-bottom: 24px
margin-bottom: var(--space-6)
.ds-media (≤990px)
margin-bottom: 40px
margin-bottom: var(--space-stack-sm)
.section-rowentry (≤990px)
padding-left/right: 20px
padding-left/right: var(--space-inset-sm)
.section-rowentry-title (≤990px)
margin-top: 30px
margin-top: var(--space-7)
.section-point (≤767px)
margin-top: 60px
margin-top: var(--space-section-y-md)
.modal-card (≤767px)
padding: 40px 20px
padding: var(--space-stack-sm) var(--space-inset-sm)
#drives-me (≤767px)
padding: 40px 20px
padding: var(--space-stack-sm) var(--space-inset-sm)
.lens-container (≤767px)
padding: 30px 20px
padding: var(--space-inset-md) var(--space-inset-sm)
.lens-tabs (≤767px)
gap: 12px
gap: var(--space-gap-sm)
.lens-tab-btn (≤767px)
padding: 10px 20px
padding: 10px var(--space-inset-sm)
.lens-content h3 (≤767px)
margin-bottom: 12px
margin-bottom: var(--space-3)
publications3.php <style> block 4 changes
Selector
Before
After
.p3-item
padding-bottom: 30px
padding-bottom: var(--space-7)
.p3-row
gap: 16px
gap: var(--space-4)
.p3-toggle
padding: 30px 0 10px 0
padding: var(--space-7) 0 10px 0
.p3-row (≤560px)
gap: 4px
gap: var(--space-gap-xs)
.p3-viewlink (≤560px)
padding: 0 0 12px 0
padding: 0 0 var(--space-3) 0
header.php / footer.php / index.php / about.php / contact.php / mentorship.php / publications.php / thankyou.php No changes needed
No spacing-related inline styles that required tokenization. Inline styles present are: functional accordion display: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.

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.

Lobotomised-owl stacks
.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.

Top-margin helpers
.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) }
Bottom-margin helpers
.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) }
Gap utilities
.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) }
Padding utilities
.u-p-sm { padding: var(--space-inset-sm) } .u-p-md { padding: var(--space-inset-md) } .u-p-lg { padding: var(--space-inset-lg) }

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.

45px .blueprint-card-inner { padding: 45px }
Sits between --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.
46px .section-rowentry--lg_padding::after { margin: 46px 0 30px 0 }
A single-use visual tweak to the divider line spacing on a specific row-entry variant. Not a repeating pattern — tokenizing it would name something used in exactly one place. Leave it.

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.

6px 14px .nav-links a { padding: 6px 14px }
Nav link tap target sizing. The asymmetric vertical/horizontal values are intentional optical adjustment, not a spacing unit.
11px 13px .nav-toggle { padding: 11px 13px }
Hamburger button padding tuned to produce exactly 44×44px minimum touch target. A round-number token would break the target size calculation.
5px 12px .theme-toggle { padding: 5px 12px }
Pill-shaped toggle inset. The vertical value (5px) is below the scale floor; replacing with --space-1 (4px) or --space-2 (8px) would make the pill visibly too flat or too tall.
10px .shrink .site-header, .p3-toggle bottom padding, various responsive overrides
10px appears in several places as a "half step" between --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.

156px body { margin-top: 156px }
The fixed-header clearance in default state. If header height changes, this must change in lockstep. A dedicated token like --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.
80px / 90px body.shrink { margin-top: 80px } — body (≤767px) { margin-top: 90px }
Shrunken and mobile header clearance values. Same reasoning as 156px above.

Structural / technique values

−40px .ds-media-headline-image { margin-right: -40px }
A negative margin used to create the overlapping headline-image stacking effect. Not a spacing unit — this is a deliberate overflow/overlap technique. Negative tokens are not part of this system.
15px .container { padding: 0 15px } — .row { margin: 0 -15px } — [class*="col-"] { padding: 0 15px }
Bootstrap-compatible grid gutter. The negative margin on .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.
25px .styled-list li { padding-left: 25px }
Indent sized to clear the 8px diamond bullet plus visual breathing room. Falls between --space-6 (24px) and --space-7 (30px). Not shared with any other component.
1.7rem .p3-meta { padding-left: 1.7rem } — .p3-desc p { padding: 10px 0 0 1.7rem }
Em-relative indent in the patent accordion that aligns description text with the title (accounting for icon width). Intentionally em-based so it scales with font size. Not a candidate for a px token.
25px gap (.lens-container mobile) .lens-container (≤767px) { gap: 25px }
Mobile gap between the tab list and the content panel in the lens system. Falls between --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.