WRMP Exhibit Design System
The canonical inventory of tokens, components, and patterns behind WRMP's interactive "natural history museum" exhibits on wrmp.org.
Every exhibit is a single-page HTML experience rendered inside a fixed 16:9 frame: a story panel overlays a background layer (map, video, or photo), and a stepper drives the narrative. The specimens below are the pieces that make that up, each labeled with its class names and source files.
Foundations
Radix-backed color tokens, type ramp, spacing scale, and logo marks.
Exhibit Chrome
The story panel, nav bar, and step-level layouts that frame every exhibit.
Content Components
Stat pills, callouts, metric cards, and chart cards that live inside the story panel.
Map Components
Labels, legend chips, and marker popups that sit directly on the Leaflet layer.
Data Viz
Custom charts: species gallery, stacked bar, and the catch composition treemap.
Colors
Active palette maps WRMP semantic tokens to Radix color scales (Cyan, Amber, Orange, Grass,
Lime, Sky, Brown) via CDN
@radix-ui/colors@3.0.0. Alpha scales (--{color}-a1,
--{color}-a2, …) are also imported for subtle tints. Dark scale files redefine the
same variable names (--gray-1 … --gray-12,
--cyan-a1 …) under .dark or .dark-theme — they do not add
separate --gray-dark-1 names. Wrap dark UI in one of those classes so
var(--gray-12) resolves to the dark palette. WRMP original hex values are preserved
as --wrmp-*-brand reference tokens below.
Cyan 9
Cyan 12
Amber 9
Orange 9
Grass 9
Lime 9
Sky 9
Brown 9
Gray 11
Gray 12
App bg
Subtle bg
UI bg
Hover bg
Active bg
Subtle border
Border
Strong border
Solid bg
Solid hover
Lo-contrast text
Hi-contrast text
Below, the same --gray-* slots are shown inside .dark-theme (from
gray-dark.css) so you can see the dark scale values.
App bg (dark)
.dark-theme)
The same step names as light (--cyan-1 … --cyan-12, etc.); only
values change under .dark / .dark-theme. Hover a chip for the
token name.
#228b9c
#005e6a
#e09337
#d66b2c
#379352
#92bb4d
#00acec
#664d26
#4e4e50
#272525
Typography
Self-hosted Source Sans Pro. Apply text style classes to any semantic element —
.heading-display, .heading-lg, .heading-md,
.heading-sm. Add .subtle for de-emphasized color. ADA minimum: 16px
body copy. Map overlays (DivIcon labels and marker popups) use the same step scale; they are
grouped under
Map & marker typography
below.
Sampling occurs quarterly using standardized gear across Benchmark, Reference, and Project site types.
Multi-mesh panel, overnight set, 100m length
Gill Net
Spacing
4/8/16/24/40 scale. All layout and padding uses these tokens.
Logos
ADA variants for all digital use. Dark-mode version on teal/dark backgrounds (nav bar, cover slides, dark panels).
Story Panel
Glassmorphism left panel — 42% width, 16px inset from the exhibit frame edges. Two variants:
light (default) and dark. Step copy uses the shared .step-header pattern (icon
mark, kicker, title) before the body. Rendered here inside a mock exhibit frame so positioning
matches production.
Where We're Watching
The WRMP network spans 119 stations across three Bay-Delta regions.
Where We're Watching
The WRMP network spans 119 stations across three Bay-Delta regions.
Exhibit Cover
Full-bleed "title screen" that opens every exhibit. Sits at the top of
.exhibit-frame with z-index: 1000 so it always overlays the map, story
panel, and any floating chart cards until the visitor taps Step Inside. Hero
image is wired via a sibling .exhibit-cover-image div with an inline
background-image. The row of step dots previews how many stops lie ahead — the first
is pulsing. Source: shared/css/exhibit-cover.css, controller
#btn-enter.
The Bay: Where We're Watching
Explore how WRMP monitors the San Francisco Estuary across regions, networks, and restoration sites.
How We Monitor
A field guide to the gear and sampling design behind 14 years of Bay fish data.
End Card
The closing bookend to every exhibit. Shares the cover's hero image for visual continuity, but
the content block is a glassmorphism panel
(backdrop-filter: blur(18px)) so a longer body of links stays legible over a busy
photograph. Differences from the cover: step dots are all filled (completion state), a
links row presents the Tier-3 data-ecosystem destinations (featured chips use
brand teal), and two actions appear — a primary white pill and a ghost-outlined secondary.
Modifier class .exhibit-cover--end, controller #btn-restart.
The data runs deeper
This exhibit is one layer. WRMP's monitoring lives inside a larger data ecosystem — from the Bay-wide EcoAtlas toolset to the raw datasets scientists work with every day.
Explore the data ecosystemStep Dots
Journey-state primitives used by both the Exhibit Cover and the End Card. Three states total.
Base (.exhibit-cover-dot) is a semi-transparent white circle —
neutral "stop ahead." Pulsing (.is-pulsing) draws the eye to the
starting dot on the cover with a soft ring animation. Done
(.is-done) is solid white with a faint ring — used on the end card to signal
completion. Only reads against a dark backdrop; all three are shown on a matching dark swatch
below. The pulsing dot animates live — the
@keyframes cover-dot-pulse respects prefers-reduced-motion.
.exhibit-cover-dot — 8×8px,
rgba(255,255,255,0.45).
.is-pulsing — solid white with a 2.4s ring pulse
(@keyframes cover-dot-pulse).
.is-done — solid white with a 2px translucent ring for "filled-in"
feel.
Gallery Card
Poster tile used on /index.html to route visitors into an exhibit. 16:9
full-bleed hero, dark lower-third gradient for title legibility, kicker + title + semi-transparent
step dots at the bottom-left. The entire card is a single anchor — on hover or focus, the hero
scales to 1.04, the card lifts 3px, and a centered white
Begin exhibit pill fades in (YouTube-style affordance). Rules live inline in the
gallery page; they're inlined here as a mirrored specimen rather than extracted to
shared/css, because the gallery is the only consumer today.
Step Content
Each step opens with a step header row: the transparent WRMP icon mark
(wrmp-logo-2in-mark-crop.png) on the left, then
.step-header-text containing the kicker (thematic label, required on every step)
and the h2 title. Below that: h3 → h4 → body → stat pills
→ callout → caption as needed.
Native vs. Invasive
The 2014–2015 Drought Shift
Of the 237 species recorded, 113 are native fish and 38 are native invertebrates. A dramatic regime shift occurred during the 2014–2015 drought as salinity and temperature extremes favored non-native species.
South Bay Response
Invasive species now dominate biomass in South Bay and Suisun regions during dry years. Shimofuri goby and yellowfin goby increased by 340% between 2013 and 2016.
Content Area Overlay
Class: .content-area (toggle .content-area.active).
Absolutely positioned region to the right of the story panel inside .exhibit-frame;
hosts species gallery, bar charts, etc. Source:
exhibits/2026-04-10-native-vs-invasive/index.html (steps 2, 3, 5). Below, the same
layout is simulated inside .ds-map-mini (design system only).
In production, place .content-area siblings next to #map under
.exhibit-frame (same stacking context as the real exhibit).
.content-area {
position: absolute;
top: var(--panel-inset);
left: calc(var(--panel-width) + var(--panel-inset) + var(--panel-inset));
right: var(--panel-inset);
bottom: var(--panel-inset);
z-index: 5;
display: none;
flex-direction: column;
justify-content: center;
padding: var(--space-lg);
overflow: hidden;
}
.content-area.active { display: flex; }
Toggle with JS by adding or removing .active on the content root.
Infographic Background
Class: .infographic-bg (toggle
.infographic-bg.active). Full-bleed photo (or image) layer for non-map steps in
exhibits/2026-04-10-how-we-look/index.html. ::after adds a
left-to-right scrim for panel legibility; optional .photo-credit in the corner.
Typically wraps .gear-cards bottom-right.
Set background-image: url('…') on .infographic-bg for
production photos (see exhibit index.html).
.infographic-bg {
position: absolute;
inset: 0;
z-index: 2;
background-size: cover;
background-position: center;
display: none;
align-items: flex-end;
justify-content: flex-end;
padding: 24px;
padding-left: calc(var(--panel-width) + var(--panel-inset) + 24px);
}
.infographic-bg::after { /* scrim */ }
.infographic-bg.active { display: flex; }
In how-we-look, each gear step uses an
infographic-step* layer with a JPEG background-image. Toggle with JS
via .active.
Stat Pills
No fill — label on top (uppercase, muted), bold number below, optional sublabel. Pills in a row
are separated by a vertical divider on
:not(:last-child). Optional .sub adds a contextual line below the
number.
Callouts
4px light-green left bar, no fill, medium weight body text. Warning variant uses orange bar. Also shown in dark panel context.
Metrics Card
Flexible info card from shared/css/metrics-card.css. Slots: colored marker strip,
kicker, title, subtitle, stat number, percentage container (trend icon + badge), info list
(plain, labeled key-value, color indicator, tag). Omit any slot from the HTML to collapse it —
no modifier classes needed. Set --card-marker-color inline to match semantic
context; set --percentage-bg / --percentage-color
for negative trend states. For media overlays (photo/video), add
.metrics-card--dark.dark-theme to switch to the dark glass variant with
dark-resolved token text.
Kicker
Gill Net
Multi-mesh panel, overnight set, 100m length
Multi-mesh panel, overnight set, 100m length
Slough
Label
Multi-mesh panel, overnight set, 100m length
Label
tagOtter Trawl
OT14
Bottom-contact net, 1.5 kts, 5 min tow
Beach Seine
20' Seine
Shoreline enclosure, 37m × 1.2m
Trap
Fyke Trap
3' × 6' frame
Two 50' lead nets
First card shows all slots. Remaining cards show the gear variant (kicker + title + stat + plain
info items only — unused slots omitted from HTML). Set --card-marker-color inline
per card.
Overlay
Gill Net
Dark treatment over media
Slough
Mode
DarkSite Chart Card
Classes: .site-chart-card, .site-chart-card.visible,
.site-chart-title. Glassmorphism card anchored mid-right on the map; hosts a
<canvas> in exhibits/2026-04-10-restoration-network/index.html.
Opacity transitions on .visible. Scoped specimen uses real class names inside
.ds-map-mini.
Matches restoration-network (right: 24px, blur card over the map).
Exhibit-local CSS until promoted to shared CSS.
Map Labels
Non-interactive Leaflet DivIcon labels. .poi-label for key landmarks,
.station-label for individual monitoring sites. Type specs (step scale, weights)
live with the rest of the ramp in
Typography → Map & marker typography.
Marker Popup
Vanilla JS component — WRMP.makeMarkerPopup(opts) returns an
HTMLElement, consistent with the rest of the WRMP.* API. All visual
values use tokens.css custom properties. Load marker-popup.css and
marker-popup.js alongside the other shared assets. Text role summary
(.mp-title, .mp-tag, etc.):
Typography → Map & marker typography.
OBP 1 — Outer Bair Restoration
Tidal Pond Slough
RST 4 — North Delta Levee Breach
Riparian Corridor
Coyote Creek Fish Passage
Stream Restoration
OBP 1 — Outer Bair Restoration
Tidal Pond Slough
JS: shared/js/marker-popup.js → WRMP.makeMarkerPopup(opts) returns an
HTMLElement · CSS: shared/css/marker-popup.css · load
alongside
tokens.css
Map Legend Chips
Floating glassmorphism chips, top-right of the map. Clickable to filter map layers. Three states: default, active (bolder + elevated shadow), dimmed (45% opacity).
States — active: brighter bg + bold weight + stronger shadow · default: standard · dimmed: 45% opacity
Map & Marker Typography
Non-story text that sits on the Leaflet map: fixed DivIcon chips from
shared/css/exhibit-frame.css and rich popups from
shared/css/marker-popup.css. All use var(--font-body) /
var(--font-heading) and the step scale (--step-*). Full layout
specimens: Map labels · Marker popup.
OBP 1 — Outer Bair Restoration
Tidal Pond Slough
Species Gallery
Species cards use .metrics-card from shared/css/metrics-card.css.
Groups use .heading-md as the group label. Rows are flex
.gallery-row under a .gallery-group. Native cards set
--card-marker-color: #228B9C; invasive cards use
--card-marker-color: #E09337. Slots used: marker, kicker, title, subtitle,
indicator row.
Native
Longfin Smelt
Spirinchus thaleichthys
Benthic
Native
Chinook Salmon
Oncorhynchus tshawytscha
Pelagic
Invasive
Shimofuri Goby
Tridentiger bifasciatus
Benthic
Invasive
Yellowfin Goby
Acanthogobius flavimanus
Pelagic
Slots used: marker (native=teal, invasive=orange), kicker, title, subtitle, indicator row. Stat row and percentage container omitted — no numeric metric for species cards.
Stacked Bar Chart
Classes:
.bar-group, .bar-label, .bar-sublabel,
.bar-track, .bar-segment.native / .bar-segment.invasive,
.bar-legend-row, .bar-legend-item, .bar-legend-swatch.
Proportional stacked bars for native vs. invasive composition from
exhibits/2026-04-10-native-vs-invasive/; segment widths are set inline as
percentages. Exhibit-local styles.
⚠ Styles defined inline above until promoted to exhibit-frame.css — once moved, remove the <style> block from this page.
Species Treemap
Classes: .species-treemap, .species-treemap.active,
.species-treemap-card, .species-treemap-title,
.species-treemap-canvas. Full-bleed white overlay in
exhibits/2026-04-10-how-we-look/index.html; canvas uses a squarify layout
(DPR-aware) with habitat colors. Panel offset is applied as left padding on
.species-treemap-card.
The specimen canvas is decorative; the exhibit draws cells from catch data. Structure:
<div class="species-treemap" id="species-treemap">
<div class="species-treemap-card">
<canvas class="species-treemap-canvas" id="treemap-canvas"></canvas>
</div>
</div>
Squarify and redraw logic lives in the exhibit script; colors follow
:root habitat tokens in that file.