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.
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.
Where implementation and brand docs diverge, pull the implementation toward the approved brand logic. Do not rationalize drift after the fact.
Labels, metadata, nav items, and form controls must remain readable as interface elements. Atmospheric styling cannot undermine utility text.
Hover is not enough. Focus, keyboard navigation, validation states, loading indicators, empty states, and error handling are all implementation requirements.
/uploads directorysite.css, ~3000 lines) with custom properties. No Tailwind, no CSS-in-JS.content.config.ts — Zod schemas for all collectionslive.config.ts — EmDash CMS configurationastro.config.mjswrangler.jsonc
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.
Five Zod-validated content collections in content.config.ts:
| Collection | Route Pattern | Content 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 |
| fsccFeature | Used on FSCC homepage | Feature cards with eyebrow, group, and status |
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.
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.
| Token | Value | Usage |
|---|---|---|
| --ink | #0f0e0c | Primary text, headings, links |
| --i2 | #3a3835 | Body copy, secondary text |
| --i3 | #6b6560 | Descriptions, lede text |
| --i4 | #706860 | Labels, tertiary text |
| --i5 | #8b8680 | Faint text, disabled states |
| --ink-6 | #c4c0b8 | Borders, dividers |
| --manual-bg | #f0efeb | Manual surface background |
| --fscc-bg | #f5f4f0 | FSCC surface background |
| --rule | rgba(15,14,12,0.10) | Hairline borders |
| --rule-s | rgba(15,14,12,0.18) | Emphasis borders, section rules |
| Token | Value | Section |
|---|---|---|
| --c-reference | #656260 | Reference articles |
| --c-faq | #72633a | FAQ entries |
| --c-glossary | #5a5650 | Glossary terms |
| --c-tools | #2a2825 | Tools section |
| Role | Family | Size | Weight |
|---|---|---|---|
| Display | DM Serif Display | clamp(3rem, 8vw, 5.2rem) | 400 |
| Article Title | DM Serif Display | 2.8rem (desktop: clamp to 4.2rem) | 400 |
| Section H2 | DM Serif Display | 1.4–1.8rem | 400 |
| Body H2 | Inter | 1rem | 600 |
| Body | Inter | 0.92rem | 400 |
| Blurb | Inter | 0.85–0.88rem | 400 |
| Label | IBM Plex Mono | 0.44–0.55rem | 400–500 |
| Nav | IBM Plex Mono | 0.52–0.58rem | 400 |
| Meta | IBM Plex Mono | 0.48–0.52rem | 400 |
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.
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.
Overlapping diagonal line pairs act as section dividers. Implemented in
SurfaceDivider.astro as inline SVG:
| Tone | Height | Stroke | Opacity |
|---|---|---|---|
| hero | 16px | 1.5px | 0.22 |
| section | 12px | 1px | 0.12 |
| end | 10px | 0.75px | 0.06 |
Chevrons are structural thresholds, not decoration. They separate content regions in the Manual and signal hierarchy.
PennantMark.astro — inline SVG, viewBox 0 0 200 300--c-reference, --c-faq, etc. Default --ink.kind (manual, reference, faq, glossary, tools), size (sm, md, lg), decorativeFsccRegisterMark.astro — CSS pseudo-elements, 62% width bars, 1.5px height--ink-6 color for footer usageDual-mode header. Manual: pennant + wordmark + nav tabs with live counts + mobile dropdown. FSCC: register mark + name + mono nav.
Manual: chevron divider + section links with pennants + meta links. FSCC: subdued register mark + name + URL + footer links.
SVG chevron divider with three tones (hero, section, end). Padded variant available.
Manual identity mark. SVG triangle in section-specific colors at three sizes.
FSCC stepped-bar monogram. CSS pseudo-elements at three sizes with subdued variant.
Trust metadata: reviewer, last reviewed, last verified, sources. Must remain readable and machine-extractable.
Sidebar cross-reference links with collection-specific pennant marks.
Section heading + item list for Manual homepage. Color-coded by section, shows up to 3 items.
FSCC feature card with shelf bars, eyebrow, serif title, blurb, and CTA.
Mono breadcrumb trail for detail pages. Slash-separated path items.
Newsletter signup with inline form. Posts to /api/email-capture.
Interactive Leaflet map with region filters, search, and result list. Most complex interactive component.
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.
Every interactive element must define behavior for these states:
Resting state. Must be legible and signal affordance without hover.
Current: indent shift + border darken on entry rows, color change on links. Hover enhances but cannot be the only signal.
Keyboard focus must be visually distinct. Do not rely on browser defaults disappearing into the warm palette.
Brief visual feedback on click/tap. Important for buttons and filter controls.
Reduced opacity, no pointer events. Used on submit buttons during loading, inactive filters.
Validation feedback for form inputs. Must be readable, not just a color shift on tiny mono text.
Confirmation state after form submission. Email capture needs this.
Active data fetch or submission in progress. Map, filters, and email capture.
No content matches the current filter or search. ShopMap and section indexes.
Interactive components must support keyboard use:
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.
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;
}
}
All meaningful text must meet WCAG AA contrast (4.5:1 for normal text, 3:1 for large text). Current audit areas:
--i4 (#706860) on --manual-bg (#f0efeb) = 3.8:1 — passes for large text only--i5 (#8b8680) on backgrounds = ~2.8:1 — fails AA for body text, acceptable for decorative use only--i4 color are the highest-risk combinationWhere 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.
Especially important for the manual's LLM/AEO mission. Templates must preserve:
main, nav, aside, footer)manualSchema() in lib/schema.ts)
Manual pages render inside the .S CSS scope with
--pad controlling horizontal rhythm (1.25rem mobile, 3rem desktop).
Background: #f0efeb.
src/pages/the-manual/index.astro.hero, .toc, .tg (table group), .te (table entry)
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.
src/pages/the-manual/{reference,faq,glossary,tools}/index.astro.six-head (section index heading), reuses .te entry rowssrc/pages/the-manual/reference/[slug].astro.ah (article header), .a-layout, .ab (article body), .a-side, .as (article sources)1fr 220px with 3rem gap. Sidebar is sticky at top: 1.5rem.::before. Body text at 0.92rem (0.95rem desktop), 1.75 line-height, max-width 58ch.
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.
src/pages/the-manual/faq/[slug].astro.faq-answer block with mono label "Direct Answer" in --c-faq color, 1.05rem answer text (1.15rem desktop)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.
src/pages/the-manual/glossary/[slug].astro.glo-def block with 2px top border in --ink, 1.05rem definition text (1.15rem desktop)src/pages/the-manual/tools/[slug].astro.tool-notes blocksShopMap.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 pages render inside the .F CSS scope with
--p: 20px (40px desktop) controlling horizontal rhythm.
Background: #f5f4f0.
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.
.img) with aspect ratio and gradient background.entry__shelf) — two 62%-width faint lines, 14px gap
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.
src/pages/index.astro.mw block with pennant, title, description, and stacked links to each Manual section with live counts.F .hdr — flex row, space-between. Register mark + name left, mono nav right. Nav hidden on mobile..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.
.F .nl/api/email-capture with email, source, pagePath fieldsThe 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.
Placeholder gradients for entries without photography:
| Class | Gradient |
|---|---|
| .img--dark | 135deg: #2a2825 → #1a1918 40% → #2e2c28 70% → #1a1918 |
| .img--warm | 145deg: #b8a898 → #8a7d72 → #6b6058 → #4a4440 |
| .img--steel | 145deg: #c0bdb8 → #989490 → #787470 → #585450 |
| .img--dust | 135deg: #c8b898 → #a89878 → #887860 → #685840 |
| .img--fog | 180deg: #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.
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.
src/pages/index.astro, fsccFeature collection
Spec
FSCC Direction — entry rhythm and homepage structure
src/pages/
Spec
FSCC Direction — feature pages
site.css globally, ShopMap.astro, FsccEmailCapture.astro, nav dropdown
--i4/--i5 color fall below AA in some contexts
Expected
Decorative labels may stay small. Actionable labels (form controls, trust metadata, filter buttons) must meet AA.
Affected
site.css — .entry__type, .faq-answer__label, .tf__b, .ftr__link, .as__l
.F uses --ti-dark, --ti-pol, --ti-mid. .S uses --i2, --i3, --i4.
Expected
Unified naming across surfaces (same hex values, same token names)
Affected
site.css .F block (line ~2466)
.site-header, .site-footer, .manual-section-block classes coexist with direction-aligned .S/.F system
Expected
Legacy classes removed after confirming no template references
Affected
site.css lines ~222–600
#9e9790
Expected
#8b8680 (matches --i5 in .S and direction spec)
Affected
site.css .F block
.S .a-layout
Expected
Proportional column per direction spec
.img--3x2 used. CSS for --4x5, --1x1, --16x9 exists but is never applied.
Expected
Content-driven aspect ratio selection per entry
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.
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.