Variation

Master Blueprint · Design System v1.0

A neutral system, cohesively documented.

A single-source reference for the tokens, components, and motion driving the Master Blueprint. Every value resolves from src/data/project-decisions.ts.

01 · Color Palette

Five roles that do the work, three you can turn on.

A small palette is easier to keep consistent than a large one, so this system asks each colour to do one job: one colour for action, one for text, one for the page, one for lines, one for supporting copy. Three more are available when a brand needs them. Click a swatch to copy its HEX value.

Palette resolved from project-decisions.tsUnresolved

Contrast — computed, not stored

Mandatory for the five core roles: each against Paper, each against Ink, and the button pair — Accent fill with its foreground. Opt-in roles are checked once a project declares them.

Every pair used for text, computed from the approved palette. Body 4.5:1 · large text and UI 3:1.
PairSampleRatioGradeUsed for
Ink on Paper#1a1a1a on #ffffffAg17.4:1AAAAll body copy and headings
Ink on Wash#1a1a1a on #f8f8f8Ag16.39:1AAAHeadings inside wash bands
Muted on Paper#636363 on #ffffffAg6.01:1AASecondary copy, card body
Slate Deep on Wash Deep#565656 on #f1f1f1Ag6.5:1AAKicker / eyebrow text
Inverse on Ink#ffffff on #1a1a1aAg17.4:1AAACTA band copy and feature tile
Accent on Paper#c4c4c4 on #ffffffAg1.74:1FAILneeds 3:1Step numerals, quote marks
Rule on Paper#e5e5e5 on #ffffffAg1.26:1FAILneeds 3:11px borders and dividers
CTA — Inverse on Ink#ffffff on #1a1a1aAg17.4:1AAAPrimary button: "PLACEHOLDER CTA"

02 · Typography

One scale, one source.

Families, sizes, tracking and available weights all come from decisions.typography — no size is written in this route.

Typography resolved from project-decisions.tsUnresolved

H1

A neutral platform, ready for your brand.

48px / 3rem · tracking 0em · weights available 400/700

Hero headline (one per page)

H2

Structured, unopinionated, systematic.

36px / 2.25rem · tracking 0em · weights available 400/700

Section titles

H3

Component headline.

28px / 1.75rem · tracking 0em · weights available 400/700

Card titles · step labels

H4

Section sub-heading.

22px / 1.375rem · tracking 0em · weights available 400/700

Sub-headings within cards

H5shares Lead

A quiet aside that still gets read.

18px / 1.125rem · tracking 0em · weights available 400/700

Minor headings within content

H6shares Body

The smallest voice in the hierarchy.

16px / 1rem · tracking 0em · weights available 400/700

Smallest heading in the system

Lead

A neutral, high-whitespace framework whose type and colour resolve from one decision record — a canvas awaiting its brand.

18px / 1.125rem · tracking 0em · weights available 400/500/600/700

Lead paragraphs

Body

Standard body copy used across cards, lists, and default running text.

16px / 1rem · tracking 0em · weights available 400/500/600/700

Card copy · default body

Kicker

SECTION EYEBROW

10px / 0.625rem · tracking 0.22em · weights available 400/500/600/700

Eyebrows above section titles

Captionalso in the kit

Meta · caption · fine print

12px / 0.75rem · tracking 0em · weights available 400/500/600/700

Helper text · meta

Resolved heading sizes

Heading sizes step down at ≤992 and ≤768; the rest hold constant.
RoleTokenLarge Desktop (XL ≥1201)Small Desktop (Large 993–1200)Tablet (Medium 769–992)Mobile (Small ≤768)
H1--text-h148px / 3rem48px / 3rem36px / 2.25rem28px / 1.75rem
H2--text-h236px / 2.25rem36px / 2.25rem28px / 1.75rem22px / 1.375rem
H3--text-h328px / 1.75rem28px / 1.75rem22px / 1.375rem18px / 1.125rem
H4--text-h422px / 1.375rem22px / 1.375rem22px / 1.375rem22px / 1.375rem
H5shares Lead--text-lead18px / 1.125rem18px / 1.125rem18px / 1.125rem18px / 1.125rem
H6shares Body--text-body16px / 1rem16px / 1rem16px / 1rem16px / 1rem
Lead--text-lead18px / 1.125rem18px / 1.125rem18px / 1.125rem18px / 1.125rem
Body--text-body16px / 1rem16px / 1rem16px / 1rem16px / 1rem
Kicker--text-kicker10px / 0.625rem10px / 0.625rem10px / 0.625rem10px / 0.625rem

Live type specimens

One card per declared family. A specimen marked Fallback is not rendering the intended typeface — never approve type from it.

ui-serif

system · --font-serif

Checkingui-serif: checking webfont

Type is a decision, not a default.

  • 400

    Sphinx of black quartz, judge my vow.

  • 700

    Sphinx of black quartz, judge my vow.

AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz 0123456789 &@#$% ,.;:!?—“”‘’()

Headings and pull quotes. Never body copy at small sizes.

system-ui

system · --font-sans

Checkingsystem-ui: checking webfont

Type is a decision, not a default.

  • 400

    Sphinx of black quartz, judge my vow.

  • 500

    Sphinx of black quartz, judge my vow.

  • 600

    Sphinx of black quartz, judge my vow.

  • 700

    Sphinx of black quartz, judge my vow.

AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz 0123456789 &@#$% ,.;:!?—“”‘’()

Body copy, UI labels, forms and navigation.

Type scale at true size

Every declared step rendered at its real pixel size, so an off-scale value is visible rather than argued about.

Type scale at true size

Canonical steps: 10 · 12 · 16 · 18 · 22 · 28 · 36 · 48 px. A size outside them is flagged off-scale rather than tokenised.

  • h1 · 48pxThe quick brown fox
  • h2 · 36pxThe quick brown fox
  • h3 · 28pxThe quick brown fox
  • h4 · 22pxThe quick brown fox
  • lead · 18pxThe quick brown fox
  • body · 16pxThe quick brown fox
  • caption · 12pxThe quick brown fox
  • kicker · 10pxThe quick brown fox

03 · Spacing & Scale

One spacing scale, one spacing owner.

The kit's eight steps, the two-tier band scale, the BB Global Settings that make them work, and the row width classes — all read from the decision record and kit constants.

Layout resolved from project-decisions.tsUnresolved

Spatial rhythm — the kit scale

Eight steps, each bar at its true pixel width. A value outside these eight steps is a violation, not an extension — the build sheet reports it instead of tokenising it.

  • xs 4px
  • sm 8px
  • md 16px
  • lg 24px
  • xl 40px
  • 2xl 64px
  • 3xl 96px
  • 4xl 128px

Largest step: 128px. Section padding is never written in pixels on a row — it is owned by a jcd_band_* class.

Bands — the only owner of row vertical spacing

Row vertical spacing is owned by a jcd_band_* class. BB Row Padding stays at Global Settings default (0) — never set per row.

Band classes with desktop and mobile padding values
ClassDesktop (>768px)Mobile (≤768px)
jcd_band_sm40px24px
jcd_band_md64px40px
jcd_band_lg96px64px
jcd_band_xl128px96px

Modifiers

jcd_band_top_only
Keeps the top padding, zeroes the bottom
jcd_band_bottom_only
Keeps the bottom padding, zeroes the top
jcd_band_flush
Zeroes both — for rows that must butt against a neighbour

Beaver Builder breakpoints

Large
≤ 1200px
Medium
≤ 992px
Small
≤ 768px

Set these exactly in Tools → Global Settings

This table is the primary reference. Highlighted rows are the spacing-critical ones — get any of them wrong and band padding either doubles up or disappears.

Beaver Builder Global Settings values for this project
SettingValueCopy
Row Width (default)Full Width
Max Row Width1200px
Content Width1024px
Narrow Content Width (jcd_width_narrow)768px
Wide Content Width (jcd_width_wide)1200px
Row Paddingspacing-critical0px — spacing is owned by jcd_band_*, never the BB fields
Row Marginsspacing-critical0px all sides — margin moves the row BACKGROUND off the page edge and breaks full-bleed rows
Column Paddingspacing-critical20px
Column Marginsspacing-critical0px all sides
Auto Spacingspacing-criticalON — Auto Spacing zeroes horizontal padding below 768px at .fl-row[data-node] .fl-row-content-wrap — container insets go on .fl-row-content.
Apply to BoxON
Responsive Breakpoint — Largespacing-critical1200px
Responsive Breakpoint — Mediumspacing-critical992px
Responsive Breakpoint — Smallspacing-critical768px
Base Font Size100% — the px scale assumes a 16px root
Page Inset (jcd_bleed / .fl-row-content)spacing-critical24px
Default Heading FontInherited from _fonts.css — leave BB blank (typography UNRESOLVED)
Default Body FontInherited from _fonts.css — leave BB blank (typography UNRESOLVED)
Global Colors prefixjcd-color (NO leading dashes — BB prepends them)

Rows — width classes, graphically

Pick one width class and one band class. Touch no BB padding or margin field. Height is always auto — content height plus the band padding; there is no BB height field. A full-viewport hero uses a bespoke xxx_ min-height rule, never a band.

jcd_width_narrowFixed

Content width: 768px · Height: auto · Long-form reading measure

Content caps at 768px; below that width it fills the tier minus the page inset (24px).

jcd_width_defaultFixed

Content width: 1024px · Height: auto · The default content column

Content caps at 1024px; below that width it fills the tier minus the page inset (24px).

jcd_width_wideFixed

Content width: 1200px · Height: auto · Wide grids and galleries

Content caps at 1200px; below that width it fills the tier minus the page inset (24px).

jcd_bleedFull

Content width: full-bleed / none · Height: auto · Edge-to-edge media and colour fields

Spans the full viewport at every tier; only the page inset (24px) holds content off the edge.

Vertical rhythm is added independently

The same jcd_width_default frame with jcd_band_lg applied — 96px top and bottom on desktop, 64px at ≤768px. The width class never changes; the band adds the padding.

Class field output: section_is_example jcd_width_default jcd_band_lg

04 · Naming

Four families, split by responsibility.

System classes own spacing and width, semantic classes own identity, component classes own internal spacing only. The project family is documented but never emitted here.

Convention

Naming: Four Families By Responsibility

Token names are semantic, never descriptive — --accent, never --crimson. Client marketing names live in comments and swatch labels only. Classes use underscores so they stay visually distinct from fl-, pp- and uabb- output.

Layout

jcd_ SYSTEM

The ONLY classes permitted to carry row padding or width. Bands own vertical rhythm, widths own the measure, bleed removes both. BB Row Padding fields stay at their Global Settings default so stylesheet load order can never decide the winner.

  • jcd_band_xl
  • jcd_band_lg
  • jcd_band_md
  • jcd_band_sm
  • jcd_width_narrow
  • jcd_width_default
  • jcd_width_wide
  • jcd_bleed

Identity

section_is_ SEMANTIC

States what a section IS, never how it is spaced. Identity and spacing stay separate so a row can be re-rhythmed by swapping one band class without touching what the section means.

  • section_is_masthead
  • section_is_sb_hero
  • section_is_sb_problem
  • section_is_sb_plan
  • section_is_sb_cta

Kit

jcd_ui_ COMPONENT

Reusable kit components, implemented in the Savannah child theme. Internal spacing only — never row padding, never width. The ui_ segment exists so system and component classes stay mechanically distinguishable by a lint rule; do not collapse it back to jcd_.

  • jcd_ui_kicker
  • jcd_ui_btn_pill
  • jcd_ui_read_link
  • jcd_ui_pull_quote
  • jcd_ui_numbered_list
  • jcd_ui_field
  • jcd_ui_inverse

Client build only

xxx_ PROJECT

Bespoke rows and modules for this client only. These must NOT travel between sites — a saved row copied elsewhere should fail visibly rather than bind to a same-named class holding different values. Never emitted from the blueprint.

  • xxx_row_hero_banner
  • xxx_nav_logo_primary
  • xxx_card_feature

Beaver Builder Class Field string

Semantic class first, system classes after. Paste the string verbatim into the row’s Advanced > Class field, and leave Row Padding at its Global Settings default.

section_is_masthead jcd_band_xl jcd_width_narrow

Type steps — resolved from the typography decision

  • h1 48px
  • h2 36px
  • h3 28px
  • h4 22px
  • lead 18px
  • body 16px
  • caption 12px
  • kicker 10px

Both max-width queries apply below 768px, so every level that steps down at 992 must also be handled at 768 — otherwise two levels resolve to the same size and the heading hierarchy collapses at the narrowest breakpoint.

05 · Buttons

Every variant against every state.

States are rendered, not described. Focus-visible is always distinct from hover, and touch targets stay at 44px minimum.

Components

Button States — Every Variant, Every State

States are rendered, not described. A hover that changes only a 1px border, or one identical to the default, is a missing state. Focus-visible is always distinct from hover, and outline is never removed without a replacement.

Button variants against interaction states
VariantDefaultHoverFocus-visibleActiveDisabled
Primary — jcd_ui_btn_pillOne per viewport. Carries the approved CTA string, identical wording every time.
Secondary — jcd_ui_btn_pill is_ghostThe transitional CTA — a low-risk asset for the not-yet-ready reader.
Inverse — jcd_ui_inverse jcd_ui_btn_pillFor bands with an ink background. Contrast is verified against ink, not paper.

Touch targets stay at least 44×44px at lg and md. At md the primary CTA goes full width so it lands in thumb reach.

Reference only — buttons are maintained in the Savannah child theme and never copied per project. Copies drift; a maintained kit does not.

06 · Forms

Inputs that recover well.

Visible labels always, validation on blur, and error copy that says what happened, why, and the next step.

Components

Form Field States

Gravity Forms with the PowerPack Styler. Visible labels always — never placeholder-as-label. Validate on blur, keep submit disabled until valid, and write recovery copy that says what happened, why, and the next step. Never a raw code.

Rest

Focus

Filled

Error

Disabled

Filled in for you once the form is submitted.

Validated

Looks right.

Chunk fields in groups of five or fewer. Errors are announced with role="alert" and tied to the input with aria-describedby, so the state is never conveyed by colour alone.

Reference only — fields are maintained in the Savannah child theme.

07 · Image Aspect Ratios

Ratios and crop rules, not photography.

Placeholders are solid --rule blocks at the correct ratio — library entries never carry real media.

Media

Aspect Ratios & Crop Rules

Placeholders are solid --rule blocks at the correct ratio. Library rows never carry real media — a saved BB template links back to the source site rather than importing it.

Masthead / hero

Full-bleed row banner behind or beside the hero headline.

object-cover, focal point centre. At ≤768px switch to 3 / 2 so the subject survives the narrower measure.

Editorial feature

Section-level supporting image, Content Grid featured card.

object-cover. Holds at every breakpoint; no ratio switch.

Card thumbnail

Content Grid and Content Tiles cards, archive loop items.

object-cover, top-weighted focal point so faces are not cut.

Portrait

Team Member module, testimonial author, single-column feature.

object-cover. Never letterboxed — a fitted portrait reads as an error.

Square

Logo Grid cells, avatars, Info Box icon plates.

object-contain for logos, object-cover for photography.

08 · Outputs

One derived file.

01-tokens.css, the Global Colors table and the font enqueue snippet — all generated from the decision record.

Outputs

Export — 01-tokens.css

Derived, never authored. One file, plus the Global Colors table for manual entry and the font enqueue snippet for functions.php.

Generated file — never hand-edited. Drop it into the child theme's token layer, ahead of Savannah's _base.css.

/* ==========================================================================
   01-tokens.css — GENERATED FILE. DO NOT HAND-EDIT.
   Source: src/data/project-decisions.ts in the design blueprint.
   Client: UNNAMED CLIENT
   Decision status: Palette: UNRESOLVED · Typography: UNRESOLVED · Layout: UNRESOLVED · Navigation: UNRESOLVED · Footer: UNRESOLVED · Content & CTA: UNRESOLVED

   WARNING — 6 decision(s) NOT APPROVED.
   Do not build a client site against this file yet.

   Contains the --fl-global-* bridge and primitives ONLY.
   Semantic tokens, components, buttons, bands and focus states live in
   the Savannah child theme and are never copied per project.
   ========================================================================== */

:root {
  /* --- Bridge: Beaver Builder Global Colors -> kit primitives --------- */
  /* BB emits the left-hand names from Global Colors. The fallback is the  */
  /* approved hex, so a missing global degrades to correct, not to blank.  */
  --ink: var(--jcd-color-ink, #1a1a1a);
  --paper: var(--jcd-color-paper, #ffffff);
  --accent: var(--jcd-color-accent, #c4c4c4);
  --rule: var(--jcd-color-rule, #e5e5e5);
  --muted: var(--jcd-color-muted, #636363);
  --wash: var(--jcd-color-wash, #f8f8f8);  /* derived */
  --wash-deep: var(--jcd-color-wash-deep, #f1f1f1);  /* derived */
  --slate: var(--jcd-color-slate, #818181);  /* derived */
  --slate-deep: var(--jcd-color-slate-deep, #565656);  /* derived */
  --inverse: var(--jcd-color-inverse, #ffffff);  /* derived */
  --alert: var(--jcd-color-alert, #b3261e);

  /* --- Type primitives ------------------------------------------------ */
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-serif: ui-serif, Georgia, 'Times New Roman', serif;
  --type-h1: 48px;
  --type-h2: 36px;
  --type-h3: 28px;
  --type-h4: 22px;
  --type-lead: 18px;
  --type-body: 16px;
  --type-caption: 12px;
  --type-kicker: 10px;
  --tracking-tight: -0.01em;
  --tracking-body: 0em;
  --tracking-kicker: 0.22em;
  --tracking-meta: 0.16em;

  /* --- Layout primitives ---------------------------------------------- */
  --content-max: 1024px;
  --width-narrow: 768px;
  --page-inset: 24px;
  --bp-lg: 1200px;
  --bp-md: 992px;
  --bp-sm: 768px;
}

html { font-size: 100%; }
Beaver Builder Global Colors

Prefix is jcd-color with no leading dashes — BB prepends them. Entering “--jcd-color” yields ----jcd-color-* which resolves to nothing and drops silently.

  1. 1Ink#1a1a1a
  2. 2Paper#ffffff
  3. 3Accent#c4c4c4
  4. 4Rule#e5e5e5
  5. 5Muted#636363
  6. 6Wash#f8f8f8derived
  7. 7Wash Deep#f1f1f1derived
  8. 8Slate#818181derived
  9. 9Slate Deep#565656derived
  10. 10Inverse#ffffffderived
  11. 11Alert#b3261e

Enqueue, never @import — an @import blocks the render and cannot be given a preload or a display parameter.

What this file deliberately does NOT contain

Semantic tokens, components, buttons, bands and focus states are owned by the Savannah child theme (Beaver Builder Theme). Reference them by name; never re-specify their CSS per project.

Layer order

  1. _tokens.css (project — generated)
  2. _fonts.css (project)
  3. _base.css (Savannah)
  4. _components.css (Savannah)
  5. _navigation.css (Savannah)
  6. _modules.css (project)

Client: UNNAMED CLIENT · project prefix xxx_ (documented here, never emitted).