Fernlight — Developer & Theme Reference
Fernlight is a calm, editorial full-site-editing (FSE) block theme for writers and slow-living blogs. It ships a curated palette, a serif/sans type pairing, a consistent spacing scale, 7 style variations, and 52 inserter-enabled block patterns. This document is the technical reference for the theme and its companion plugin.
Requirements
- WordPress 6.6 or newer
- PHP 7.4 or newer
- A block theme–capable environment (FSE). No page builder required.
At a glance
- Type: Block theme (
theme.jsonv3), full site editing. - Patterns: 52 inserter-enabled patterns + 8 template-only helper patterns.
- Style variations: 7 (Sage default, plus Clay, Dusk, Ink, Sky, Warm Sand, and Dark).
- Companion plugin:
fernlight-companion— optional editorial blocks. The theme is fully functional without it. - CSS: Design system in
theme.json+ a single enqueued stylesheet; WooCommerce styles are split off and only loaded when WooCommerce is active.
File structure
fernlight/
├── style.css Theme header (required); design lives in theme.json + CSS
├── theme.json Palette, type, spacing, layout (v3)
├── functions.php Setup + conditional asset enqueue
├── readme.txt WordPress.org readme
├── screenshot.png 1200×900
├── templates/ Block templates (index, single, archive, 404, search…)
├── parts/ Header, footer template parts
├── patterns/ 60 PHP pattern files (52 inserter + 8 template-only)
├── styles/ 7 style-variation JSON files
├── inc/ block-patterns.php (categories), perf-a11y.php (font preload)
└── assets/
├── css/ post-styles.css (+ .min) and woocommerce.css (+ .min)
└── fonts/ Cormorant Garamond + DM Sans (woff2)
Design tokens (theme.json)
Color — semantic roles
Fernlight uses role-named color slugs (not hue names), so every style variation can re-skin the theme by swapping the same eight roles. Default (Sage) values:
| Slug | Default | Role |
|---|---|---|
contrast | #2C4A5A | Primary text / strong foreground |
muted | #7A9BAD | Secondary text, meta, eyebrows |
accent | #C8D8B0 | Accent / highlight |
surface | #F4F7FA | Raised surface (cards, bands) |
surface-2 | #DDE8EF | Secondary surface |
on-invert | #FFFFFF | Text on inverted backgrounds |
base | #FFFFFF | Page background |
invert | #2C4A5A | Inverted background (dark blocks) |
Patterns reference these roles only (e.g. backgroundColor:"surface", textColor:"muted"), never raw hex — which is what makes a one-file variation swap re-skin the whole library.
Typography
- Families:
cormorant(Cormorant Garamond — display/headings),dm-sans(DM Sans — body and chrome). - Font sizes:
small0.875rem ·body1rem ·medium1.125rem ·large1.5rem ·h31.5rem ·h22rem ·h13rem. Headings also use fluidclamp()sizing in several patterns.
Spacing scale
2xs 0.25rem · xs 0.5rem · s 1rem · m 1.5rem · l 2rem · xl 3rem · 2xl 5rem. Reference as var:preset|spacing|m.
Layout
contentSize 720px (the reading measure) · wideSize 1140px (wide alignment).
Style variations
Each file in styles/ is a theme.json-shaped palette that overrides the eight color roles. Switch them under Appearance → Editor → Styles.
- Sage (default) — soft green/blue editorial calm.
- Clay, Dusk, Ink, Sky, Warm Sand — alternate light palettes.
- Dark — a full dark palette (
base#11171B,contrast#E6EDF1,accent#9DBE9A, surfaces#1B2329/#27323B). Because patterns use role tokens, dark mode is a selectable variation rather than a separate stylesheet.
Pattern catalog
Patterns auto-register from patterns/*.php via header comments. Categories are registered in inc/block-patterns.php:
fernlight-editorial— Editorialfernlight-page-sections— Page sectionsfernlight-post-parts— Post partsfernlight-archive— Archive
The 52 inserter-enabled patterns group as: Heroes & headers (6), Editorial sections (13), Post building blocks (13), Newsletter & CTA (3), Post lists & archives (10), Full-page layouts (7). The 8 Inserter: no patterns (template-*) are wired into block templates (404 text, search form, no-results messages) and aren't shown in the inserter.
Full-page patterns (page-about, page-now, page-resources, page-work-with-me, page-contact, page-essay, wow-page-start-here) use templateLock:"contentOnly" so editors replace copy without breaking the layout.
Companion plugin blocks
fernlight-companion (separate, optional install) adds six editorial blocks: divider, newsletter (platform-agnostic sign-up), post-callout, post-hero, pull-quote, and related-grid. The theme never hard-depends on the plugin; patterns degrade gracefully if it is inactive.
CSS architecture
theme.jsondefines presets and block styles (the bulk of the design).assets/css/post-styles.cssis the single front-end stylesheet, enqueued fromfunctions.php. It defines internal aliases (e.g.--sage: var(--wp--preset--color--accent)) over the semantic presets.assets/css/woocommerce.cssis only enqueued whenclass_exists( 'WooCommerce' ), keeping WooCommerce overrides off the global path.- Minified
.min.cssfiles are the production artifacts (built with lightningcss).
Accessibility
WordPress core supplies the block-theme "Skip to content" link, targeting the #fern-main landmark; the theme styles it via .skip-link and does not duplicate it. Focus states are strengthened in post-styles.css: a 3px :focus-visible outline globally, plus a tinted ring on navigation links.
Build & tooling
npm run build:css Minify post-styles.css and woocommerce.css (lightningcss)
npm run lint composer lint (WPCS) + stylelint
npm run lint:css stylelint
npm run i18n regenerate the .pot translation template
npm run package build installable dist zips (bin/build-zip.sh)
CI (.github/workflows/quality.yml) runs PHP syntax + WPCS, stylelint, JS/JSON checks, the WordPress theme-review action, and uploads the packaged zips.
Internationalization
All user-facing strings use fernlight as the text domain. Run npm run i18n to refresh languages/fernlight.pot.