Starter template

Starter structure

Start with a predictable structure. Good architecture should remove decisions, not create new ones every Monday morning. The goal is a system people can explain without opening seven files first.

Real-world folder structure

TEXT code example
css/
    critical.css

    settings/
        variables.css
        media.css
        fonts.css

    base/
        reset.css
        typography.css

    helpers/
        vh.css
        a11y-link.css

    layout/
        site-head.css

    components/
        logo.css
        button.css
        main-nav.css
        card.css

    theme/
        brand.css

    legacy/
        vendor.css
        old-site.css

    hacks/
        temporary-fixes.css

Folder names may vary. Some teams group files under components/ with subfolders. The important rule is ownership, not folder worship.

Root CSS file as an import map

Your main stylesheet should define layer order first, then import files into the correct place. It should not become the place where emergency CSS goes to retire.

CSS code example
@layer legacy, settings, base, utilities,
layout, components, theme, hacks;

@import 'legacy/vendor' layer(legacy);
@import 'legacy/old-site' layer(legacy);

@import 'settings/variables' layer(settings);
@import 'settings/media' layer(settings);
@import 'settings/fonts' layer(settings);

@import 'base/reset' layer(base);

@import 'helpers/vh' layer(utilities);

@import 'layout/site-head' layer(layout);

@import 'components/logo' layer(components);
@import 'components/button' layer(components);
@import 'components/main-nav' layer(components);
@import 'components/card' layer(components);

Tokens first

Use variables for spacing, typography, colour, shadows, radius, and layout decisions. Random values spread inconsistency fast and turn design into avoidable inconsistency.

CSS code example
:root {
    --ff: 'Funnel Sans', system-ui;
    --ff-display: 'Funnel Display', system-ui;

    --fs: 1rem;
    --fs-l: 1.5rem;

    --space: 1rem;
    --space-m: 1.5rem;

    --c-red: hsl(343 100% 45.5%);
    --c-blue: hsl(222 53% 35%);

    --shadow-m: 0 2px 8px rgba(0, 0, 0, 0.12);
}

Change the token, not twenty unrelated selectors hunting for the correct shade of red.

Fonts belong in settings

Font loading belongs in the settings layer. Typography defaults belong in base. Components should not be quietly importing font decisions like suspicious luggage.

CSS code example
@font-face {
    font-family: 'Funnel Display';
    font-style: normal;
    font-display: swap;
    font-weight: 300 800;
    src: url('/fonts/funnel-display-variable.woff2')
        format('woff2-variations');
}

Working rules

  • One component, one partial.
  • No IDs for styling.
  • No !important unless there is a real emergency.
  • Base styles stay in the base layer.
  • Utilities stay rare and purposeful.
  • Theme changes presentation, not structure.
  • Legacy CSS stays low in the cascade.
  • Hacks are temporary and visible.
  • Large files trigger review, not pride.

What to avoid

  • One giant site.css containing everything.
  • Component styles mixed into reset or base files.
  • Random colours and spacing values spread across components.
  • Third-party CSS imported without a legacy layer.
  • Temporary fixes hidden inside normal component files.
  • Utility chains replacing actual component architecture.

If the project becomes difficult to explain, it will become worse to maintain.