Operations console
Tokens, components, the application shell and schedule primitives for Master Marketing, an information platform for planning. Colour is reserved for meaning: channel identity and status. Everything shown uses synthetic data.
Foundations
Warm neutral surfaces, one indigo accent, and meaning colours that are always paired with words or shapes. Every value and ratio below is read from app/tokens.css; apps/web/lib/tokens.test.ts enforces the same minimums (4.5:1 text, 3:1 controls and outlines).
Surfaces and borders
| Swatch | Token | Light | Dark | Contrast light / dark |
|---|---|---|---|---|
Canvas--bg | Light#f6f6f3 | Dark#121211 | Contrast5.48:1≥ 4.56.55:1--text-3 on it | |
Surface (panels)--surface | Light#ffffff | Dark#1c1c1a | Contrast5.93:1≥ 4.55.97:1--text-3 on it | |
Surface 2 (headers)--surface-2 | Light#fafaf8 | Dark#232321 | Contrast5.67:1≥ 4.55.50:1--text-3 on it | |
Surface 3 (hover, selected)--surface-3 | Light#f0f0ec | Dark#2c2c29 | Contrast5.19:1≥ 4.54.89:1--text-3 on it | |
Border--border | Light#e6e5e0 | Dark#34332f | ContrastDecorative divider | |
Border strong--border-strong | Light#d3d2cb | Dark#45443f | ContrastButton outline; buttons are named by text | |
Control border--border-control | Light#85847c | Dark#7d7c75 | Contrast3.75:1≥ 34.07:1on --surface | |
Skeleton--skeleton | Light#e6e5df | Dark#32322e | ContrastLoading placeholder |
Text, accent and outlines
| Swatch | Token | Light | Dark | Contrast light / dark |
|---|---|---|---|---|
Text--text | Light#18181a | Dark#ededea | Contrast17.73:1≥ 4.514.55:1on --surface | |
Text 2--text-2 | Light#4a4944 | Dark#b3b2ab | Contrast9.02:1≥ 4.58.02:1on --surface | |
Text 3 (smallest allowed)--text-3 | Light#65645e | Dark#9a9992 | Contrast5.19:1≥ 4.54.89:1on --surface-3 | |
Accent (white text on it)--accent | Light#4b44d8 | Dark#5f5af0 | Contrast6.73:1≥ 4.54.99:1white on it | |
Accent text--accent-text | Light#4038c9 | Dark#aba8ff | Contrast6.91:1≥ 4.56.91:1on --accent-subtle | |
Focus ring--focus | Light#4b44d8 | Dark#8e8bff | Contrast6.73:1≥ 35.92:1on --surface | |
Event dates outline--event-ink | Light#4a4944 | Dark#b3b2ab | Contrast9.02:1≥ 38.02:1on --surface |
Signals: preparation, timing and alerts
Each foreground is measured on its own tint. Red is reserved for cancelled items and destructive actions; amber explains problems such as missing schedules.
| Swatch | Token | Light | Dark | Contrast light / dark |
|---|---|---|---|---|
Warning--warn-bg | Light#fff3dc | Dark#2e230f | Contrast5.33:1≥ 4.58.34:1--warn on it | |
Danger--danger-bg | Light#fdecee | Dark#3a1a1f | Contrast5.70:1≥ 4.56.91:1--danger on it | |
Success--success-bg | Light#e1f4e8 | Dark#12301f | Contrast5.32:1≥ 4.57.80:1--success on it | |
Info--info-bg | Light#e8efff | Dark#172340 | Contrast5.63:1≥ 4.57.34:1--info on it | |
Draft, cancelled--st-draft-bg | Light#efefeb | Dark#30302d | Contrast5.72:1≥ 4.55.88:1--st-draft on it | |
Preparation--st-draft-bg | Light#efefeb | Dark#30302d | Contrast7.82:1≥ 4.56.22:1--text-2 on it | |
Live--tm-live-bg | Light#ddf4e6 | Dark#0f3321 | Contrast4.66:1≥ 4.57.94:1--tm-live on it | |
Archived--tm-archived-bg | Light#efedf7 | Dark#262338 | Contrast5.99:1≥ 4.57.54:1--tm-archived on it |
Typography
Inter for every working surface, with tabular numerals for dates and counts. Newsreader is used only for page titles, the Overview lede and large figures.
Spacing, radius and elevation
A 4px base in rem, so Meeting view (112.5%) scales everything. Three radii, clearly apart. Hierarchy comes from surface steps and 1px borders; shadows are minimal.
| Token | rem | px | Size |
|---|---|---|---|
--space-0-5 | 0.125 | 2 | |
--space-1 | 0.25 | 4 | |
--space-1-5 | 0.375 | 6 | |
--space-2 | 0.5 | 8 | |
--space-2-5 | 0.625 | 10 | |
--space-3 | 0.75 | 12 | |
--space-4 | 1 | 16 | |
--space-5 | 1.25 | 20 | |
--space-6 | 1.5 | 24 | |
--space-8 | 2 | 32 | |
--space-10 | 2.5 | 40 | |
--space-12 | 3 | 48 | |
--space-16 | 4 | 64 |
--radius-sm4px · bars, badges, entries--radius-md6px · buttons, inputs--radius-lg10px · panels, menus, dialogs--shadow-1Panels, buttons--shadow-2Menus, popovers--shadow-3Dialogs, sheets
Icons
Inline SVG on a 16px grid with 1.5px strokes. Decorative by default; icon-only buttons take their name from a required label.
- overview
- initiatives
- timeline
- calendar
- list
- archive
- archive-clock
- settings
- search
- plus
- minus
- chevron-down
- chevron-up
- chevron-left
- chevron-right
- chevrons-up-down
- check
- close
- alert
- alert-circle
- info
- check-circle
- clock
- file
- link
- paperclip
- user
- more
- filter
- arrow-right
- arrow-left
- open
- external
- flag
- megaphone
- tag
- folder
- rocket
- pulse
- screen
- sun
- moon
- contrast
- sync
- star
- star-filled
- copy
- edit
- sort
- sort-ascending
- sort-descending
- log-out
- keyboard
- history
- gantt-mark
Forms
Every field has a visible label; hints and errors are linked with aria-describedby. Dates are stored as calendar dates in the initiative timezone; the browser's date picker shows its own format, so each date field spells the date out underneath.
Shown on the activation and in search.
Give this activation a label so the team can tell instances apart.
Only editors can change owners.
Tentative dates belong here until they are confirmed.
Date fields
The line under each field echoes the date as the product writes it. “Last day live” is inclusive and explains the removal day (SCHEDULING.md); while a field has an error, that derived helper is withheld.
Sat, Oct 10, 2026 · Dates in Pacific Time
Sun, Nov 1, 2026 · Remove or swap on Mon Nov 2
Last day live can’t be before the launch (Sat Oct 10).
Mon, Oct 5, 2026
Checkbox, radio and switch
Status and labels
Three separate kinds of status, each glyph with one meaning. Preparation is stored (glyph + word). Timing is derived from dates and never stored (small glyph + word). Initiative state uses the badge anatomy with a state glyph.
Preparation status (stored)
Neutral on purpose: hue on a row always means channel, so the glyph’s shape and the word carry the status. Dashed ring, ring, half ring, check, cross. These describe readiness, not publication.
Timing (derived from dates, relative to today)
Filled dot: live. Ring: starts later. Short bar: ended. Square: archived. Diamond: no dates.
Initiative state, kind and badges
One badge anatomy: 20px, 4px radius, sentence case and a 1px border mixed from the badge's own text colour. Draft uses the same dashed ring everywhere. Counts are the only pills.
Glyphs alone
Channels
Eight canonical swatches (blue, amber, green, rose, teal, orange, violet, slate), ordered so neighbours are far apart in hue. A channel stores one of them and a new channel takes the next, so the ninth below repeats the first. Organizations name and edit channels; a swatch only colours them, with the same values on iPhone. The name is always visible beside the colour and never truncates.
| Slot | Default name | Light solid / tint | Dark solid / tint | Edge on surface | Text 3 on tint |
|---|---|---|---|---|---|
| Slot 1 | Outdoor--ch1 / --ch1-tint | Light#2f6fde / #e4ecfc | Dark#6fa0ff / #1a2b4a | Edge on surface4.71:1≥ 36.62:1 | Text 3 on tint5.00:1≥ 4.54.93:1 |
| Slot 2 | On-site signage--ch2 / --ch2-tint | Light#b47714 / #fbf0dc | Dark#f2b54a / #3a2c12 | Edge on surface3.75:1≥ 39.33:1 | Text 3 on tint5.25:1≥ 4.54.74:1 |
| Slot 3 | Wayfinding--ch3 / --ch3-tint | Light#3f8f2f / #e5f2e0 | Dark#74c960 / #1b3317 | Edge on surface4.05:1≥ 38.34:1 | Text 3 on tint5.11:1≥ 4.54.79:1 |
| Slot 4 | Print--ch4 / --ch4-tint | Light#c8385a / #fae3e9 | Dark#ff7093 / #3f1a24 | Edge on surface5.03:1≥ 36.49:1 | Text 3 on tint4.86:1≥ 4.55.30:1 |
| Slot 5 | Email--ch5 / --ch5-tint | Light#0e8a86 / #ddf2f0 | Dark#3cc6bf / #103634 | Edge on surface4.20:1≥ 38.14:1 | Text 3 on tint5.09:1≥ 4.54.59:1 |
| Slot 6 | Social--ch6 / --ch6-tint | Light#d2572a / #fbe7df | Dark#ff8c5e / #40221a | Edge on surface4.08:1≥ 37.44:1 | Text 3 on tint4.97:1≥ 4.55.02:1 |
| Slot 7 | Web--ch7 / --ch7-tint | Light#7b4fd6 / #eee7fb | Dark#a98bff / #2a2145 | Edge on surface5.34:1≥ 36.35:1 | Text 3 on tint4.93:1≥ 4.55.23:1 |
| Slot 8 | Partners--ch8 / --ch8-tint | Light#5e6e80 / #e9edf1 | Dark#9daec0 / #252c33 | Edge on surface5.22:1≥ 37.51:1 | Text 3 on tint5.04:1≥ 4.54.94:1 |
| Slot 1 | Press--ch1 / --ch1-tint | Light#2f6fde / #e4ecfc | Dark#6fa0ff / #1a2b4a | Edge on surface4.71:1≥ 36.62:1 | Text 3 on tint5.00:1≥ 4.54.93:1 |
Channel tag
Layout
Flat panels on the canvas. Never nest cards: use group rows, dividers or a key/value strip inside one panel.
Card title3
Description sits beside the titleCard body. Tables and lists usually run edge to edge instead of padding.
Attention card
An amber top rule marks panels that need attention without alarm.
Section header
Grouped by channel
A serif title over a 1px ink rule separates groups of panels.
Key/value facts strip
Equal columns of at least 10rem that wrap cleanly. Timezone appears only when an initiative's differs from the organization's (shown once, in the top bar). Secondary facts use the same component.
- Owner
- Maya Okafor
- Event dates
- Thu Nov 5 – Sun Nov 8
- Promotion
- Oct 1 – Nov 132 days
- Activations
- 3in 3 channels · 2 files
- Created
- Aug 24, 2026
- Auto-archive
- On
- Archive reason
- Not archived
Summary strip
Planning counts, not KPIs. Each links to the list it counts, and every number comes from the same query as that list.
Search
The top-bar field suggests initiatives, activations and people as you type (a WAI-ARIA combobox with a grouped listbox). Arrow keys move the highlight, Enter opens it, Escape closes the list and then clears, and a polite status reports the count. With nothing highlighted, Enter submits to the full search route.
Try it
Type “flyer”, “Rosa” or “festival”.
States
Data display
Fixed-layout tables with captions, scoped headers and explicit table roles. Low-priority columns drop first as space runs out; below 768px rows become list items, the header row stays available to screen readers and each cell carries its field name. Titles wrap to two lines; dates never truncate. Sortable headers expose aria-sort and draw an up or down arrow.
Sortable table
| Preparation | Owner | ||
|---|---|---|---|
| Billboard — Route 9 northboundLantern Festival | Launch | Preparation: Planned | Daniel Reyes |
| Newsletter feature — Registration opensLantern Festival | Launch | Preparation: Planned | Maya Okafor |
| Bathroom flyer — East entranceLantern Festival | Launch | Preparation: Planned | Priya Natarajan |
| A-frame — Main gateLantern Festival | Launch | Preparation: In progress | Priya Natarajan |
Preparation mix
Drawn in one ink ramp (solid completed, half-tone in progress, outlined planned, dashed draft) so saturated colour always means a channel. The words carry the counts.
Avatars and people
Initials only; no photos. Decorative beside a visible name.
Activity feed
Priya Natarajan moved the launch of A-frame — Main gate
from Oct 18to Oct 20· Lantern Festival
Open Studios Weekend archived automatically after its last promotion ended Sep 20
· Files and dates kept in Archive
Feedback
Distinct loading, empty, no-results and error states. A failed request is never shown as an empty calendar.
Inline alerts
Dates are in Pacific Time
Saved
Auto-archive blocked
Couldn’t load activations
Banner
Empty states
A quiet icon, a serif title, one sentence and the next step. An empty month still looks like a calendar, so it never reads as a failure.
Start your first initiative
Create a campaign, promotion, event or project, then add the collateral it needs. Nothing here is sample data.
No activations match these filters
Owner is Daniel Reyes and Channel is Press. Clear a filter or widen the dates to see more.
No launches in December
Nothing starts this month. Items without dates are under Missing schedules.
Skeletons
Static placeholders for first load only, drawn at the real column widths and row heights with the real headers, so nothing jumps when data arrives. Nothing shimmers or animates on entry.
Upcoming launches
Recently changed
Toasts
Two regions exist before any toast: polite for confirmations, assertive for errors, so each message is announced once. Errors and toasts with an action (such as Undo) stay until dismissed; Alt+Shift+N moves focus to the newest toast. Keep the same undo in the page too.
Overlays
Native modal dialogs: the page behind is inert, focus stays inside, Escape closes, and focus returns to the trigger.
Archive forecast
Explains automatic archiving before it happens, in one typeface and without a clock time (the rule is date-only). The diagram is proportional: the promotion as a neutral range bar, the archive tick, and event dates in the ink outline, with end labels on the top line.
Archives — the day after the last day live, Nov 1
Auto-archive is blocked
Auto-archive is off
Create flow
The same sequence on web and phone. Step 3 in a sheet: search, All types / My favorites, per-person stars, a − / count / + stepper for several instances, and a shared ‘Add a collateral type’.
Schedule primitives
Documented building blocks for T05, not wired to data. One dataset, two lenses: bars show inclusive live ranges; the calendar lists each activation once, on its first live day.
Timeline bar encodings
Bounded range, one-day item, open-ended (flat edge + OPEN), started before the view (dashed edge + ‹), continues after the view, and event dates as an ink outline with a flag. Past days get a flat wash; nothing is hatched. Labels always state real dates, and each bar's accessible name contains its visible label followed by a full sentence.
- Live range, first to last day
- One-day item
- Open-ended or continues
- Started before this view
- Event dates (reference)
- Today
Narrow screens and assistive technology get the agenda equivalent (TimelineAgendaItem): title, dates and a proportional mini-track with a today tick.
Calendar entries
Channel tint with a 3px channel edge, a two-line title, and “Channel · Initiative”. The channel never truncates; only the initiative does. The event-start marker uses the same ink outline and flag as event dates on the timeline.
Calendar day cells
Separate examples, each with its month. A cell shows the date (with the month on the 1st and after any gap), a visible launch count, at most two entries, then “+N more”. Today is the filled date chip and the word “Today”. Cells use static table semantics, with the full date and count as text for screen readers.
- Today, no launches
Monday, September 28, 2026, today, no launches
- First of the month, three launches, “+1 more”
Thursday, October 1, 2026, 3 launches
- Billboard — Route 9 northboundOutdoor, Lantern Festival
- Poster — Studio corridorOn-site signage, Autumn Clay & Print Workshops
- One launch
Saturday, October 10, 2026, 1 launch
- Bathroom flyer — East entranceOn-site signage, Lantern Festival
- Event layer on, no launches
Thursday, November 5, 2026, no launches
Event starts · Lantern Festival
App shell
AppShell is presentational: it takes organization, user, navigation, counts and sync state as props. T04 supplies data and routes.
Responsive behaviour
- Desktop (≥1100px): 240px sidebar with the organization switcher, Create menu (C, which viewers can turn off in the account menu), primary navigation with counts, active initiatives, Settings, sync status and account menu. Opaque sticky top bar with breadcrumbs, search (⌘K), the timezone chip (the one place the timezone appears), Meeting view and theme.
- Tablet (768–1099px): 72px rail with icon plus visible label for each destination and a short sync word; org tile and square Create keep accessible names.
- Phone (<768px): opaque top app bar (organization with the timezone under it, sync status, search, Create) and a fixed bottom tab bar: Overview, Initiatives, Timeline, Calendar and More (Archive, Settings, display). Scroll padding keeps a focused control clear of both bars, and on short screens the bars stop sticking.
Sync status (role=status)
One persistent status per layout. Each state has its own shape: filled dot, open ring (turns only when motion is allowed), square, triangle. The phone top bar shows only the dot while synced and a short word otherwise; the tablet rail shows a short word.