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 Cormorant Garamond / DM Sans type pairing, a consistent spacing scale, 7 style variations, and 59 inserter-enabled block patterns — plus an AI-native, machine-readable surface. This is the technical reference for the theme and its companion plugin.
Requirements
- WordPress 6.7 or newer (the native Abilities API surface activates on 6.9+)
- PHP 8.0 or newer
- A block-theme-capable (FSE) environment. No page builder required.
At a glance
- Type: Block theme (
theme.jsonv3 with a documented design-intent contract), full site editing. - Patterns: 59 inserter-enabled + 8 template-only helper patterns (67 pattern files).
- Style variations: 7 (Sage default, plus Warm Sand, Clay, Sky, Dusk, Ink, and Dark).
- Block styles: 14 brand styles across button, navigation, details, separator, paragraph, and heading.
- Companion plugin:
fernlight-companion1.2.26 — 8 optional editorial blocks plus the REST/Abilities surface. The theme is fully functional without it. - Front-end JS: 0 KB from the theme. CSS is split into conditional layers (always-loaded base under 10 KB).
- Dark mode: automatic (system
prefers-color-scheme, off by default, toggle in Settings) plus a selectable Dark style variation. - i18n: translation-ready (POT included), full RTL stylesheet, Polylang/WPML compatible.
- AI/MCP-ready: a design-intent contract, the native WordPress Abilities API, and five public read-only REST endpoints.
Design tokens (theme.json)
Color — semantic roles
Fernlight uses role-named color slugs (not hue names), so every style variation re-skins the theme by swapping the same eight roles. Default (Sage-adjacent base) values:
| Slug | Default | Role |
|---|---|---|
contrast | #2C4A5A | Primary text / strong foreground |
muted | #4A7589 | 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.
Semantic tokens & spacing roles
Beyond the raw palette, theme.json documents semantic intent under settings.custom.designIntent — consumed by the /tokens and /type endpoints:
- Text roles:
primary,secondary,muted,invert. - Surface roles:
page,card,section,subtle,invert. - Border roles:
subtle,strong. - Spacing roles:
contentRhythm(s/1rem),clusterGap(m/1.5rem),cardPadding(l/2rem),sectionPadding(2xl/5rem),gutter(--gutter/5vw),inset(xl/3rem).
Typography
- Families:
cormorant(Cormorant Garamond — display/headings),dm-sans(DM Sans — body and chrome). 8 self-hosted WOFF2 faces; the 500 weight of each is preloaded. - Font sizes (fluid
clamp()):small0.875rem ·body1rem ·medium1.125rem ·large1.5rem ·h31.5rem ·h22rem ·h13rem. - Measures & rhythm: reading measure 42rem, editorial measure 52rem; body leading 1.65, prose leading 1.8, display leading 1.06; baseline unit 0.75rem.
Spacing scale & layout
Spacing: 2xs 0.25 · xs 0.5 · s 1 · m 1.5 · l 2 · xl 3 · 2xl 5 rem (reference as var:preset|spacing|m). Layout: contentSize 720px (reading measure) · wideSize 1140px.
Block styles
Registered on init in inc/block-styles.php (14 total):
| Block | Styles |
|---|---|
core/button | fern-primary (default), fern-outline-light, fern-outline-dark, fern-ghost |
core/navigation | fern-mega |
core/details | fern-accordion |
core/separator | fern-sage, fern-pale |
core/paragraph | fern-lede, fern-deck, fern-kicker, fern-baseline |
core/heading | fern-display, fern-compact |
Pattern catalog
Patterns auto-register from patterns/*.php via header comments. Four Fernlight categories are registered in inc/block-patterns.php: fernlight-editorial, fernlight-page-sections, fernlight-post-parts, and fernlight-archive. 59 patterns are inserter-enabled; the 8 Inserter: no template-* patterns are wired into block templates (404 text, search form, no-results messages). 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 blocks
fernlight-companion (separate, optional install) adds eight server-rendered editorial blocks: post-hero, pull-quote, post-callout, divider, related-grid, newsletter (provider-neutral sign-up), icon (12 inline SVGs), and tabs (ARIA tabbed panels; the only block with a front-end script). It also provides the archive layout switcher, load-more / instant search, per-category accents, reading enhancements, and the REST/Abilities surface below. The theme never hard-depends on the plugin.
AI-native surface
REST endpoints
Provided by Fernlight Companion in the fernlight/v1 namespace. All five are public and read-only.
| Endpoint | Returns |
|---|---|
/wp-json/fernlight/v1/capabilities | Master discovery: versions, all fernlight/* blocks & patterns, the endpoint map, and the abilities descriptor. |
/wp-json/fernlight/v1/patterns | Registered patterns, filterable by intent, purpose, limit (1–25). |
/wp-json/fernlight/v1/tokens | Live palette, semantic-token CSS variables, and the semanticTokens intent. |
/wp-json/fernlight/v1/type | Font families, sizes, spacing, layout, and the full typographySystem block. |
/wp-json/fernlight/v1/perf | Versions, WP/PHP requirement booleans, CSS KB, js_front_kb: 0, font inventory, dark-mode flag. |
Abilities API (WordPress 6.9+)
Companion registers a fernlight-content ability category and three read-only abilities — they suggest, they never change content:
fernlight/list-patterns(public) — patterns filtered by natural-languageintent/purpose/limit, ranked by keyword match.fernlight/compose-page(requiresedit_posts) — a deterministic page recipe (title, orderedpattern_slugs,<!-- wp:pattern -->reference markup, notes) forpage_typehome / about / resources / start-here / essay-index / newsletter / landing. Does not create content.fernlight/get-performance-report(public) — same payload as/perf.
Hooks & filters
do_action( 'fernlight_getting_started_after_links' )— inject links into the wizard’s Build tab (used by Companion).apply_filters( 'fernlight_pillar_slug_class_map', $map, $categories )— override the category-slug → accent-class map.apply_filters( 'fernlight_blog_grid_categories', $pillars )/fernlight_blog_grid_base_url/fernlight_posts_index_url— customize theshortcode.No posts found for this filter yet. Show all posts.
CSS architecture
Fernlight loads no front-end JavaScript and splits its CSS into conditional layers, enqueued from functions.php and version-busted with FERNLIGHT_VERSION:
- Always:
post-styles.min.css(base, under ~10 KB),view-transitions.min.css, andprint.min.css(media="print"). - Conditional:
components,buttons,typography,newsletter(loaded when matching classes/blocks appear in content);single(is_single());archive(home/archive/search);mega-menu(header usesis-style-fern-mega);dark-mode(only when auto dark mode is not disabled);woocommerce(only when WooCommerce is active). - The same files (minus print) register as editor styles via
add_editor_style()pluseditor.css. - Minified
.min.cssfiles are the production artifacts (built with lightningcss).
Performance & accessibility
0 KB front-end JS, self-hosted fonts (the 500 weight of each preloaded with fetchpriority="high"), and a ~191 KB critical path. Lighthouse on real content, with the Companion active: 100 / 100 / 100 / 100 on mobile and desktop. WCAG 2.2 AA contrast across every variation; a core skip link to #fern-main; strengthened :focus-visible states; single non-duplicated banner/contentinfo landmarks; reduced-motion-aware View Transitions; a reading-first print stylesheet.
Build & tooling
npm run build:css Minify CSS layers (lightningcss)
npm run lint composer lint (WPCS) + stylelint
npm run i18n regenerate the .pot translation template
npm run package build installable dist zips (bin/build-zip.sh)
CI runs PHP syntax + WPCS, stylelint, JS/JSON checks, and the WordPress.org theme-review action, then uploads the packaged zips. The complete version-by-version build history is kept in an internal build log. All user-facing strings use the fernlight text domain.