Getting Started
Farn ships as a single CSS file — no build step, no runtime, no framework assumptions. Add one <link> tag and you have all tokens.
CDN Install
Link directly from jsDelivr. Replace @0.1.0 with the latest version tag.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/jabopiti/farn-theme@0.1.0/dist/farn.css"> dist/farn.css is a single concatenated file containing palette tokens, semantic tokens, typography, spacing, and base resets.
What Gets Loaded
farn.css bundles five layers in order:
colors.css— 17 palette tokens across four palettestypography.css— font-family tokens and scale referencespacing.css— space scale, layout widths, border radius, z-indexdark-light.css— semantic token layer with dark/light switchingbase.css— box-sizing reset, smooth scroll, focus ring, reduced-motion
Fonts are not bundled in farn.css. Load them separately via Google Fonts.
Load Fonts
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Instrument+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
/> Load fonts via a <link> tag in <head>, not via @import url() inside CSS — the @import approach delays rendering.
Prevent Flash of Wrong Theme (FOWT)
Add this inline script in <head> before any CSS loads. It reads the stored preference and sets data-theme on <html> before the browser paints.
<script>
(function () {
const stored = localStorage.getItem('farn-theme');
const system = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', stored ?? system);
})();
</script> Dark / Light Mode
Theme mode is controlled via a data-theme attribute on <html>:
<html data-theme="light"> … </html>
<html data-theme="dark"> … </html> Toggle it with JavaScript and persist the choice:
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('farn-theme', next);
} Default (no attribute) is light mode. System preference is respected when no manual choice is stored.
Surface Overrides
Apply data-surface to any element to force a surface context independently of the page theme:
<section data-surface="dark">
<!-- All children use dark surface tokens automatically -->
<button>Primary action</button>
</section> Available values: light, dark, tinted. All child components inherit the re-scoped tokens with no extra markup.
Using Tokens
Reference semantic tokens in your component CSS — never raw palette tokens:
.btn-primary {
background: var(--color-accent); /* Fern (light) or Sage (dark) */
color: var(--color-accent-text); /* always Parchment */
border-radius: var(--radius-md); /* 6px */
padding: 0 var(--space-md);
font-family: var(--font-body);
font-weight: 600;
}
.card {
background: var(--color-bg-elevated); /* Linen (light) or Iron (dark) */
border-radius: var(--radius-lg); /* 8px */
padding: var(--space-md);
} Fraunces Headings
font-variation-settings: 'opsz' is mandatory on every Fraunces heading — the font renders incorrectly without it:
h1 { font-family: var(--font-display); font-variation-settings: 'opsz' 72; }
h2 { font-family: var(--font-display); font-variation-settings: 'opsz' 24; }
h3 { font-family: var(--font-display); font-variation-settings: 'opsz' 20; } Minimal Example
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/jabopiti/farn-theme@0.1.0/dist/farn.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Instrument+Sans:wght@400;500;600&display=swap">
<script>
(function(){
const s = localStorage.getItem('farn-theme');
const p = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', s ?? p);
})();
</script>
<style>
body {
font-family: var(--font-body);
background: var(--color-bg);
color: var(--color-text);
padding: var(--space-xl);
}
h1 {
font-family: var(--font-display);
font-variation-settings: 'opsz' 72;
font-weight: 800;
}
</style>
</head>
<body>
<h1>Hello, Farn</h1>
<p>Token-first design system.</p>
</body>
</html>