Documentation menu
Documentation / Blocks & themes / theme-roster
Theme Roster
npm install @wabbit/tome-theme-rosterFirst install from our registry?
Get a registry token from your credentials page. You need a purchase that includes this package, or a Craft Library membership.
Add the registry and your token to the
.npmrcat the root of your project, with your token in place ofYOUR_TOKEN:@wabbit:registry=https://npm.wabbit.com/ //npm.wabbit.com/:_authToken=YOUR_TOKEN
Roster, a Tome theme for businesses that run classes on a timetable and sell a term of them: gymnastics, dance, swim, martial arts, camps and preschools. A printed gym timetable in one darker frame, a level stripe as the one recurring mark, Bricolage Grotesque with Atkinson Hyperlegible Next, and one filled action per page. Four palettes (Chalk, Lane, Camp, Sprout) plus derived opposite modes, self-hosted OFL fonts, activated by data-tome-theme="roster".
Overview
Roster is a Tome theme for businesses that run classes on a timetable and sell a term of them: gymnastics, dance, swim, martial arts, camps and preschools. Its stance is "come try a class". The reader is usually a parent on a phone, so a Roster page answers, in order: is there a class for my child's age, when is it, who teaches it, and is it safe. Then it makes the first step small, a free trial class. It is warm without being cute, and every reassurance on the page is something a parent could check.
This package is a theme (a ThemeManifest of kind: 'theme'). It owns no blocks. It declares the whole token surface blocks read, self-hosts its two OFL font families, and restyles blocks and chrome through their public hooks. The blocks it is composed from (local-class-finder, local-class-schedule, local-class-pathway, local-credentials and local-price-list in @wabbit/tome-blocks-local-pack) read their classes from the opt-in local-class-schedules collection in @wabbit/tome-local.
Layer: ui · Family / tier: themes / addon · Versioning: independent.
Peer dependencies
| Peer | Range | Required |
|---|---|---|
@wabbit/tome-ui | >=0.20.0 <1.0.0 | yes |
@wabbit/tome-core | >=1.23.0 <2.0.0 | no (optional) |
@wabbit/tome-ui is a CSS-contract peer. Its tokens.css re-declares the var()-aliased Layer 2 under [data-tome-theme] and defines the fixed-band ink family this theme sets, and it ships the platform scroll reveal Roster turns on (0.20.0 adds the font-waiting arm, --tome-reveal-wait: fonts). @wabbit/tome-core is needed only for the ./register subpath and the ThemeManifest type.
What is in the first release
Roster ships the full treatment set for a class-based business's home page and program pages: four palettes with their derived dark modes, the type and rhythm tokens, the self-hosted fonts, the section opener, the Navbar 1 and Footer 7 chrome treatment (including the hours-aware call and the phone bar), the class finder (ask and level), the Safe Hands strip and panel, the framed timetable with its phone strip and print sheet, the pathway (bands, cards and ladder), the coaches, the first-class steps, parent voices, the term fee cards, the FAQ, the close, the program page's detail, next-sessions card and schedule slice, the level stripe, the tags and spots marks, and the motion tokens with their reduced-motion paths. ROSTER_STYLED_BLOCKS lists every block with a treatment.
Usage
Import the stylesheet once at the app root, after tome-ui's tokens, and activate the theme on <html>:
// app/layout.tsx
import '@wabbit/tome-ui/tokens.css'
import '@wabbit/tome-theme-roster/styles.css'
import '@wabbit/tome-theme-roster/fonts.css'<html data-tome-theme="roster"> <!-- Chalk, light -->
<html data-tome-theme="roster" data-tome-palette="lane"> <!-- Lane -->
<html data-tome-theme="roster" data-tome-palette="camp"> <!-- Camp -->
<html data-tome-theme="roster" data-tome-palette="sprout" data-theme="dark">With no data-tome-palette the theme renders Chalk. All four palettes are light by design, with alternating ground and tint bands and no dark bands; data-theme="dark" gives each its derived dark mode, which exists for conformance and for a site that wants one.
To have the admin ThemeSwitcher, createThemeConfig() and other registry readers list it, register it on the server before building the Payload config:
import '@wabbit/tome-theme-roster/register'Header: Navbar 1
Roster recommends the tome-chrome Header global on Navbar 1 (design version '1'): the wordmark, the links, a quiet "Call us, open till 7" note and the one filled "Book a free trial". The header sits on the page ground and earns a hairline once the page scrolls ([data-nav-scrolled="true"], which Navbar 1 reports when the Header global turns on reportScrolled). The call to action is the action fill, a 44px pill; the phone call beside the hamburger is an outline pill; both are set through chrome's --tome-header-cta-* and --tome-header-call-* knobs. The desktop call (headerCall.showOnDesktop) and the hours-aware drop of the call (callWhen: 'open') are styled to the contract chrome's open-state seam will emit.
The name is your LogoComponent. Mark it up with the lockup hook: <span data-roster-wordmark>Your Gym</span>. It is Bricolage 700 at 22px (24px in the footer). A level stripe's segments inside it (data-level-segment) draw as 10px dots.
Footer: footer 7
Set the Footer global to design version '7'. A hairline above; group labels in Atkinson 700, links in the meta size, text rows ([data-footer-text-row], emphasis [data-footer-text-strong]) for the front desk's hours and address, and the fine print ([data-footer-fine-print]) for the credits line, ruled off and at full width. There is no footer call-to-action band: the closing enrol band is the last ask.
The hero
The class finder is "Start with your child's age": two pill questions (an age band, then experience) answer in place with one result card holding the said-back line, the class, its level stripe and level line, this week's times, a real spots line, the one filled trial button and a link to the timetable. From 1024px a single tall photo stands in the right lane, from grid line 12 to the content edge, at the copy column's height; below that it shows once, after the result card (4:3 on phones, 3:2 on tablets). The treatment is written against the hook contract (data-class-finder, data-finder-*).
The level stripe
The theme's one recurring mark: one 14x7 pill per level in the learner's track, 3px apart, filled in the accent mark up to the learner's level and outlined in --input beyond it. It always ships with text ("Level 2 of 5") and its markup is aria-hidden. It fills once to the real level when it first enters view, only where motion is allowed: the stripe's island sets data-stripe-fill="armed" on mount and "in" on first sight. Under prefers-reduced-motion the collapsed state does not exist, so the stripe is always drawn at its real level.
Scroll reveal
Roster turns on tome-ui's opt-in platform scroll reveal (--tome-reveal: on) with a small rise and fade: --tome-reveal-rise 12px, --tome-reveal-duration 420ms, --tome-reveal-stagger 80ms. It also sets --tome-reveal-wait: fonts, which tome-ui releases that read the token use to arm only after fonts and layout settle. The reveal runs only where the site mounts the controller:
import { TomeReveal } from '@wabbit/tome-ui/reveal'
// inside <body>, once
<TomeReveal />Without the controller nothing moves and nothing is hidden. Blocks that animate themselves (data-tome-motion="self") are skipped by the controller. data-tome-reveal="off" on <html>, a region or a block root turns it off there, and overrides the theme. Pills answer a press with a small spring (--roster-spring, a 0.97 scale), only where motion is allowed.
Fonts
Bricolage Grotesque (one variable face carrying opsz 12 to 96 and wght 500 to 800, with the width axis pinned) and Atkinson Hyperlegible Next (upright and italic, each one variable face carrying wght 400 to 700) ship inside the package as latin-subset woff2, with their SIL OFL 1.1 licences (fonts/OFL-BricolageGrotesque.md, fonts/OFL-AtkinsonHyperlegibleNext.md). There is no third-party font request by default. Three ways to deliver them:
import '@wabbit/tome-theme-roster/fonts.css': the bundler resolves the files from this package.- Your own host: copy the files from
@wabbit/tome-theme-roster/fonts/*and injectgetRosterFontStyles({ src }). next/font/local, using the file list fromgetRosterFontFaces().
import { getRosterFontPreloads } from '@wabbit/tome-theme-roster/fonts'
// in <head>: preload the display roman and the body roman
{getRosterFontPreloads({ src: '/fonts/roster/' }).map((l) => <link key={l.href} {...l} />)}Both faces are cut from the full TTFs in the google/fonts repository at a pinned commit (scripts/subset-fonts.py, SHA-256 checked), not from the Google Fonts CSS API, because the served builds strip layout features. The subset keeps tnum in both faces: the timetable depends on "4:30" and "11:00" stacking on one column in every row, and Atkinson's tabular advance is 632 units at every weight from 400 to 700. Neither face has U+2197, so the new-tab mark is a CSS mask on [data-new-tab]::after, never a glyph.
Bricolage sets headings at 700 (the hero at 720) and 800 only for a single oversized numeral; its default instance is 96pt ExtraBold, so every Roster rule that sets it also sets its weight, and font-optical-sizing: auto opens the hero up while small headings stay compact. Atkinson is 400 at the one body size (19px), 700 for strong, and 600 for the sentence-case meta voice and tags. Italic means a quoted parent: a testimonial's quote is the only italic.
The grain effect
There is one opt-in effect: a faint fractal-noise grain on the ground and the header frame. It is static, Sprout in light mode only (every other palette and mode declares its grain as none), and it never touches the footer.
<html data-tome-theme="roster" data-tome-palette="sprout" data-roster-effect="grain">
<section data-roster-effect="grain">…</section>Sites can also mark their own markup with data-roster-meta (the meta voice), data-roster-figures (tabular figures) and data-roster-num (a single oversized Bricolage numeral).
Public API
| Export | Subpath | Purpose |
|---|---|---|
rosterThemeManifest (ThemeManifest) | ., ./manifest | The theme's manifest: name roster, kind: 'theme', tier addon, four palettes, fonts and one pairing, recommended blocks and chrome (Navbar 1, footer 7), styledBlocks, requires.tomeUi. |
ROSTER_PALETTE_IDS, RosterPaletteId | ., ./manifest | ['chalk', 'lane', 'camp', 'sprout'], the data-tome-palette values. |
ROSTER_PALETTE_DEFAULT_MODES | ., ./manifest | The mode each palette opens in: light for all four. |
ROSTER_STYLED_BLOCKS, ROSTER_RECOMMENDED_BLOCKS, ROSTER_PENDING_CATALOG_BLOCKS | ., ./manifest | The block slugs the theme restyles, the slugs its demo is composed from, and the ones blocks-local-pack has not yet registered in the block catalog. |
ThemeManifest (type) | . | Re-exported from @wabbit/tome-core/registry/theme. |
getRosterFontFaces(), getRosterFontStyles({ src }), getRosterFontPreloads({ src }) | ., ./fonts | Font descriptors, @font-face CSS for a custom host, and preload links. |
ROSTER_FONT_STACKS, ROSTER_FONT_LICENSES, ROSTER_LATIN_UNICODE_RANGE, ROSTER_DISPLAY_WEIGHTS, ROSTER_UI_WEIGHTS | ., ./fonts | The CSS stacks the tokens use, the licence file per family, the subset range, and the weights used. |
RosterFontFace, RosterFontPreloadDescriptor, RosterFontStylesOptions (types) | ., ./fonts | The descriptor shapes. |
| side effect | ./register | Registers rosterThemeManifest in tome-core's theme registry. |
| stylesheet | ./styles.css | Tokens, treatments and the effect. The file that does everything visual. |
| stylesheet | ./fonts.css | The three @font-face rules. Optional. |
| font files | ./fonts/* | The woff2 files and their OFL licences, for self-hosting. |
import { rosterThemeManifest } from '@wabbit/tome-theme-roster/manifest'
rosterThemeManifest.palettes.map((p) => p.label)
// ['Chalk', 'Lane', 'Camp', 'Sprout']Tokens
The theme sets Layer 1 (the names tome-ui's tokens.css reads: --background, --foreground, --card, --primary, --accent, --muted, --border, --input, --ring, the feedback, chart and muted2 names) and lets tome-ui's generated re-resolution derive the aliased Layer 2. It sets the Layer-2 names that have no Layer-1 input behind them:
--tome-color-accent-text, the feedback*-textweights, and--tome-color-surface-tint- the fixed-band inks (
--tome-color-surface-solid-darkand its family,--tome-color-surface-inverseand its family) - the class schedule's own knobs, set explicitly:
--tome-schedule-today,-tag-bg,-tag-ink,-action-bg,-action-ink,-row-bg - the four
--tome-type-*families, each paired with its--font-*slot
The colour roles Roster adds are private: --roster-mark (the accent as punctuation), --roster-tint, --roster-tag-on-tint, --roster-stripe-empty, --roster-action, -action-hover, -on-action (the one fill), the confirm, error and accent tints, --roster-placeholder, the grain, and the timetable frame: --roster-frame (one tone darker than the page), --roster-frame-today, --roster-frame-edge and --roster-frame-rule. Anything drawn straight on the frame takes --roster-frame-edge, never --input, which measures 2.84 to 2.90 on the light frames.
Selector ladder (specificity, never load order):
| Selector | Specificity | Holds |
|---|---|---|
[data-tome-theme="roster"] | 0,1,0 | Chalk light, type, radius, rhythm, lanes, motion |
[data-tome-theme="roster"][data-theme="dark"] | 0,2,0 | Chalk dark |
[data-tome-theme="roster"][data-tome-palette="p"] | 0,2,0 | palette light |
[data-tome-theme="roster"][data-tome-palette="p"][data-theme="dark"] | 0,3,0 | palette dark |
The palette files are generated from the design-brief script (palettes.mjs --fix) and copied, never retyped.
Shape and rhythm. --radius 8px; --tome-radius-sm 8px (fields, the term strip), md 16px (cards and rows), lg and xl 28px (media, the trial and fee cards, the timetable frame), full 999px for actions, age tags and the hero's pills. The prose measure is 600px and not negotiable. There are no accent bars: no thick coloured top or left stripe on any card, and the only coloured stroke on a label is the 3px section-label dash.
Type scale. The approved mockup was authored on a 16px root and tome-ui sets html { font-size: 18px }, so every rem value is converted (x 8/9) and the vw terms are kept. The body is 19px for all running copy.
Palettes
| Palette | Ground / surface | Ink / muted | Mark / accent text | The one fill |
|---|---|---|---|---|
| Chalk (default) | #F6F6F3 / #FFFFFF | #17181C / #53565E | red #D7263D / #B51D33 | #B51D33 |
| Lane | #EDF5FA / #FFFFFF | navy #0F2A44 / #42607A | pool blue #1B86B8 / #0E6A9C | ink #0F2A44 |
| Camp | #FFF8E7 / #FFFDF6 | #1D2634 / #525B68 | pine #2A8A5E / #17704F | #17704F |
| Sprout | #FAF6EE / #FFFDF8 | spruce #1F3A36 / #4A5E59 | cornflower #5C7ACB / #4562AE | ink #1F3A36 |
Contrast
All ratios are WCAG 2.x. Text must reach at least 4.5. Marks, field outlines, empty stripe segments, the focus ring, the frame's edge and the today rule must reach at least 3. The test suite re-measures every pair in all eight palette modes from the shipped values, the timetable frame's roles included.
The tightest passes: muted ink on the frame in Lane light (5.12), the accent mark on the frame in Sprout light (3.13), Sprout's accent text on the tint (4.72) and the empty stripe segment on the tint in Chalk light (3.03). Every dark mode is 8.13 or more on the frame's text pairs.
What it styles
| Hook | Treatment |
|---|---|
Section openers ([data-lane-kicker], [data-block-eyebrow], [data-contact-eyebrow], [data-local-label], [data-finder-eyebrow]) | The meta label (Atkinson 600, sentence case) with a 3px accent dash before it. |
[data-block-deck], [data-schedule-intro] | The one sentence under the heading: the body size in the surface ink, at the 600px measure. |
Level stripe ([data-level-stripe], [data-level-segment], [data-level-text], [data-stripe-fill]) | The theme's mark and its once-only fill, with a reduced-motion path. |
Tags and spots ([data-schedule-age], [data-finder-time], [data-schedule-spots], [data-finder-spots]) | Pills in the tint with ink text (on a tint band, the surface); an ink dot, filled for spots, hollow for a waitlist, none when full. |
local-class-finder ([data-class-finder], [data-finder-*]) | The hero: the promise in Bricolage 720, the live line, the pill questions, the result card, the one filled trial button, the tall photo lane. |
Chrome header ([data-visible][data-nav-theme][data-overlay], [data-header-call]) | The page ground, the hairline once scrolled, the action-fill CTA, the outline call pill and the quiet desktop call note, the lockup hook [data-roster-wordmark]. |
| Chrome footer 7 | Group labels, text rows, the credits line ruled off at full width; one left-aligned column below 768px. |
[data-appearance] links and buttons (in main, nav and the footer) | The action-fill pill, and the outline pill in ink; a spring on press where motion is allowed. |
The action pills ([data-finder-cta], [data-schedule-enrol], [data-fee-option], [data-fee-action], [data-panel-cta]) and the trial form's submit | The same press spring as the option pills; under reduced motion a pressed pill does not move. |
Focus (:focus-visible) | A 3px ring, 2px clear of the element (--roster-ring-width, --roster-ring-offset); a block's thinner ring is raised to 3px inside main, the header and the footer. A day tab's ring is drawn inside the tab so the scrolling strip cannot cut it. |
The trial form in the close band ([data-contact-form-slot], [data-form-fields], [data-form-actions]) | Two columns from 640px (a later select and a group of choices take the row), drawn checkboxes (a 24px box in the field edge colour, ink when chosen), a full-width 56px submit, and the form's own legend visually hidden when it has one step. |
Optional block fields ([data-pathway-age-tag], [data-pathway-blurb], [data-pathway-teaser-text], [data-schedule-next-term], [data-faq-callout-second], [data-fact-note], [data-panel-person-quote]) | The tag pill, body text at the measure, the term strip's third cell, a text link, a dashed note pill and a muted line. They draw nothing until a site fills the field. |
[data-new-tab] | The north-east arrow after the link text, a CSS mask. |
[data-compliance-label] (every block) | The plate tag; inside a photo, the overlay at its bottom left. |
[data-testimonial-cell] blockquote | Atkinson italic, the one place italic is used. |
Treatments target only public hooks: data-block-type on the block wrapper, data-block-variant on the root, the blocks' documented data-* part attributes, and published component tokens. Element names are used only where a block publishes no part hook (the hero's heading and photo, a question's legend).
Server / client posture
The package is pure CSS plus constants. It has no React components and no 'use client' files, so it is inert under React Server Components. It adds nothing to a client bundle beyond the stylesheets the consumer imports. ./register is a server-side import.
Tests
pnpm --filter @wabbit/tome-theme-roster testThe vitest suites cover:
- the theme conformance check in theme mode (Layer-1 surface,
*-textliterals, radius and font slots, the full dark redeclaration, palette coverage, scoping, no module class names, font pairing) and in platform mode - manifest validation, its agreement with
package.json, registration, and the pending-catalog tripwire for blocks not yet registered - every palette defining the full required set in both modes, the schedule knobs and the frame agreeing with the roles
- a contrast test that re-measures every text pair at 4.5 and every mark or boundary at 3 in all eight modes, the frame roles included, and pins the tightest pairs
- the A4.1 font pairing, the figures rule, Bricolage rules always setting a weight, the self-hosted faces with their licences,
tnumand the axes read out of the woff2 files, and the subset range - hook coverage: every hook this release styles has a selector, the hooks of unreleased treatments are not styled early, and the few that carry behaviour only are listed with the reason
- no colour literal outside
src/tokens/, no comment that swallows a rule, no structural or class selector, no hand-coloured shadow, no accent bar, and every treatment rule scoped to the theme - the reveal switch and timing, the page lanes, the motion tokens, the hero's grid and photo lane, and every reduced-motion path
Exports
| Import path |
|---|
@wabbit/tome-theme-roster |
@wabbit/tome-theme-roster/manifest |
@wabbit/tome-theme-roster/register |
@wabbit/tome-theme-roster/fonts |
@wabbit/tome-theme-roster/styles.css |
@wabbit/tome-theme-roster/fonts.css |
@wabbit/tome-theme-roster/fonts/* |
Changelog
0.1.xMinor
New package:
@wabbit/tome-theme-roster, a Tome theme for businesses that run classes on a timetable and sell a term of them: gymnastics, dance, swim, martial arts, camps and preschools.It reads like a printed gym timetable that happens to take a booking: Bricolage Grotesque for headings and single big numbers, Atkinson Hyperlegible Next for everything a parent reads or taps, a level stripe as the one recurring mark, and a single filled action per page. No accent bars anywhere.
- Four palettes, each in light and a derived dark mode. Chalk (default, a true red), Lane (pool blue), Camp (pine) and Sprout (cornflower), activated by
data-tome-theme="roster"anddata-tome-palette. All four are light by design. Every text pair clears 4.5:1 and every mark, field outline, focus ring and frame edge 3:1 in all eight modes, the timetable frame's roles included, and the tests re-measure them. - Self-hosted OFL fonts. Bricolage Grotesque (one variable face,
opsz12 to 96 andwght500 to 800, width axis not shipped) and Atkinson Hyperlegible Next (upright and italic,wght400 to 700), both with workingtnum, subset from the upstream TTFs, through./fonts.css,getRosterFontStyles()ornext/font/local. The tests read the shipped woff2 files for their features and axes. - The section opener and the marks. A sentence-case label with a 3px accent dash, a short heading and one sentence; the level stripe (14x7 segments, always beside its text, filling once to the real level), age and time tags, and ink spots dots.
- Navbar 1 and Footer 7. The header on the page ground with a hairline once scrolled, the action-fill call to action and an outline call pill; the footer's text rows and credits line, with no call-to-action band.
- The hero. The treatment for the class finder (
local-class-finder,ask): the promise, the live line, the pill questions, the result card with the one filled trial button, and a single tall photo lane from grid line 12 to the content edge. - The timetable frame's tokens and the motion tokens.
--roster-frame,-frame-today,-frame-edgeand-frame-rule;--roster-rise,--roster-filland--roster-spring, with the platform scroll reveal turned on as a small rise. Every moving thing has a reduced-motion path. - Checkpoint A geometry. The header is the mockup's 68px bar (through Navbar 1's
--tome-navbar1-pad-block), the menu links stand a--roster-space-5apart, and at 320px the wordmark, call pill and stripe dots tighten. The result card's spots line sits in the right column beside the button and the link from 1200px, and the hero pads once (--tome-finder-pad-y: 0; the wrapper pads). The block marks read the theme's px stripe tokens (--tome-level-stripe-*), its 15px small size (--tome-text-sm) and its 56px button (--tome-finder-cta-min-h), so they win over the blocks' rem fallbacks. - The stripe fill animates only the move into
in;armedis instant, so it no longer collapses and overshoots on mount. - One opt-in effect,
data-roster-effect="grain"(Sprout light only).
Not in this release: the timetable, pathway, credentials, fee card, team, FAQ, close and program-page treatments, and the hours-aware header. They arrive with the blocks that emit their hooks;
ROSTER_STYLED_BLOCKSlists only the blocks that are styled.- Four palettes, each in light and a derived dark mode. Chalk (default, a true red), Lane (pool blue), Camp (pine) and Sprout (cornflower), activated by
Parity fixes against the approved home and program pages, and the treatments for the new optional block fields.
- Measure: the hero lede and the program lede are the 600px measure (they were 612px).
- Rhythm: the program page's times list and "Your first class" sit on the tint, so ground and tint alternate down the page. The home page is unchanged.
- Close band: the portrait and the coach's line start where the form's first field starts, and the taller trial card no longer spreads the right column over its rows. The form's own legend is visually hidden when it has one step.
- Trial form: fields in two columns from 640px, real checkboxes (a 24px box in the field edge colour, ink when chosen, 3:1 or better in every palette), select chevrons and a full-width 56px submit.
- Schedule list: between 768px and 1023px the "Try it" and "Add to calendar" actions take their own line instead of running past the lane.
- Focus and press: the focus ring is 3px everywhere (a block's thinner ring is raised), the chosen fee option's ring is the ring colour, and a day tab's ring is drawn inside the tab so the scrolling strip cannot cut it. The action pills (hero button, "Try it", fee options and buttons) answer a press with the same spring as the option pills; reduced motion stays still.
- Details: the coach and pathway age tags use tabular figures, and the stripe no longer touches its words in a pathway teaser.
- New fields: treatments for the pathway teaser, blurb and age tag, the term strip's next-term cell, the FAQ's second link, a key fact's note tag and the card person's quote.
The program page's next-sessions list is a card per date, the focus ring is 3px on every pill, and the movers cards no longer outgrow their photos.
- Next-sessions list (
local-class-schedule,range: 'next'): the block's list rules were as specific as the theme's, so the surface card lost to a hairline row. The theme's rules now carry the block's root attributes and win. A date with one session is one white card with the date over the start time ("Tue 20 · Today" with an ink dot), the name and facts, the spots and the actions; a date with several sessions keeps a card each under its heading. The end time is left to screen readers, and the calendar action is a 44px round icon button when the block setscalendarStyle: 'icon'. The list takes the room the term line and controls leave when a block drops them. - Focus: the 2px rings the finder (options, call to action, submit, timetable link, selects) and the pathway (band pills, actions) draw one selector deeper than the theme's are raised to
--roster-ring-width. - Movers: the cards use an 8px step, the times sit flush under the blurb and the action's 44px tap box is drawn over the card padding, so the card column ends 29.5px below the photos at 1440px (was 88.8px).
- Card person: with
personLine: 'inline'the role follows the name after a comma, its first letter lower-case.
- Next-sessions list (
Timetable and program-page fixes: lanes, a one-row phone header, a sticky offset that follows the chrome, and compact time, short day names and a today word.
- The program page's detail band uses the three page-grid lanes (facts, prose, next-step card); the facts band is four across from 1024px to 1279px and two across on phones.
- The phone header (under 768px) is the wordmark and the menu button on one row at every width, open desk or closed; the call lives in the phone bar.
- The day strip and the program card stick under what the chrome pins (
--tome-chrome-sticky-height), so a header that scrolls away leaves no band above them. - A dated day tab is at least 55 by 62px, and the strip scrolls rather than squeeze it below that.
- The class time and the level stripe never overlap: the stripe is drawn at the width the time line reserves, and the time can stack as a start over an end (
timeSplit). Day headings take the short weekday and a "Today" word (dayNameStyle,labels.today). - Print sits level with the filters from 768px up, wherever the summary wraps.