Building the Stylesheet
style.css ships compiled, so nothing here is needed to use
the template. You need it to change the styling at source — which is what
the rest of this page describes.
Setup
Requires Node.js 18 or newer.
npm install # once
npm run watch # rebuilds Template/style.css as you edit
For a single build:
npm run build
Both run Sass over Source/assets/sass/style.scss, then Autoprefixer.
The browser targets live in .browserslistrc — one file, read by
every tool in the chain.
Where the decisions live
| File | Holds |
|---|---|
variables/_colors.scss | Every colour token, in four tiers |
variables/_variables.scss | Type scale, line heights, radii, spacing |
components/_core.scss | Base elements, buttons, the layout kit |
components/_shell.scss | Page shell, hero, reading column |
components/_overlay.scss | The overlay |
components/_swiper-custom.scss | Carousel styling |
Commit the compiled CSS. It is the deliverable, not a build
artifact. The template has to work from a double-click on
index.html, so a copy that arrives without
style.css is a broken template.
Colour Tokens
Every colour in the template comes from a custom property on :root
— 53 of them, in _colors.scss. They are organised in four tiers, and the tiers are what make
the palette swappable rather than merely centralised.
The four tiers
| Tier | What it holds | Example |
|---|---|---|
| 1 — Brand | The three colours that define the template's identity. | --cel-ink, --cel-primary, --cel-secondary |
| 2 — Palette | Raw primitives with no meaning attached: the gray ramp, the named accents. | --cel-gray-40, --cel-blue |
| 3 — Semantic | Meaning attached. References tier 1 or 2, never a literal. | --cel-body-bg, --cel-link-color |
| 4 — Component | One component's private token. Only when tier 3 will not do. | --cel-overlay-bg, --cel-menu-bg |
Components consume tiers 3 and 4. Reaching straight for tier 1 or 2 from a component is how a palette stops being swappable — the value works, and then re-theming misses it. Avoid it in new code.
Retheming
Edit tier 1 and the handful of tier 3 tokens that do not derive from it. Everything else follows.
:root {
--cel-ink: #063ccb; /* the page ground */
--cel-primary: #e63946; /* the accent */
--cel-secondary: #ffe292; /* link hover */
}
The gray ramp is complete on purpose, even though not every step is used. It is the ladder tier 3 picks from, and a ramp with holes in it invites one-off hex values — which is exactly what the tiers exist to prevent.
Contrast
The type sits on a saturated blue ground, which rules out several combinations that look reasonable in a swatch. Two decisions worth knowing before you change them:
- Links are not the red accent. Red on the ink measures 2.0:1 — unreadable. They use the body colour, with the secondary on hover.
- Muted text is
--cel-gray-20, not a darker step. Against the ink, gray-20 measures 6.3:1; gray-40 drops to 3.5:1 and gray-50 to 2.5:1.
Type Scale
One typeface, Inter, self-hosted as a variable font. Sizes are fluid: each one is a
clamp() that grows with the viewport instead of stepping at breakpoints.
| Element | Size (min → max) | Line height | Weight |
|---|---|---|---|
h1 |
52 → 113px | 1.05 | 900 |
h2 |
40 → 80px | 1.1 | 900 |
h3 |
32 → 56px | 1.15 | 700 |
h4 |
27 → 40px | 1.2 | 700 |
h5 |
23 → 28px | 1.3 | 700 |
h6 |
body size | 1.5 | 700 |
| body | 18 → 20px | 1.5 | 400 |
.cel-eyebrow |
19 → 24px | 1.3333 | — |
Behavior
- Line heights fall as size rises, from 1.5 on body copy to 1.05 on
h1. A ratio that reads well at 18px leaves a canyon between lines at 113px. - They are tuned by the resulting gap, not by the ratio. Two elements at different sizes need different declared values to look equally spaced — matching the numbers is what produces the mismatch.
- Letter-spacing tightens with size too: −0.045em on
h1, −0.035em onh2andh3, −0.02em onh4. Inter is drawn for text sizes, and its default fit is loose once it is set as display type. - The eyebrow stops growing at 768px. Its clamp is capped early on purpose, so on a phone it stays small beside the shrinking wordmark.
Layout Kit
A small set of classes for building a page without writing CSS. The two pages that
ship do not use it — it is here so you can add a third. Everything except
.cel-container works only inside .cel-main-wrapper.
| Class | Purpose |
|---|---|
.cel-main-wrapper |
The page wrapper the kit lives in. Paints the page ground. |
.cel-content-wrapper |
The breakout grid. Its children sit in a centred content column, up to 1320px wide. |
.cel-popout, .cel-feature, .cel-full |
On a child of the breakout grid: reach 2rem, 7rem or all the way past the content column, to the window edge. |
.cel-2col … .cel-5col |
Column grids that collapse to fewer columns on smaller screens. |
.cel-gap-xs … .cel-gap-xl |
Fluid gaps for those grids. |
.cel-spacer-xs … .cel-spacer-xl |
Fluid vertical spacers, from 4–8px up to 32–64px. |
.cel-container |
Full width, centred. No gutters of its own. |
<div class="cel-main-wrapper">
<div class="cel-content-wrapper">
<h2>In the content column</h2>
<figure class="cel-full">…</figure>
<div class="cel-3col cel-gap-md"> … </div>
</div>
</div>
The breakout grid
.cel-content-wrapper lays out a centred content column with wider
tracks either side, so a section can span the viewport without leaving the flow or
needing negative margins.
Floats do not apply to grid items. The pull-out figures on the
About page float, so text wraps around them — which is why that page uses
.cel-prose and its own width rather than this grid. If you need text
to wrap around something, the breakout grid is the wrong tool.
Page shell
The two shipped pages are built from .cel-shell (the page frame),
.cel-site-header, .cel-hero, .cel-stage and
.cel-main-article / .cel-prose for the reading column.
Buttons
The appearance lives on classes. The bare <button> tag carries only
a reset — it gets no colour, no padding and no border of its own.
Classes
| Class | Use |
|---|---|
.cel-button |
The default button. |
.cel-button-primary |
Filled with the dark navy, --cel-btn-primary. |
.cel-button-accent |
Filled with the accent. |
<button class="cel-button-primary">Send</button>
<a href="#" class="cel-button">Read more</a>
They are classes, not a tag rule, so an <a> that should look like a
button gets the same treatment without pretending to be one.
Why the tag carries nothing
Most buttons in the template are controls, not calls to action — a carousel
arrow, an overlay close, prev/next. Styled by tag, each of them would have to undo
the look first. So the tag keeps only the reset every button wants: inherited font
and colour, no border, no padding, a pointer cursor, and not-allowed
when disabled.
If you add a button that should look like nothing — an icon control, a
toggle — write <button> and add no class. It will already
be invisible. That is the point of the arrangement.