CSS Tutorial
CSS Variables
Custom properties store tokens: --brand, --space. var() reads them. Themes become one override.
A name for a value you reuse
Harbor studio’s teal shows up on buttons, links, and borders. If you type #0d9488 in twelve rules, a rebrand means twelve edits and a miss. A custom property holds that color once. You read it withvar(). Change the token, and every use follows.
Custom properties are real CSS, not a preprocessor. They cascade. A child can override a parent. That is how a dark theme, a compact card, or a “sold out” badge can restyle a whole subtree without new class names for every color.
:root is the usual shelf
:root is the document element — the same specificity as html, a little higher than a bare html tag selector. Put global tokens there. Names start with two dashes. Use lowercase and hyphens: --brand, --space, --text.
Example
<style>
:root {
--brand: #0d9488;
--sky: #e0f2fe;
--text: #0c4a6e;
--space: 1rem;
}
body {
font-family: system-ui, sans-serif;
color: var(--text);
background: var(--sky);
margin: var(--space);
}
h1 { color: var(--brand); margin: 0 0 0.4rem; }
.card {
background: #fff;
padding: var(--space);
border-radius: 0.65rem;
border: 2px solid var(--brand);
}
</style>
<h1>Harbor studio</h1>
<article class="card">
<p>Tokens live on :root. The card border and heading share --brand.</p>
</article>var(--brand) is a request: use the computed value of --brand on this element. If the property is missing, the declaration is invalid at computed-value time unless you pass a fallback.
var() and a fallback
The second argument to var() is used when the custom property is not set or is invalid. It is a safety net, not a default theme. Put the real defaults on :root. Use a fallback when a component should survive being dropped onto a page that forgot the tokens.
| Write | Meaning |
|---|---|
--brand: #0d9488; | Define a token on this element (and descendants) |
color: var(--brand); | Read the token |
color: var(--brand, teal); | Read, or use teal if unset |
padding: var(--space); | Tokens work for lengths, not only colors |
Example
<style>
.label {
font-family: system-ui, sans-serif;
display: inline-block;
padding: 0.35rem 0.7rem;
border-radius: 999px;
color: #fff;
background: var(--accent, #0284c7);
}
.pier { --accent: #0d9488; }
</style>
<p><span class="label">Gallery</span> uses the fallback sky.</p>
<p class="pier"><span class="label">Pier desk</span> overrides --accent to teal.</p>Custom properties inherit. Set --accent on a parent, and every var(--accent)inside follows. That is why a theme class on body can restyle the whole page. Try it in/css/try.
A theme is one override
Dark mode, a compact density, a seasonal banner — each can be a class that reassigns tokens. Component rules stay the same. They already read var(--bg) and var(--text). You do not duplicate every selector.
Example
<style>
:root {
--bg: #f0f9ff;
--panel: #fff;
--text: #0c4a6e;
--brand: #0d9488;
}
.night {
--bg: #0c4a6e;
--panel: #164e63;
--text: #e0f2fe;
--brand: #5eead4;
}
body {
font-family: system-ui, sans-serif;
background: var(--bg);
color: var(--text);
margin: 1.25rem;
}
.card {
background: var(--panel);
padding: 1rem 1.15rem;
border-radius: 0.7rem;
border: 1px solid var(--brand);
}
h1 { color: var(--brand); font-size: 1.2rem; margin: 0 0 0.4rem; }
</style>
<section class="night">
<article class="card">
<h1>Harbor studio after hours</h1>
<p>The .night class rewrites four tokens. The card rules do not change.</p>
</article>
</section>You can also set a token inline: style="--brand: navy" on one card. That is valid CSS. Use it for a one-off, not as a substitute for a class you will need in ten places.
What tokens are for
Colors, spacing steps, radii, and font stacks belong on :root. Do not put a one-usemargin-top in a variable. The point is a small dictionary the rest of the stylesheet speaks. When the harbor brand shifts from teal to a deeper sea green, you edit the dictionary.
Custom properties are not the same as preprocessor variables. They exist in the browser, they inherit, and DevTools can live-edit them. That is why they are the right tool for themes.
Worked examples
The short listings above show one property. These sheets paint documents you would actually ship: a lab dashboard, a clinic form, a marks table, a nav bar.
CSS does not invent meaning. HTML already said what the pieces are. Cascade, specificity, and the box model decide how they look. The numbers are classroom values — current, pH, pulse, 3-4-5 — so the style has something real to sit on.
Preview them in the CSS editor at /css/try. Change one property and watch the page paint. The tags stay the same.
Engineering
Brand tokens on a card
:root holds custom properties. var(--brand) reads them. One override on a parent restyles a whole dashboard. That is a theme, not a new stylesheet per page.
Fallback: var(--brand, teal) if the token is missing. Names are case-sensitive. --brand and --Brand are different.
Example
<style>
:root { --brand: #0f766e; --paper: #f0fdfa; }
.card { color: var(--brand); background: var(--paper); padding: 0.7rem; }
</style>
<div class="card">logger tokens</div>Chemistry
Acid and ok as tokens
Swap --danger in one place for a dark theme. Do not hunt twenty hex codes. The HTML class names stay .acid and .ok.
Example
<style>
:root { --danger: #b91c1c; --ok: #166534; }
.acid { color: var(--danger); }
.ok { color: var(--ok); }
</style>
<p class="acid">HCl</p>
<p class="ok">Water</p>