emdash theme

Implementation Guide

How the emdash Astro theme implements the FSCC and Manual brand systems. Architecture, design tokens, component mapping, interaction standards, and current drift from the direction specs.

This document covers

  • Architecture and routing
  • Design tokens and foundations
  • Component system
  • Interaction and accessibility
  • Surface-specific implementation
  • Current implementation drift

This does not replace

1
One codebase, two surfaces

Field Scout is the editorial surface. the manual is the explanatory surface. Both share the same implementation system but express distinct visual identities through CSS scoping.

2
Brand fidelity over local convenience

Where implementation and brand docs diverge, pull the implementation toward the approved brand logic. Do not rationalize drift after the fact.

3
Readability over atmosphere

Labels, metadata, nav items, and form controls must remain readable as interface elements. Atmospheric styling cannot undermine utility text.

4
Interaction is part of the system

Hover is not enough. Focus, keyboard navigation, validation states, loading indicators, empty states, and error handling are all implementation requirements.

Architecture

Stack

Framework
Astro 6.1.3 with SSR on Cloudflare Workers (Node.js for local dev)
CMS
EmDash CMS — markdown-first content collections with optional admin panel
Database
Cloudflare D1 (production), SQLite (local) for CMS data and email captures
Media
Cloudflare R2 (production), local /uploads directory
Styling
Single global CSS file (site.css, ~3000 lines) with custom properties. No Tailwind, no CSS-in-JS.
Fonts
Google Fonts: DM Serif Display, Inter, IBM Plex Mono
Repo
permanentbetalabs/fscc-the-manual

File Structure

Surface System

BaseLayout.astro accepts a surface prop ("fscc" or "manual") that controls body class, favicon, theme color, header/footer variant, and CSS scope. If no surface prop is passed, it is inferred from the URL: paths starting with /the-manual/ render Manual; everything else renders FSCC.

// Surface scoping
<div class={surface === "manual" ? "S" : "F"}>
  <SiteHeader surface={surface} />
  <slot />
  <SiteFooter surface={surface} />
</div>

The .S and .F wrapper classes scope all surface-specific CSS. Shared components accept the surface prop and render different markup per brand.

Content Collections

Five Zod-validated content collections in content.config.ts:

CollectionRoute PatternContent Type
manualReference/the-manual/reference/[slug]Long-form articles with sources, reviewer, verification dates
manualFaq/the-manual/faq/[slug]Direct-answer pages with answerSummary and trustBasis
manualGlossary/the-manual/glossary/[slug]Term definitions with related entries
manualTool/the-manual/tools/[slug]Maps and calculators with methodology and disclaimer
fsccFeatureUsed on FSCC homepageFeature cards with eyebrow, group, and status

Routing and Rendering

Astro file-based routing maps directly to URL paths. Each Manual content type has an index page and a dynamic detail page. Rendering is pre-rendered in Node.js builds and dynamically SSR'd on Cloudflare Workers, controlled by FSCC_RUNTIME.

CMS Integration

EmDash provides an admin panel at /_emdash/admin for content editing. Content flows from markdown in src/content/ through Astro's content collections API. The build-emdash-seed.mjs script converts markdown into the EmDash seed format for initial bootstrap.

Collection schemas remain authoritative. Bootstrap/seed scripts must stay in sync with content model changes. Admin behavior should not silently diverge from the markdown schema model.

Shared Foundations

Color Tokens

TokenValueUsage
--ink#0f0e0cPrimary text, headings, links
--i2#3a3835Body copy, secondary text
--i3#6b6560Descriptions, lede text
--i4#706860Labels, tertiary text
--i5#8b8680Faint text, disabled states
--ink-6#c4c0b8Borders, dividers
--manual-bg#f0efebManual surface background
--fscc-bg#f5f4f0FSCC surface background
--rulergba(15,14,12,0.10)Hairline borders
--rule-srgba(15,14,12,0.18)Emphasis borders, section rules
Section Colors (Manual)
TokenValueSection
--c-reference#656260Reference articles
--c-faq#72633aFAQ entries
--c-glossary#5a5650Glossary terms
--c-tools#2a2825Tools section

Typography System

RoleFamilySizeWeight
DisplayDM Serif Displayclamp(3rem, 8vw, 5.2rem)400
Article TitleDM Serif Display2.8rem (desktop: clamp to 4.2rem)400
Section H2DM Serif Display1.4–1.8rem400
Body H2Inter1rem600
BodyInter0.92rem400
BlurbInter0.85–0.88rem400
LabelIBM Plex Mono0.44–0.55rem400–500
NavIBM Plex Mono0.52–0.58rem400
MetaIBM Plex Mono0.48–0.52rem400

Labels and meta text always use uppercase with letter-spacing between 0.04em and 0.16em. Serif is reserved for display and entry titles; body headings use Inter bold.

Readability Standard

The brand direction specs define very small type sizes (down to 0.44rem) for labels and metadata. These sizes are approved in the brand system but create a WCAG tension: when combined with uppercase, wide letter-spacing, and reduced contrast, they can fall below AA contrast thresholds. Review utility text against WCAG AA before shipping. Where a label is purely decorative, the small size holds. Where a label carries actionable information (form labels, filter controls, status indicators, trust metadata), ensure it meets contrast minimums at the rendered size.

Chevron System

Overlapping diagonal line pairs act as section dividers. Implemented in SurfaceDivider.astro as inline SVG:

ToneHeightStrokeOpacity
hero16px1.5px0.22
section12px1px0.12
end10px0.75px0.06

Chevrons are structural thresholds, not decoration. They separate content regions in the Manual and signal hierarchy.

Pennant Mark

Component
PennantMark.astro — inline SVG, viewBox 0 0 200 300
Sizes
sm (10×14px), md (14×20px), lg (28×40px), desktop hero (80×116px at 14% opacity)
Color
Section-specific fill using --c-reference, --c-faq, etc. Default --ink.
Props
kind (manual, reference, faq, glossary, tools), size (sm, md, lg), decorative

FSCC Register Mark

Component
FsccRegisterMark.astro — CSS pseudo-elements, 62% width bars, 1.5px height
Sizes
sm (14×7px), md (20×10px), lg (64×28px)
Subdued
--ink-6 color for footer usage
Clear space
6px minimum (equals bar height)

Components

SiteHeader.astro

Dual-mode header. Manual: pennant + wordmark + nav tabs with live counts + mobile dropdown. FSCC: register mark + name + mono nav.

SiteFooter.astro

Manual: chevron divider + section links with pennants + meta links. FSCC: subdued register mark + name + URL + footer links.

SurfaceDivider.astro

SVG chevron divider with three tones (hero, section, end). Padded variant available.

PennantMark.astro

Manual identity mark. SVG triangle in section-specific colors at three sizes.

FsccRegisterMark.astro

FSCC stepped-bar monogram. CSS pseudo-elements at three sizes with subdued variant.

ManualMetaBlock.astro

Trust metadata: reviewer, last reviewed, last verified, sources. Must remain readable and machine-extractable.

RelatedLinks.astro

Sidebar cross-reference links with collection-specific pennant marks.

ManualSectionBlock.astro

Section heading + item list for Manual homepage. Color-coded by section, shows up to 3 items.

ContentCard.astro

FSCC feature card with shelf bars, eyebrow, serif title, blurb, and CTA.

Breadcrumbs.astro

Mono breadcrumb trail for detail pages. Slash-separated path items.

FsccEmailCapture.astro

Newsletter signup with inline form. Posts to /api/email-capture.

ShopMap.astro

Interactive Leaflet map with region filters, search, and result list. Most complex interactive component.

Interaction & Accessibility

The brand direction docs define visual presentation. This section defines the interaction layer that every interactive component must implement. These are implementation requirements, not suggestions.

Required States

Every interactive element must define behavior for these states:

default

Resting state. Must be legible and signal affordance without hover.

hover

Current: indent shift + border darken on entry rows, color change on links. Hover enhances but cannot be the only signal.

focus-visible

Keyboard focus must be visually distinct. Do not rely on browser defaults disappearing into the warm palette.

active / pressed

Brief visual feedback on click/tap. Important for buttons and filter controls.

disabled

Reduced opacity, no pointer events. Used on submit buttons during loading, inactive filters.

error

Validation feedback for form inputs. Must be readable, not just a color shift on tiny mono text.

success

Confirmation state after form submission. Email capture needs this.

loading

Active data fetch or submission in progress. Map, filters, and email capture.

empty / no-results

No content matches the current filter or search. ShopMap and section indexes.

Keyboard Navigation

Interactive components must support keyboard use:

Touch Targets

Minimum touch target: 44×44px effective area. Several current patterns use small mono labels as tap targets (filter buttons, footer links, CTA links). These need adequate padding or hit-area expansion even when the visible element is small.

Motion

Current motion is restrained: 0.15s transitions on hover/focus for padding shifts, border-color changes, and opacity. All animated properties should respect prefers-reduced-motion:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
  }
}

Contrast and WCAG

All meaningful text must meet WCAG AA contrast (4.5:1 for normal text, 3:1 for large text). Current audit areas:

Where small mono labels carry actionable information (form labels, trust metadata, filter controls, status indicators), either increase the size or darken the color. Where labels are purely decorative type identifiers, the approved sizes hold.

Semantics and Machine Readability

Especially important for the manual's LLM/AEO mission. Templates must preserve:

the manual

Manual pages render inside the .S CSS scope with --pad controlling horizontal rhythm (1.25rem mobile, 3rem desktop). Background: #f0efeb.

Manual Homepage

Template
src/pages/the-manual/index.astro
Classes
.hero, .toc, .tg (table group), .te (table entry)
Layout
Hero (serif title + description, pennant on desktop) → chevron → 4 section blocks → chevron → footer
Desktop
Hero becomes 2-column grid with 80×116px pennant at 14% opacity. TOC sections max-width 72%.

Each section block (.tg) shows a section header with pennant mark, section name, and rule — followed by up to 3 entry rows (.te) with hover indent animation and a "View all" link.

Entry rows must remain scannable without hover. The indent shift (0.5rem on hover) enhances affordance but the default state must already read as a link through underline or other visual cue.

Section Index Pages

Templates
src/pages/the-manual/{reference,faq,glossary,tools}/index.astro
Classes
.six-head (section index heading), reuses .te entry rows
Layout
Section heading with pennant + title + count → full entry list → footer

Reference Article

Template
src/pages/the-manual/reference/[slug].astro
Classes
.ah (article header), .a-layout, .ab (article body), .a-side, .as (article sources)
Layout
Article header (pennant + type label + serif title + description) → hero chevron → 2-column grid (body + sidebar) → section chevron → metadata block
Desktop
Grid becomes 1fr 220px with 3rem gap. Sidebar is sticky at top: 1.5rem.
Body
H2 has full-width rule separator above via ::before. Body text at 0.92rem (0.95rem desktop), 1.75 line-height, max-width 58ch.

Trust Metadata

Reference and FAQ pages expose trust signals via ManualMetaBlock.astro. These are a core part of the manual's identity, not optional footer decoration.

Trust metadata must be human-readable and consistently labeled. It should also be available in structured form (JSON-LD) for machine extraction. If the label text is too small or low-contrast to read comfortably, the trust signal fails its purpose.

FAQ Page

Template
src/pages/the-manual/faq/[slug].astro
Special
.faq-answer block with mono label "Direct Answer" in --c-faq color, 1.05rem answer text (1.15rem desktop)
Title
Smaller serif: 2rem (vs 2.8rem for reference)

The direct answer block is the primary content. It must remain extractable by both human readers and LLM crawlers — clear heading hierarchy and answer-first structure.

Glossary Page

Template
src/pages/the-manual/glossary/[slug].astro
Special
.glo-def block with 2px top border in --ink, 1.05rem definition text (1.15rem desktop)
Title
Serif at 2.2rem

Tool Page

Template
src/pages/the-manual/tools/[slug].astro
Title
Sans-serif (Inter 600), functional not editorial: 1.5rem–2.2rem
Notes
Methodology, disclaimer, and formula version in .tool-notes blocks
Map tool
ShopMap.astro with region filter buttons (.tf), search input, Leaflet map, result list (.shop-row)

Tool pages have the highest interaction complexity. The ShopMap must define loading, empty, no-results, and error states. Filter buttons need focus-visible and active states. Search must support keyboard submission. Result rows must be navigable without requiring map interaction.

FSCC

FSCC pages render inside the .F CSS scope with --p: 20px (40px desktop) controlling horizontal rhythm. Background: #f5f4f0.

Horizon Bars

The .hz element is the FSCC equivalent of the Manual's chevron divider. Two overlapping horizontal lines (62% width, 1.5px height) — top-left and bottom-right — creating the stepped-bar rhythm that mirrors the register mark. Height: 24px, margin: 40px vertical.

Entry Rhythm

Image Entry (.entry)
  1. Image block (.img) with aspect ratio and gradient background
  2. Caption below in mono 0.42rem
  3. Shelf bar (.entry__shelf) — two 62%-width faint lines, 14px gap
  4. Type label in mono 0.44rem, warm color
  5. Serif title (DM Serif, 1.7rem mobile / 2rem desktop)
  6. Blurb (0.85rem, max 44ch mobile / 52ch desktop)
  7. Link (mono 0.52rem with bottom border and arrow)
Compact Entry (.entry--compact)
  1. Shelf bar
  2. Type label
  3. Sans title (Inter 700, 1.05rem / 1.15rem desktop)
  4. Blurb
  5. Link

Entries are separated by .closer hairlines (1px, faint opacity). Major sections are separated by .hz horizon bars. Entry links need keyboard focus treatment and adequate touch target area despite the small mono text.

Homepage Structure

Template
src/pages/index.astro
Sequence
Hz → Image entry (manual feature) → Closer → Compact entry (tool) → Closer → Compact entry (spotlight) → Hz → Manual widget → Hz → Email capture → Hz
Manual widget
.mw block with pennant, title, description, and stacked links to each Manual section with live counts

Header and Footer

Header
.F .hdr — flex row, space-between. Register mark + name left, mono nav right. Nav hidden on mobile.
Footer
.F .ftr — subdued register mark (lg) → brand name (0.6rem bold uppercase) → URL (mono 0.48rem) → footer links (mono 0.44rem)

Mobile currently hides the FSCC nav entirely. There is no hamburger or alternative mobile navigation pattern. Footer links serve as the only mobile navigation.

Email Capture

Class
.F .nl
Layout
Label (mono 0.52rem warm) + inline form (email input with bottom border + submit button) + output message
Desktop
Max-width 640px, centered
API
POST /api/email-capture with email, source, pagePath fields

The email form currently handles success state via output text. It needs: validation error messaging (empty, invalid email), disabled state during submission, clear success confirmation, and graceful failure if the API is unreachable.

Image Gradients

Placeholder gradients for entries without photography:

ClassGradient
.img--dark135deg: #2a2825 → #1a1918 40% → #2e2c28 70% → #1a1918
.img--warm145deg: #b8a898 → #8a7d72 → #6b6058 → #4a4440
.img--steel145deg: #c0bdb8 → #989490 → #787470 → #585450
.img--dust135deg: #c8b898 → #a89878 → #887860 → #685840
.img--fog180deg: #d0ccc4 → #b0a898 → #908880 → #706860

Gradients are placeholders. They should not become the de facto visual system. When photography is available, it replaces the gradient class.

Current Drift

Implementation status relative to the approved brand direction specs. This section tracks what needs work, not what the system should be — the source-of-truth specs live in the direction and direction docs.

Brand Documentation

The brand system is documented in three layers per brand. These are the source-of-truth specs — this implementation guide describes how the codebase realizes them.

the manual by FSCC

Field Scout Cycling Club

Relationship

Field Scout points. the manual explains. Both share the same codebase, design tokens, and typography stack but express distinct visual personalities through the surface system. The implementation must preserve that difference.