Foundations

Global decisions every other layer is built from. Defined in assets/css/foundations/.

Colour

Raw tokens define the palette. Components never use them directly: they read semantic tokens, which each surface remaps. Contrast is measured against each token's usual background.

Paper--color-paper#F7F6F2 / default background
White--color-white#FFFFFF / light surface, inputs
Stone--color-stone#ECEAE4 / alternate surface
Line--color-line#D8D6CF / hairlines
Border strong--color-border-strong#8A8881 / 3.3:1 on paper
Muted--color-muted#5C5B57 / 6.3:1 on paper
Ink--color-ink#121212 / 17.3:1 on paper
On dark--color-on-dark#F2F1ED / 16.6:1 on ink
Muted on dark--color-muted-on-dark#9D9B94 / 6.7:1 on ink
Brand--color-brandOne accent per view
Positive--color-positive#1F6B4F
Warning--color-warning#B7791F / dots only
Error--color-errorAlways with an icon
Code--code-bg#161615 / own surface

Semantic tokens (what components read)

TokenRoleDefaultDarkBrand
--surface-bgBackgroundpaperinkbrand
--surface-textTextinkon-darkwhite
--surface-mutedText mutedmutedmuted-on-darkwhite
--surface-lineBorder, hairlinelineink-linewhite 50%
--surface-line-strongInput and control bordersborder-strong#75736Dwhite
--surface-ruleSection-opening ruleinkon-darkwhite
--surface-subtleSurface alternate, fillsstoneink-raisedwhite 12%
--action-bg / --action-textPrimary action (brand contrast)ink / paperpaper / inkwhite / brand
--feedback-errorErrorerror#E9A08Ewhite + icon
--focusFocus ringinkon-darkwhite

Surfaces

Put data-surface on any section or element. Descendants inherit background, text, muted text, borders, actions and focus. The markup in each panel below is identical.

data-surface="default" Tag

Plan your next launch with confidence.

Muted text on this surface. An inline link.

data-surface="light" Tag

Plan your next launch with confidence.

Muted text on this surface. An inline link.

data-surface="alternate" Tag

Plan your next launch with confidence.

Muted text on this surface. An inline link.

data-surface="brand" Tag

Plan your next launch with confidence.

Muted text on this surface. An inline link.

data-surface="dark" Tag

Plan your next launch with confidence.

Muted text on this surface. An inline link.

data-surface="image" Tag

Plan your next launch with confidence.

Text on a photograph always sits on a scrim. An inline link.

Typography

Eight visual roles, two weights, two families. Classes are text-{role}, tokens are --text-{role}. Sizes are fluid between the mobile and desktop values with clamp().

Displaytext-display92 / 44, 0.98, −0.04emProportion
Page Titletext-page-title64 / 36, 1.0, −0.035emPlaces that last
Section Headingtext-section-heading40 / 28, 1.1, −0.025emFour ideas the system is built on
Card Titletext-card-title24 / 20, 1.25, −0.015emAlignment to the grid
Leadtext-lead20 / 18, 1.5, −0.005emIntros beneath a heading, comfortable at every size.
Bodytext-body16, 1.6Running text sits at a measure of 60 to 72 characters so the eye can find the next line without effort.
Smalltext-small14, 1.5Help text, metadata and supporting detail.
Captiontext-captionMono 12, 1.4, 0.02emFigure captions, labels and tokens

Structure and style are separate

Choose the element from the document outline, then apply the style the design needs. An h2 can be a Page Title in one place and a Section Heading in another. Never change a heading level to change a size.

ElementStyleWhere
<h1>DisplayHomepage hero statement
<h1>Page TitleInner page headers
<h2>Section HeadingSection titles
<h2>Page TitleClosing CTA statements
<h3>Card TitleCards within a section
<h3>Section HeadingPrinciples, values (feature rows)
<p>LeadIntros under a heading
<figcaption>CaptionMedia captions

Spacing

A 4px base scale for elements and components, plus three fluid roles for sections, headings and panels.

--space-14
--space-28
--space-312
--space-416
--space-524
--space-632
--space-748
--space-864
--space-996
--space-10128
Role tokenMobile to desktopUse
--space-section80 to 128Section padding
--space-heading32 to 56Section header to content
--space-component24 to 32Padding inside panels and cards
--grid-gap16, 20, 24Column and grid gaps
--page-gutter20, 40, 48, 64Page margins

Grid and breakpoints

Section labels hang in columns 1 to 3; headings and copy start at column 4. On mobile, labels stack above content. The live grid below uses the current viewport's column count.

ViewportQueryColumnsGutterMargin
Mobile< 48em (768)41620
Tablet≥ 48em82040
Laptop≥ 64em (1024)122448
Desktop≥ 90em (1440)122464

Containers: data-width

narrow / 720, long-form reading
standard / 1312, the default content width
wide / 1440, media that should run past the text

Shape, line and ratio

Radius

--radius-0
--radius-1
--radius-full

Surfaces, cards and media are square. Controls and tags use 4px. Full rounding is for status dots and avatars only.

Line and shadow


divider: 1px --surface-line
divider data-weight="strong": 1px --surface-rule

No decorative shadows. --shadow-overlay is reserved for menus and dialogs that float above the page.

Aspect ratios: data-ratio

square 1:1, portrait 4:5, standard 4:3, photo 3:2, video 16:9, cinema 21:9

Motion, focus and layers

Motion
--duration-fast120ms, hover and press
--duration-base200ms, state changes
--duration-slow320ms, drawers, dialogs
--ease-standardcubic-bezier(.2, 0, 0, 1)
Reduced motionAll durations 0, no movement

Focus: 2px outline, 2px offset, surface text colour

Focused Focused link
On dark

Press Tab anywhere on this page to see real focus states.

Z-index
--z-raised1
--z-dropdown100
--z-sticky200
--z-overlay300
--z-dialog400
--z-toast500

Icons

Inline SVG, 1.5px stroke in currentColor, on a 16px grid. Decorative icons carry aria-hidden="true"; an icon-only control needs an aria-label. Sizes: data-size 12, 14, 16, 20, 24.

arrow-right arrow-left external chevron-down menu close search check alert mail instagram linkedin

Primitives

Small, generic interface elements. They know nothing about case studies, people or posts. Defined in assets/css/primitives/.

Layout: container, grid, stack, cluster

container
Centres content and applies gutters. data-width: narrow, standard, wide, full.
grid
The 4 / 8 / 12 page grid. data-columns 2, 3, 4, 6 for equal columns; data-span and data-start on children for asymmetric layouts; data-divided for hairline cells.
stack
Vertical rhythm. data-gap 1 to 8 on the space scale.
cluster
Wrapping horizontal group. data-gap, data-justify.
1
2
3
4

grid data-columns="4" (2 on tablet, 1 on mobile)

span 3
span 9
span 7
span 5
start 4, span 6

grid with data-span / data-start (64em+)

Button and button group

Variants
data-variant: primary (default), secondary, text. data-size: default (48), small (44). data-width="full". data-icon-only.
Content
Label, optional trailing icon. Use <a> to navigate, <button> to act.
Accessibility
Never below 44px. Icon-only buttons need aria-label. Disabled links use aria-disabled="true".
default
small
disabled
default
small
disabled
brand
alternate

button-group data-stack="mobile": full-width 52px stack below 48em

Tags, badges, avatars and dividers

tag
Content labels and filters. <span>, <a> or <button>. Selected via data-selected, aria-pressed or aria-current.
badge
Status: data-status stable, review, error, planned, neutral. Text always accompanies the dot. data-variant="pill" for versions and counts.
avatar
Image or initials. data-size: small, medium, large. Group with avatar-group.
StableIn reviewDeprecatedPlannedv1.0
RH+4
StableIn reviewDeprecatedPlannedv1.0
RH

Form controls

field
Label above, control, help below, error below help. field__optional marks optional fields.
Controls
input (text, email, tel, number, url, search, password, date, textarea), select, check (checkbox, radio), switch, file, input-group (input + button).
Validation
form[data-validate]: samla.js shows inline errors with an icon, sets aria-invalid and aria-describedby, and adds a focusable error summary when there is more than one problem.
Accessibility
Every control has a visible <label>. Groups use <fieldset> and <legend>. 16px text prevents zoom on iOS. Errors never rely on colour alone.

We only use this to send release notes.

Enter a full address, including https://

Services
Studio
Attachments

We only use this to send release notes.

Enter a full address, including https://

Services
Studio

Try validation

form data-validate: submit empty to see the error summary and inline errors

Accordion and disclosure

Built on
Native <details> and <summary>. Works without JavaScript; a shared name makes the group exclusive.
Variants
data-size: default, small.
Can I use Samla with my own brand?

Yes. Replace the brand and surface tokens and every component follows.

Does it need a JavaScript framework?

No. Pages render as static HTML.

Which platforms are supported?

Any. Implementations keep the same class names and contracts.

Small accordion item

For dense lists such as footnotes or specifications.

Not exclusive: several can be open

Omit the name attribute.

On a dark surface

Rules, hover fill and focus come from the surface.

Tabs

Behaviour
Without JavaScript every panel shows with its own heading. samla.js adds the tablist, roving tabindex, arrow keys, Home and End.
Variants
data-orientation: horizontal, vertical. data-variant: line, contained.

Overview

Eleven hectares of former railway sidings returned to the river.

Specification

Five terraces built from ballast and spoil already on site.

Maintenance

A thirty-year plan agreed with the council's parks team.

tabs (horizontal, line). The homepage code viewer uses data-orientation="vertical" data-variant="contained".

Image and video

Variants
data-ratio: square, portrait, standard, photo, video, cinema. data-fit: cover, contain.
Content
Image with width, height and alt; optional caption. Video: a poster image and .media__play[data-src], which loads the player only on request.
Performance
Always set width and height. loading="lazy" below the fold, fetchpriority="high" on the largest image above it.
Site plan
square
Street
portrait
Park
photo, with caption
video: poster with play, loads an embed on click
Placeholder 16:9
no image yet: stone placeholder

Rich text and tables

prose
Wrap CMS rich text in .prose. It styles bare elements, so editors need no classes. Measure is capped at 68ch.
table
.table inside .table-scroll. Use <caption>, scope on headers and data-align="end" for numbers.

Landscapes can store carbon, cool cities and support wildlife, but only if they are designed with care.

Heading level two

Running text with an inline link, strong emphasis, emphasis, inline code and Ctrl + P.

Heading level three

  • An unordered list item
  • A second item, long enough to wrap onto another line at narrow widths so the hanging indent is visible
  1. An ordered step
  2. Another step

A tree planted in the right soil will outlive every other decision on the drawing.

Planting plan
A figure with a caption.
<div class="prose">…</div>
Embodied carbon, kgCO₂e per m²
Surface20202025
Concrete paving8261
Natural stone6448

Small print after a rule.

Components

Reusable combinations of primitives, with finite variants. Defined in assets/css/components/.

Section header

Content
label, title, intro?, action?
Variants
data-align: hang (default, Swiss hanging label), stack, center. data-rule: strong (default), none.
Guidance
Opens almost every section. The heading level follows the outline; the size is a text class.

Principles

Four ideas the whole system is built on.

An optional intro sits under the heading, in the same column.

data-align="hang" (default)

Stacked

Label above the heading.

data-align="stack"

Centred

For centred CTAs and landing pages.

data-align="center" data-rule="none", dark

Card

Replaces
Media card, article card, case study card, profile card, service card, location card. One component, different content.
Content
media?, meta?, title, summary?, tags?, person?, action?, url?
Variants
data-layout: stacked, horizontal, overlay. data-variant: plain, panel, bordered. data-size: default, large.
Accessibility
.card__link on the title stretches over the card: one link, one accessible name. Keep other links out of linked cards; the action text is aria-hidden.
  • Case studyHalden, 2025

    Millbrook Riverside Park

    Eleven hectares of railway sidings returned to the river.

    • Parks
    • Ecology

    plain, with tags

  • panel, no media, with person and action

  • bordered, service

  • profile: portrait media, meta after title

  • overlay: text over media on a scrim

data-layout="horizontal" data-size="large": featured items

Feature

Replaces
Feature item, feature card, step, value, principle, service summary.
Content
index? or icon?, title, text, link?
Variants
data-variant: rule, plain, panel. data-size: default, large, xlarge. data-layout: stacked, row. data-accent.
  1. 01

    Rule (default)

    An ink hairline above, mono index, small text.

  2. Large, with icon

    Card Title and body text.

  3. 03

    Panel

    Filled with the surface's subtle colour.

  4. 04

    Accent

    A brand rule marks the current item.

  1. Designed, not decorated.

    Row layout, xlarge: principles, values and service lists.

  2. Streets and public realm

    Row layout, large, with a link.

    Streets

feature-list with data-layout="row"

Quote

Replaces
Blockquote, pull quote, testimonial.
Content
text, name?, role?, avatar?
Variants
data-size: default, large, display. data-variant: plain, rule, panel.

We kept every tree that could be kept. The oldest birch is now the centre of the play area.

Sam WhitfieldPartner, Ecology

I drew the planting plan for a park I now walk through on my way to work.

Priya NairLandscape Architect

Fieldwork listened longer than anyone we have worked with. The park works because the people who use it helped design it.

Amara OkaforHead of Parks, Halden Council

data-size="large" on dark: testimonial sections

Stat

Content
value, label, text?. Wrap in <dl class="stat-list">: the label is the <dt>, the value its <dd>, shown first visually.
Variants
data-size: default, small. stat-list data-columns: 2, 3, 4.
Hectares of public space
146
Designed since 2009.
Trees planted
18,400
94% five-year survival.
Residents engaged
32k
Awards
11

Logo item and logo list

Variants
data-layout: rail (one row, scrolls on small screens), grid (divided).
Accessibility
Alt text is the organisation's name. Logos are muted until hover.

Meta list, link list, social links

meta-list
Key and value pairs: facts, contact details, job details. data-layout: inline, stacked, columns.
link-list
Rows of links with an arrow: documentation, jobs, events. Optional meta column. data-size: default, small.
social-links
44px icon links with visually hidden names.
Client
Halden Council
Completed
April 2025
Area
11.2 hectares
New projects
projects@fieldwork.example
Telephone
0117 946 0000
Release
1.0
Output
Static HTML
Team
24
Studios
2

Notice

Variants
data-status: info, positive, warning, error. Always an icon plus text.
Accessibility
role="status" for polite updates, role="alert" for errors after an action.

Studio closed 24 December

We reopen on 2 January.

Enquiry sent

A partner will reply within two working days.

Path closed for planting

Use the river entrance until March.

Upload failed

Files must be under 20 MB.

Dropdown, dialog, drawer and tooltip

dropdown
Button plus a menu of links or actions. Without JavaScript the options render inline. Escape and outside click close it.
dialog
Native <dialog>: focus is trapped and restored by the browser. Open with data-dialog-open="id"; close with data-dialog-close, Escape or a backdrop click. data-variant: modal, drawer. data-size: default, large.
tooltip
Supplementary text on hover and focus, dismissed with Escape. Never the only place information lives.
Biodiversity net gain The measurable improvement in habitat after development, required by law in England.

Modal dialog

Focus moved into this dialog and is trapped here. Press Escape, click the backdrop or use a button to close it; focus returns to the trigger.

Filters

Type
Studio

Code block

Content
file name?, code, line numbers (optional, data-line-numbers with one .line per line). Syntax colours: tok-keyword, tok-property, tok-string, tok-comment.
Behaviour
A copy button with data-copy copies the block's text.
theme.css
/* Rebrand a project: override raw tokens only */
@layer project {
  :root {
    --color-brand: #2B45C9;
    --font-sans: 'Inter', system-ui, sans-serif;
  }
}

Sections

Page bands. Every band is a .section with data-surface and data-spacing, containing a section header and a layout block. Card grids, feature grids, stats, testimonials and related content are compositions, not separate sections. Defined in assets/css/sections/.

Common sections and how they are composed
SectionComposition
Intro, rich contentsection + section-header + section__body[data-indent] + prose
Card grid, related contentsection + section-header + grid[data-columns] of cards
Feature grid, stepssection + section-header + grid of features
Stats, resultssection + section-header + stat-list (+ quote)
Testimonialssection[dark] + quote[large], or a carousel of quote panels
Logo gridsection + section-header + logo-list[grid]
Videosection + section-header + media[video] with play
FAQ, contactsection + split (header aside, accordion or form main)
Newslettersection + cta with a subscribe form

Hero

Content
eyebrow?, title, lead?, actions?, aside facts?, media?
Variants
data-layout: text, split, media, centered. data-align: left, center. data-width: narrow, standard, wide. data-surface: default, alternate, brand, dark, image.

data-layout="text" / alternate

Public spaces designed to be used.

Founded
2009
Studios
Bristol, Glasgow

Title across the grid, facts in columns 1 to 4, lead and actions from column 7.

data-layout="split"

Parks and open space

Content left, media right. Here the title uses Page Title instead of Display.

data-layout="media" / image

Learn landscape by building one.

The image fills the band behind a scrim that guarantees contrast.

data-layout="centered" / brand

One set of components, any brand.

A centred stack for campaigns and landing pages.

data-width="narrow" / dark

A narrower title column for shorter statements.

Media + content

Variants
data-media: start, end. data-align: center, start.

The studio

Designers, ecologists and engineers at one table.

Media in columns 1 to 6, content from column 8.

How we work

data-media="end"

Chartership, paid for and planned in.

  • LI fees paid
  • 10 study days

Split and FAQ

Variants
data-ratio: 4-8 (default), 3-9, 5-7, 6-6. data-sticky keeps the aside in view.

FAQ

Before you apply.

Do I need a landscape degree?

No. Show us how you look at places.

Bristol or Glasgow?

Both. You choose a home studio.

What happens after two years?

Most graduates stay.

CTA and newsletter

Variants
data-layout: stacked (default), inline, centered. Actions are a button group or a subscribe form. Place inside any surface.

Have a site in mind?

data-layout="inline" on brand.

Get the next essay by email.

data-layout="centered" with a subscribe form, on dark.

Stacked

Every project on this page started with a conversation.

Logo rail

Trusted by

Patterns

Opinionated arrangements for particular content. A site may use none, some or many. Defined in assets/css/patterns/.

Listing

Used for
Case studies, articles, services, team, events and careers indexes.
Parts
toolbar (filters, count, sort), results (card grid or link-list rows), pagination.
Filtering
Link filters (server-rendered, shareable) by default. For client-side filtering use buttons with data-filter inside [data-filter-root] and data-tags on results.

3 projects

Article layout

Used for
Articles, case study narratives, job descriptions, legal pages. Sticky aside for author, contents and sharing; prose body from column 4.

In the summer of 2022, the temperature on a play area we had opened three years earlier reached 58°C at ground level.

That afternoon changed how we design. Shade is now the first layer we draw, before paths or play.

Profile

Ruth Hallam

Founding partner

Ruth Hallam

Ruth co-founded Fieldwork in 2009 and leads the studio's park projects.

Studio
Bristol
Specialisms
Parks, heritage landscapes
Email
ruth@fieldwork.example

Careers, events, locations and results

Compositions of existing parts. No new CSS.

Location: bordered card + meta-list

Map of the Bristol studio

Bristol

Address
22 Feeder Road, Bristol BS2 0SB
Telephone
0117 946 0000

Case study results: stat-list + quote

Visits in year one
410k
Net gain
38%

The park works because the people who use it helped design it.

Amara OkaforHalden Council

Responsive previews

Live pages at phone and tablet widths. The menus work: each shows a different data-mobile variant.

390 / home / data-mobile="drawer"
390 / landing / data-mobile="fullscreen"
390 / samla / data-mobile="dropdown"
768 / case study