Senators open the season Saturday in Toronto
Opening night is October 3 at Scotiabank Arena. This is where the preview column runs.
Coming In Hot
Every token, type size, motif, component and screen the site is built from, in one place. Values on this page are read live from the running site, so they are always the real ones.
Values shown from lib/tokens.mjs. They are re-read from the page once it loads.
Five rules that settle most design questions before they become meetings.
Pages should feel like a sports network on game night: condensed type, hard edges, red, black and white. If it could be any blog, it is not done.
Red marks the one thing to tap or notice. Most of every page is white, paper and black, so the red still means something.
Articles, video and merch are the reason people come. Motifs frame the content; they never sit on top of it or slow it down.
Sample content says Sample. Ads say Ad. Nothing claims to be official, and nothing invents a quote, a score or a fact.
New shows, writers and sections reuse these tokens and parts, so they arrive on brand without a redesign or a new colour.
Three layers. Primitives are the raw ramps. Semantic tokens name a job (text, surface, focus) and point at a primitive. Components and component tokens use semantic tokens, and the token guard warns on any alias or primitive reference in app/ or components/, so changing the red is one line in lib/tokens.mjs.
Each chip shows its contrast as text on white and on broadcast black. AA needs 4.5:1 for body text and 3:1 for large text and UI parts.
The brand red and its steps. 600 is the brand red; 400 is the red that reads on black.
--red-50#FDECEESoft red wash behind warnings and highlightsOn white 1.1:1FailOn black 17.2:1AAA--red-100#FBD5D9On white 1.3:1FailOn black 14.6:1AAA--red-200#F6AAB2On white 1.9:1FailOn black 10.6:1AAA--red-300#FF7A84On white 2.5:1FailOn black 7.8:1AAA--red-400#FF4D5AHot red: red text and accents on blackOn white 3.3:1AA largeOn black 6.1:1AA--red-500#E8192FOn white 4.6:1AAOn black 4.3:1AA large--red-600#D50F25Coming In Hot red, the brand colourOn white 5.4:1AAOn black 3.7:1AA large--red-700#A30D1CDeep red: hoverOn white 8.0:1AAAOn black 2.5:1Fail--red-800#760915PressedOn white 11.5:1AAAOn black 1.7:1Fail--red-900#4A050DOn white 15.9:1AAAOn black 1.2:1FailNear-blacks for the masthead, footer and video bands, stepping up to the greys used for secondary text and control borders.
--ink-1000#000000Pure black: the masthead top stripOn white 21.0:1AAAOn black 1.1:1Fail--ink-950#0B0B0CBroadcast black: masthead, footer, video bandsOn white 19.7:1AAAOn black 1.0:1Fail--ink-900#121315Body text on lightOn white 18.6:1AAAOn black 1.1:1Fail--ink-850#1B1C1FRaised panels on blackOn white 17.0:1AAAOn black 1.2:1Fail--ink-800#2A2B30Lines on blackOn white 14.1:1AAAOn black 1.4:1Fail--ink-700#3D3F45On white 10.5:1AAAOn black 1.9:1Fail--ink-600#5C5F66Secondary text on lightOn white 6.4:1AAOn black 3.1:1AA large--ink-550#73767DPlaceholder text (4.5:1 on white)On white 4.5:1AAOn black 4.3:1AA large--ink-500#8A8D94Form control borders (3:1 on white)On white 3.3:1AA largeOn black 5.9:1AA--ink-400#A3A6ADSecondary text on blackOn white 2.4:1FailOn black 8.1:1AAA--ink-300#B9BBC0On white 1.9:1FailOn black 10.2:1AAA--ink-200#D4D6DAOn white 1.5:1FailOn black 13.5:1AAA--ink-100#EBECEEOn white 1.2:1FailOn black 16.6:1AAAWarm off-whites for the page, so white cards lift off it, and the warm greys used for lines and image wells.
--paper-0#FFFFFFWhiteOn white 1.0:1FailOn black 19.7:1AAA--paper-50#F7F6F3Lightest washOn white 1.1:1FailOn black 18.2:1AAA--paper-100#F5F4F1The page backgroundOn white 1.1:1FailOn black 17.9:1AAA--paper-150#F0EEEAImage wells behind product photosOn white 1.2:1FailOn black 17.0:1AAA--paper-200#EBE9E4Loading skeletonsOn white 1.2:1FailOn black 16.2:1AAA--paper-300#E3E1DCHairlines and card bordersOn white 1.3:1FailOn black 15.1:1AAA--paper-400#CFCCC4On white 1.6:1FailOn black 12.3:1AAA--paper-500#B9B6AEDashed placeholder bordersOn white 2.0:1FailOn black 9.7:1AAA--paper-700#6B675FOn white 5.6:1AAOn black 3.5:1AA largeUsed sparingly. 400 is the bright gold of the Sample label, demo notes and the focus ring on black; 500 is the muted broadcast gold for thin rules, badge rings and small labels. Never large fills, never a brand colour on its own.
--gold-50#FFF8DBDemo note backgroundOn white 1.1:1FailOn black 18.5:1AAA--gold-100#FFF0B8On white 1.1:1FailOn black 17.2:1AAA--gold-200#E8D48ADemo note borderOn white 1.5:1FailOn black 13.3:1AAA--gold-400#FFD54ASample label, focus ring on blackOn white 1.4:1FailOn black 13.9:1AAA--gold-500#C9A24ABroadcast gold: thin rules, badge rings, small labels on blackOn white 2.4:1FailOn black 8.2:1AAA--gold-600#C99700Gold text on blackOn white 2.7:1FailOn black 7.4:1AAA--gold-700#8A6A00Gold text on whiteOn white 5.1:1AAOn black 3.9:1AA largeGreen for success, blue for keyboard focus on light backgrounds. Red doubles as the danger colour.
--green-50#E8F4ECSuccess backgroundOn white 1.1:1FailOn black 17.4:1AAA--green-300#4FBF75Success on blackOn white 2.3:1FailOn black 8.5:1AAA--green-500#2D8A4EOn white 4.3:1AA largeOn black 4.6:1AA--green-600#1C7C3ASuccess text and iconsOn white 5.3:1AAOn black 3.7:1AA large--blue-500#1F6FEBFocus ring on lightOn white 4.6:1AAOn black 4.2:1AA large--blue-700#1D4AA8Rink blue linesOn white 8.1:1AAAOn black 2.4:1Fail--blue-200#B9D3F0Rink creaseOn white 1.5:1FailOn black 12.8:1AAA--ice-50#F3F7FARink iceOn white 1.1:1FailOn black 18.3:1AAA--ice-150#DDE7EFRink ice, worn and shadedOn white 1.3:1FailOn black 15.7:1AAATranslucent black, white and red for shadows, scrims and the speed-slash textures. Each chip shows the step over paper and over black.
--ink-a05#0b0b0c0d--ink-a08#0b0b0c14--ink-a10#0b0b0c1a--ink-a12#0b0b0c1f--ink-a16#0b0b0c29--ink-a18#0b0b0c2e--ink-a25#0b0b0c40--ink-a45#0b0b0c73--ink-a50#0b0b0c80--ink-a72#0b0b0cb8--gold-a35#c9a24a59--white-a05#ffffff0d--white-a10#ffffff1a--white-a75#ffffffbf--white-a90#ffffffe6--red-a35#d50f2559--red-a50#d50f2580--blue-a30#1f6feb4dWhat components actually use. The Points to column is the primitive each one lands on.
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--bg | --paper-100 | #f5f4f1 | Page background | |
--surface | --paper-0 | #ffffff | Cards, panels, forms | |
--surface-subtle | --paper-50 | #f7f6f3 | Quiet wash inside a card | |
--surface-sunken | --paper-150 | #f0eeea | Image wells behind photos | |
--surface-muted | --paper-300 | #e3e1dc | Muted chips and disabled fields | |
--surface-inverse | --ink-950 | #0b0b0c | Black bands: masthead, footer, video | |
--surface-inverse-raised | --ink-850 | #1b1c1f | Panels sitting on black | |
--surface-inverse-pressed | --ink-800 | #2a2b30 | Black controls while pressed | |
--surface-inverse-deep | --ink-1000 | #000000 | The deepest strip at the very top | |
--surface-accent | --red-600 | #d50f25 | Red bands: the charity push, primary buttons | |
--surface-accent-subtle | --red-50 | #fdecee | Soft red behind errors and highlights | |
--surface-note | --gold-50 | #fff8db | Demo notes | |
--surface-success | --green-50 | #e8f4ec | Success messages | |
--surface-dark | --ink-950 | #0b0b0c | Alias of surface-inverse |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--text | --ink-900 | #121315 | Body text | |
--text-strong | --ink-950 | #0b0b0c | Headlines and outlined controls | |
--text-muted | --ink-600 | #5c5f66 | Dates, captions, secondary text | |
--text-placeholder | --ink-550 | #73767d | Placeholder text in fields | |
--text-accent | --red-600 | #d50f25 | Red text on light: eyebrows, links | |
--text-accent-strong | --red-700 | #a30d1c | Red text hover, red text on red tint | |
--text-inverse | --paper-0 | #ffffff | Text on black | |
--text-inverse-muted | --ink-400 | #a3a6ad | Secondary text on black | |
--text-accent-on-dark | --red-400 | #ff4d5a | Red text on black | |
--text-on-accent | --paper-0 | #ffffff | Text on red | |
--text-on-color-soft | --white-a90 | #ffffffe6 | Softer text on red or black photos | |
--text-gold-on-dark | --gold-600 | #c99700 | Gold text on black (rare) | |
--text-success | --green-600 | #1c7c3a | Success text | |
--text-success-on-dark | --green-300 | #4fbf75 | Success text on black | |
--text-danger | --red-600 | #d50f25 | Error text |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--border | --paper-300 | #e3e1dc | Hairlines and card borders | |
--border-strong | --ink-950 | #0b0b0c | Black rules under section headers | |
--border-input | --ink-500 | #8a8d94 | Form control borders (3:1 on white) | |
--border-input-hover | --ink-600 | #5c5f66 | Form control borders on hover | |
--border-inverse | --ink-800 | #2a2b30 | Lines on black | |
--border-accent | --red-600 | #d50f25 | Red rules | |
--border-dashed | --paper-500 | #b9b6ae | Dashed placeholder outlines (ads, sponsors) | |
--border-note | --gold-200 | #e8d48a | Demo note border |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--accent | --red-600 | #d50f25 | The brand accent | |
--accent-hover | --red-700 | #a30d1c | Accent on hover | |
--accent-active | --red-800 | #760915 | Accent while pressed | |
--accent-on-dark | --red-400 | #ff4d5a | Accent on black | |
--highlight | --gold-400 | #ffd54a | Restrained gold highlight | |
--accent-gold | --gold-500 | #c9a24a | Muted broadcast gold: thin rules, badge rings, small labels | |
--vendor-accent | --red-600 | #d50f25 | The asterisk in Zavi Digital's wordmark on the invoice, in the client's red | |
--danger | --red-600 | #d50f25 | Errors | |
--danger-surface | --red-50 | #fdecee | Error background | |
--success | --green-600 | #1c7c3a | Success | |
--success-surface | --green-50 | #e8f4ec | Success background |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--focus | --blue-500 | #1f6feb | Keyboard focus ring on light | |
--focus-on-dark | --gold-400 | #ffd54a | Keyboard focus ring on black or red | |
--focus-ring-color | --blue-500 | #1f6feb | The ring in the current context (switches inside .on-dark) | |
--focus-halo | --blue-a30 | #1f6feb4d | Soft halo around a focused field | |
--focus-ring-width | Base value | 3px | ||
--focus-ring-offset | Base value | 2px |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--sample-bg | --gold-400 | #ffd54a | Sample label fill | |
--sample-fg | --ink-950 | #0b0b0c | Sample label text | |
--sample-border | --ink-950 | #0b0b0c | Sample label outline | |
--note-bg | --gold-50 | #fff8db | Demo note fill | |
--note-border | --gold-200 | #e8d48a | Demo note border | |
--note-accent | --gold-400 | #ffd54a | Demo note left rule | |
--overlay-scrim | --ink-a72 | #0b0b0cb8 | Behind dialogs and photo captions | |
--on-red | --paper-0 | #ffffff | Anything on red | |
--on-ink | --paper-0 | #ffffff | Anything on black | |
--on-ink-muted | --ink-400 | #a3a6ad | Quieter things on black |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--tint-ink-faint | --ink-a05 | #0b0b0c0d | Faint dark lines on light: pinstripes, skate marks | |
--tint-ink | --ink-a16 | #0b0b0c29 | Dark texture over red | |
--tint-ink-shade | --ink-a25 | #0b0b0c40 | Shade at the foot of a photo panel | |
--tint-ink-half | --ink-a50 | #0b0b0c80 | Half-strength black | |
--tint-white-faint | --white-a05 | #ffffff0d | Faint light texture over black | |
--tint-white | --white-a10 | #ffffff1a | Light wash and hairlines on black | |
--tint-white-strong | --white-a75 | #ffffffbf | Light catching a surface | |
--tint-accent-glow | --red-a35 | #d50f2559 | Soft red glow | |
--tint-accent-half | --red-a50 | #d50f2580 | Half-strength red |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--ad-stripe-a | --paper-150 | #f0eeea | Ad slot hatching, dark stripe | |
--ad-stripe-b | --paper-50 | #f7f6f3 | Ad slot hatching, light stripe | |
--ad-border | --paper-500 | #b9b6ae | Ad slot outline | |
--skeleton-base | --paper-200 | #ebe9e4 | Loading skeleton | |
--skeleton-shine | --paper-50 | #f7f6f3 | Loading skeleton shimmer | |
--media-well | --paper-150 | #f0eeea | Behind product photos and thumbnails | |
--mask-opaque | --ink-1000 | #000000 | Solid stop in CSS masks (only the alpha matters) |
The short names the site was first built with. They still resolve, but they point straight at the ramps, so no component uses them: the token guard warns if one appears in app/ or components/.
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--red | --red-600 | #d50f25 | Coming In Hot red | |
--red-deep | --red-700 | #a30d1c | Hover and pressed red | |
--red-hot | --red-400 | #ff4d5a | Red text and accents on black | |
--red-tint | --red-50 | #fdecee | Soft red wash | |
--black | --ink-950 | #0b0b0c | Broadcast black | |
--ink | --ink-900 | #121315 | Body text | |
--charcoal | --ink-850 | #1b1c1f | Raised panels on black | |
--graphite | --ink-800 | #2a2b30 | Lines on black | |
--white | --paper-0 | #ffffff | White | |
--paper | --paper-100 | #f5f4f1 | Page background | |
--line | --paper-300 | #e3e1dc | Hairlines on light | |
--gold | --gold-500 | #c9a24a | Restrained broadcast gold: thin rules, badge rings, small labels |
Per-component knobs built from semantic tokens. Change a button colour here, not in Button.module.css.
| Token | Points to | Value | Use |
|---|---|---|---|
--state-disabled-opacity | Base value | 0.45 | Disabled controls fade to this |
--state-pressed-shift | Base value | 1px | Buttons nudge down this far when pressed |
--state-hover-lift | Base value | -2px | Interactive cards lift this far on hover |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--btn-height-sm | Base value | 36px | ||
--btn-height | Base value | 44px | Minimum tap target | |
--btn-height-lg | Base value | 52px | ||
--btn-primary-bg | --red-600 | #d50f25 | ||
--btn-primary-bg-hover | --red-700 | #a30d1c | ||
--btn-primary-bg-active | --red-800 | #760915 | ||
--btn-primary-fg | --paper-0 | #ffffff | ||
--btn-secondary-bg | --ink-950 | #0b0b0c | ||
--btn-secondary-bg-hover | --ink-850 | #1b1c1f | ||
--btn-secondary-bg-active | --ink-800 | #2a2b30 | ||
--btn-secondary-fg | --paper-0 | #ffffff | ||
--btn-outline-fg | --ink-950 | #0b0b0c | ||
--btn-outline-bg-hover | --ink-950 | #0b0b0c | ||
--btn-outline-bg-active | --ink-850 | #1b1c1f | ||
--btn-outline-fg-hover | --paper-0 | #ffffff | ||
--btn-ghost-fg | --red-600 | #d50f25 | ||
--btn-ghost-fg-hover | --red-700 | #a30d1c | ||
--btn-ghost-fg-active | --red-800 | #760915 | ||
--btn-inverse-bg | --paper-0 | #ffffff | ||
--btn-inverse-bg-hover | --paper-100 | #f5f4f1 | ||
--btn-inverse-bg-active | --paper-300 | #e3e1dc | ||
--btn-inverse-fg | --ink-950 | #0b0b0c | ||
--btn-outline-inverse-fg | --paper-0 | #ffffff | ||
--btn-outline-inverse-bg-hover | --paper-0 | #ffffff | ||
--btn-outline-inverse-bg-active | --paper-300 | #e3e1dc | ||
--btn-outline-inverse-fg-hover | --ink-950 | #0b0b0c |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--field-height | Base value | 48px | ||
--chevron-inset | Base value | 1rem | Gap from a dropdown's right edge to its chevron (every select and menu trigger), about the text's left padding | |
--field-bg | --paper-0 | #ffffff | ||
--field-fg | --ink-900 | #121315 | ||
--field-border | --ink-500 | #8a8d94 | ||
--field-border-hover | --ink-600 | #5c5f66 | ||
--field-border-focus | --blue-500 | #1f6feb | ||
--field-halo-focus | --blue-a30 | #1f6feb4d | ||
--field-border-invalid | --red-600 | #d50f25 | ||
--field-halo-invalid | --red-50 | #fdecee | ||
--field-placeholder | --ink-550 | #73767d | ||
--field-bg-disabled | --paper-50 | #f7f6f3 |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--nav-fg | --paper-0 | #ffffff | Nav links on the black masthead | |
--nav-fg-muted | --ink-400 | #a3a6ad | Quieter nav links (admin bar) | |
--nav-indicator | --paper-0 | #ffffff | Underline under the current page | |
--nav-indicator-accent | --red-400 | #ff4d5a | Red underline (admin bar, drawer) | |
--tab-bg | --paper-0 | #ffffff | ||
--tab-fg | --ink-600 | #5c5f66 | ||
--tab-fg-hover | --ink-900 | #121315 | ||
--tab-bg-hover | --paper-50 | #f7f6f3 | ||
--tab-bg-selected | --ink-950 | #0b0b0c | ||
--tab-fg-selected | --paper-0 | #ffffff |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--slash-angle | Base value | -18deg | The lean of every speed slash | |
--slash-shade | --ink-a16 | #0b0b0c29 | Dark speed slashes over red | |
--slash-light | --white-a05 | #ffffff0d | Faint speed slashes over black | |
--slash-ghost-red | --red-a50 | #d50f2580 | The trailing half-strength red slash | |
--slash-ghost-ink | --ink-a50 | #0b0b0c80 | The trailing half-strength black slash | |
--texture-pinstripe | --ink-a05 | #0b0b0c0d | Fine diagonal pinstripe on light cards | |
--ice-base | --paper-50 | #f7f6f3 | Ice surface | |
--ice-scratch | --ink-a05 | #0b0b0c0d | Skate marks on the ice | |
--ice-sheen | --white-a75 | #ffffffbf | Light catching the ice | |
--boards-line | --red-600 | #d50f25 | The red line | |
--boards-kick | --ink-950 | #0b0b0c | The black kick plate under it | |
--scorebug-bg | --ink-950 | #0b0b0c | ||
--scorebug-fg | --paper-0 | #ffffff | ||
--scorebug-label-bg | --red-600 | #d50f25 | ||
--scorebug-muted | --ink-400 | #a3a6ad | ||
--numeral-stroke | --ink-950 | #0b0b0c | ||
--numeral-stroke-on-dark | --paper-0 | #ffffff | ||
--numeral-shadow | --red-600 | #d50f25 |
| Swatch | Token | Points to | Value | Use |
|---|---|---|---|---|
--band-red | --red-400 | #ff4d5a | Frame for a player 1st to 10th in the NHL in his headline stat | |
--band-red-glow | --red-a50 | #d50f2580 | Glow around a top 10 card and its platform ring | |
--band-gold | --gold-500 | #c9a24a | Hairline frame for 11th to 50th in the NHL | |
--band-gold-text | --gold-400 | #ffd54a | Rank chip text on a gold card | |
--band-gold-glow | --gold-a35 | #c9a24a59 | Glow around a gold card and its platform ring | |
--band-silver | --ink-200 | #d4d6da | Hairline frame for 51st to 250th in the NHL | |
--band-silver-glow | --white-a10 | #ffffff1a | The light around a silver card | |
--band-graphite | --ink-500 | #8a8d94 | Frame for every other card | |
--band-graphite-glow | --white-a05 | #ffffff0d | The faint light around a graphite card | |
--band-red-1 | --red-400 | #ff4d5a | Red frame gradient: the lit edge | |
--band-red-2 | --red-700 | #a30d1c | Red frame gradient: the deep middle | |
--band-red-3 | --gold-400 | #ffd54a | Red frame gradient: the gold catch light | |
--band-gold-1 | --gold-200 | #e8d48a | Gold frame gradient: the lit edge | |
--band-gold-2 | --gold-500 | #c9a24a | Gold frame gradient: the deep middle | |
--band-gold-3 | --gold-100 | #fff0b8 | Gold frame gradient: the catch light | |
--band-silver-1 | --ink-100 | #ebecee | Silver frame gradient: the lit edge | |
--band-silver-2 | --ink-500 | #8a8d94 | Silver frame gradient: the deep middle | |
--band-silver-3 | --ink-200 | #d4d6da | Silver frame gradient: the catch light | |
--band-graphite-1 | --ink-600 | #5c5f66 | Graphite frame gradient: the lit edge | |
--band-graphite-2 | --ink-800 | #2a2b30 | Graphite frame gradient: the deep middle | |
--band-graphite-3 | --ink-400 | #a3a6ad | Graphite frame gradient: the catch light | |
--sweater-top | --ink-700 | #3d3f45 | The sweater body, at the shoulders | |
--sweater-mid | --ink-850 | #1b1c1f | The sweater body, at the chest | |
--sweater-bottom | --ink-950 | #0b0b0c | The sweater body, at the hem | |
--segment-off | --ink-800 | #2a2b30 | An unlit segment in a meter bar | |
--art-shadow | --ink-a72 | #0b0b0cb8 | The drop shadow under the sweater art and its number | |
--rink-ice | --ice-50 | #f3f7fa | The ice of the rink and the shot maps | |
--rink-ice-shade | --ice-150 | #dde7ef | Worn ice toward the boards | |
--rink-sheen | --white-a90 | #ffffffe6 | The light on fresh ice | |
--rink-red | --red-600 | #d50f25 | Centre red line, goal lines, faceoff circles | |
--rink-blue | --blue-700 | #1d4aa8 | Blue lines and the centre circle | |
--rink-crease | --blue-200 | #b9d3f0 | Goal creases | |
--rink-boards | --ink-850 | #1b1c1f | The boards around the ice | |
--rink-kick | --gold-400 | #ffd54a | The kick plate along the bottom of the boards | |
--rink-panel | --paper-300 | #e3e1dc | Blank board panels, never a logo | |
--rink-them | --ink-600 | #5c5f66 | Opponent events on the rink | |
--rink-us | --red-600 | #d50f25 | Senators events on the rink | |
--rink-ink | --ink-900 | #121315 | Numbers and labels on the ice | |
--rink-pulse | --red-a50 | #d50f2580 | The pulse around a goal | |
--medal-gold | --gold-500 | #c9a24a | An earned honour medal | |
--medal-gold-hi | --gold-400 | #ffd54a | The lit edge of an earned medal | |
--medal-ink | --ink-950 | #0b0b0c | The glyph on an earned medal | |
--medal-locked | --ink-700 | #3d3f45 | The outline of a medal not earned yet | |
--medal-locked-text | --ink-400 | #a3a6ad | The glyph and lock on a medal not earned yet | |
--platform-floor | --white-a10 | #ffffff1a | The lit floor under the sweater art | |
--platform-beam | --white-a05 | #ffffff0d | The light falling on the sweater art | |
--card-face | --ink-850 | #1b1c1f | Player card face | |
--card-face-deep | --ink-1000 | #000000 | The darkest part of a card: the art well, the sweater | |
--card-line | --ink-800 | #2a2b30 | Hairlines on a card | |
--holo-a | --white-a05 | #ffffff0d | Holo shimmer, soft band | |
--holo-b | --white-a10 | #ffffff1a | Holo shimmer, bright band |
Every text and UI pairing the site uses, checked against WCAG 2.2. The build fails if any pair drops below AA. 57 of 57 pass.
| Sample | Foreground on background | Ratio | Level | Where |
|---|---|---|---|---|
| Aa | --texton--bg | 16.90:1 | AAAText, 4.5:1 | Body text on the page |
| Aa | --texton--surface | 18.59:1 | AAAText, 4.5:1 | Body text on cards |
| Aa | --text-mutedon--bg | 5.81:1 | AAText, 4.5:1 | Dates and captions on the page |
| Aa | --text-mutedon--surface | 6.40:1 | AAText, 4.5:1 | Dates and captions on cards |
| Aa | --text-mutedon--surface-muted | 4.89:1 | AAText, 4.5:1 | Muted chip text |
| Aa | --text-placeholderon--field-bg | 4.55:1 | AAText, 4.5:1 | Placeholder text in fields |
| Aa | --text-accenton--surface | 5.35:1 | AAText, 4.5:1 | Red links and eyebrows on cards |
| Aa | --text-accenton--bg | 4.87:1 | AAText, 4.5:1 | Red links and eyebrows on the page |
| Aa | --text-accent-strongon--surface-accent-subtle | 7.00:1 | AAText, 4.5:1 | Red text on the red tint |
| Aa | --text-dangeron--surface | 5.35:1 | AAText, 4.5:1 | Field errors |
| Aa | --text-on-accenton--surface-accent | 5.35:1 | AAText, 4.5:1 | White on red: primary buttons, charity band |
| Aa | --text-on-accenton--accent-hover | 7.98:1 | AAAText, 4.5:1 | Primary button on hover |
| Aa | --text-on-color-softon--surface-accent | 4.51:1 | AAText, 4.5:1 | Ledes on red heroes |
| Aa | --text-inverseon--surface-inverse | 19.67:1 | AAAText, 4.5:1 | White on black |
| Aa | --text-inverseon--surface-inverse-raised | 17.04:1 | AAAText, 4.5:1 | White on raised black panels |
| Aa | --text-inverse-mutedon--surface-inverse | 8.07:1 | AAAText, 4.5:1 | Grey text on black |
| Aa | --text-inverse-mutedon--surface-inverse-raised | 6.99:1 | AAText, 4.5:1 | Grey text on raised black panels |
| Aa | --text-accent-on-darkon--surface-inverse | 6.05:1 | AAText, 4.5:1 | Red text on black |
| Aa | --text-accent-on-darkon--surface-inverse-raised | 5.24:1 | AAText, 4.5:1 | Red text on raised black panels |
| Aa | --text-on-color-softon--surface-inverse | 15.85:1 | AAAText, 4.5:1 | Soft white on black |
| Aa | --text-gold-on-darkon--surface-inverse | 7.41:1 | AAAText, 4.5:1 | Gold text on black |
| Aa | --text-successon--surface | 5.26:1 | AAText, 4.5:1 | Success text |
| Aa | --text-successon--surface-success | 4.65:1 | AAText, 4.5:1 | Success message |
| Aa | --text-success-on-darkon--surface-inverse | 8.47:1 | AAAText, 4.5:1 | Success on black |
| Aa | --text-success-on-darkon--surface-inverse-raised | 7.34:1 | AAAText, 4.5:1 | Success on raised black panels (Today card) |
| Aa | --texton--surface-success | 16.45:1 | AAAText, 4.5:1 | Text in an all-clear panel |
| Aa | --text-mutedon--surface-subtle | 5.92:1 | AAText, 4.5:1 | Empty-state text |
| Aa | --sample-fgon--sample-bg | 13.93:1 | AAAText, 4.5:1 | Sample label |
| Aa | --texton--surface-note | 17.44:1 | AAAText, 4.5:1 | Demo note |
| Aa | --texton--highlight | 13.16:1 | AAAText, 4.5:1 | Prototype banner (yellow team note) |
| Aa | --btn-inverse-fgon--btn-inverse-bg-hover | 17.89:1 | AAAText, 4.5:1 | Inverse button on hover |
| Aa | --btn-outline-inverse-fg-hoveron--btn-outline-inverse-bg-active | 15.05:1 | AAAText, 4.5:1 | Outline-inverse button pressed |
| Aa | --text-inverseon--btn-secondary-bg-active | 14.13:1 | AAAText, 4.5:1 | Secondary button pressed |
| Aa | --tab-fgon--tab-bg | 6.40:1 | AAText, 4.5:1 | Unselected tab |
| Aa | --scorebug-mutedon--scorebug-bg | 8.07:1 | AAAText, 4.5:1 | Score bug secondary text |
--border-inputon--surface | 3.32:1 | PassUI, 3:1 | Field borders on white | |
--focuson--surface | 4.63:1 | PassUI, 3:1 | Focus ring on white | |
--focuson--bg | 4.21:1 | PassUI, 3:1 | Focus ring on the page | |
--focus-on-darkon--surface-inverse | 13.93:1 | PassUI, 3:1 | Focus ring on black | |
--focus-on-darkon--surface-accent | 3.79:1 | PassUI, 3:1 | Focus ring on red | |
--accenton--surface | 5.35:1 | PassUI, 3:1 | Red icons and rules on white | |
--accent-on-darkon--surface-inverse | 6.05:1 | PassUI, 3:1 | Red icons and rules on black | |
| Aa | --band-gold-texton--card-face | 12.06:1 | AAAText, 4.5:1 | Rank chip on a gold player card |
| Aa | --band-silveron--card-face | 11.71:1 | AAAText, 4.5:1 | Rank chip on a silver player card |
--band-redon--card-face | 5.24:1 | PassUI, 3:1 | Red frame on a top 10 player card | |
--band-goldon--card-face | 7.10:1 | PassUI, 3:1 | Gold hairline on a player card | |
--band-graphiteon--card-face | 5.13:1 | PassUI, 3:1 | Graphite frame on a player card | |
| Aa | --medal-inkon--medal-gold | 8.20:1 | AAAText, 4.5:1 | Glyph on an earned honour medal |
| Aa | --medal-locked-texton--card-face | 6.99:1 | AAText, 4.5:1 | Glyph and next line on a locked medal |
| Aa | --rink-inkon--rink-ice | 17.26:1 | AAAText, 4.5:1 | Numbers printed on the ice |
--rink-uson--rink-ice | 4.97:1 | PassUI, 3:1 | Senators events on the rink | |
--rink-themon--rink-ice | 5.94:1 | PassUI, 3:1 | Opponent events on the rink | |
| Aa | --text-accent-strongon--bg | 7.26:1 | AAAText, 4.5:1 | Links on hover (--link-color-hover), light page |
| Aa | --text-accent-strongon--surface | 7.98:1 | AAAText, 4.5:1 | Links on hover (--link-color-hover), light page |
| Aa | --accent-activeon--bg | 10.49:1 | AAAText, 4.5:1 | Links while pressed (--link-color-active), light page |
| Aa | --accent-activeon--surface | 11.54:1 | AAAText, 4.5:1 | Links while pressed (--link-color-active), light page |
--focus-on-darkon--surface-inverse-raised | 12.06:1 | PassUI, 3:1 | Focus ring (--focus-ring-color), dark context |
Two families. A condensed display face for the broadcast voice, and a clean sans for anything read in sentences. Sizes are fluid: they grow with the screen between a phone and a desktop size.
Barlow Condensed
--font-display, weights 500 to 900 plus italics. Headlines, nav, labels, buttons, numbers. Narrow letters fit big headlines on a phone, and the italic 900 is the score-graphic voice.
Inter
--font-sans. Body text, ledes, forms, captions. Open shapes and tall lowercase stay readable at 12px and in long articles, and it stays out of the way of the display face.
Fallbacks: --font-display and --font-sans fall back to system fonts, so text shows immediately while the web fonts load.
Size, line height, tracking and weight travel together as a role. Use the .type-<role> class or the --type-<role>-* tokens.
Display
.type-display44 to 84px · line 0.95 · tracking -0.005em · weight 900
Page heroes (PageHero). One per page.
Home opener Oct 8
Title
.type-title36 to 60px · line 0.95 · tracking 0 · weight 900
Article titles (ArticleView) and the lead story card.
Chabot named captain
Headline
.type-headline30 to 44px · line 1 · tracking 0.005em · weight 900
Section titles (SectionHeader).
Latest episodes
Subhead
.type-subhead24 to 32px · line 1.1 · tracking 0.01em · weight 800
Story card headlines (ArticleCard), article h2.
Live from the Carp Fair
Card title
.type-title-sm20 to 24px · line 1.15 · tracking 0.01em · weight 800
Compact card headlines, article h3.
The week ahead
Article body
.type-body-lg18px · line 1.7 · tracking 0 · weight 400
Article paragraphs.
Article paragraphs are 18px with generous line height so they read easily on a phone.
Body
.type-body16px · line 1.6 · tracking 0 · weight 400
Everything else you read in sentences.
Ledes, card summaries, form help.
Label
.type-label14px · line 1.2 · tracking 0.08em · weight 700
Eyebrows (.eyebrow), tags, buttons, nav.
Full episode
Caption
.type-caption12px · line 1.4 · tracking 0.01em · weight 500
Photo credits, fine print.
Photo: Coming In Hot. 4 min read.
The raw steps behind the roles. Reach for a role first.
--fs-xs12px--fs-sm14px--fs-base16px--fs-md18px--fs-lg20 to 24px--fs-xl24 to 32px--fs-2xl30 to 44px--fs-3xl36 to 60px--fs-4xl44 to 84px--fs-numeral64 to 128px, jersey numeralsA 4px base. Space is the cheapest way to make a page feel premium, so be generous between sections and tight inside a card.
--space-14px--space-28px--space-312px--space-416px--space-520px--space-624px--space-832px--space-1040px--space-1248px--space-1664px--space-2080px--section-gap40 to 72px between page sectionsPages sit in a container with a fluid gutter. Grids go 1 column on phones, 2 from 640px, then 3 or 4 from 960px.
--container 1240px--container-narrow 760px--container-wide 1440px| Token | Points to | Value | Use |
|---|---|---|---|
--container | Base value | 1240px | Default page column |
--container-narrow | Base value | 760px | Article column |
--container-wide | Base value | 1440px | Admin and wide grids |
--gutter | Base value | clamp(1rem, 0.6rem + 1.6vw, 2rem) | 16px on phones, 32px on desktop |
--header-h | Base value | 64px | The masthead bar |
--banner-h | Base value | 40px | Prototype banner pinned above the header while this is a demo (0px removes it) |
--header-total | calc(var(--header-h) + 35px + var(--banner-h)) | calc(64px + 35px + 40px) | Everything sticky at the top (banner + top strip + bar + red rule). Use for sticky offsets. |
--tabbar-h | Base value | 60px | Phone tab bar height, not counting the iPhone home-indicator inset |
--tap-target | Base value | 44px | Smallest thing a thumb has to hit |
--app-header-height | --header-total | calc(64px + 35px + 40px) | The sticky header as measured (SiteHeader writes the live value on <html>); the docked player stays below it |
--dock-clearance | Base value | 0px | Room the docked video player needs at the foot of the page (the player writes it on <html> while docked; the footer ends with a spacer this tall) |
At (max-width: 760px): Phones: the header drops its top strip (the tab bar and More sheet carry those links), so the sticky header is the banner, the bar and its red rule (68px plus the banner).
Mobile first: styles start at phone size and add columns as the screen grows. CSS can't read variables inside @media, so queries use these numbers directly.
| Token | Points to | Value | Use |
|---|---|---|---|
--bp-sm | Base value | 480px | Large phones |
--bp-md | Base value | 640px | Two-column grids start |
--bp-phone | Base value | 760px | Phone chrome: at this width and under the tab bar replaces the top strip and Menu button |
--bp-lg | Base value | 960px | Desktop nav, three and four columns |
--bp-xl | Base value | 1200px | Wide desktop |
Sports media is sharp: small radii, flat surfaces, and shadows only when something lifts or floats.
--radius-xs2px. Tags, focus rings--radius-sm4px. Buttons, photos, fields--radius-md6px. Cards--radius-lg10px. Dialogs--radius-pill999px. Filter chips, the Write pill--shadow-smLevel 1: resting controls--shadow-mdLevel 2: hovered cards--shadow-lgLevel 3: dialogs and pop-ups--shadow-redPrimary button hover glow--shadow-bar-upBars pinned to the bottom of the screen--shadow-playPlay buttons over thumbnails--shadow-hardFlat broadcast drop shadow on cut-outs--shadow-inset-hairlineKeeps photos and avatars off same-colour backgrounds--shadow-focus-haloFocused fieldsPick a named layer. Never type a z-index number.
--z-transition200Full-screen page transitions, above everything--z-tour160The guided tour (its dim, light and coach card): over dialogs, sheets and the full size player, under page transitions--z-cinema150The video player opened full size, and its backdrop: over dialogs and sheets, under page transitions--z-overlay100Dialogs, the More sheet, skip link--z-menu60The phone menu and the phone tab bar--z-dock55The docked video player: over the page and the masthead, under the phone menu, tab bar and every overlay--z-header50The site masthead--z-sticky4Sticky toolbars and save bars--z-raised1Lifted inside a card--z-below-1Decoration behind contentQuick and decisive, like a broadcast cut. Most things move in 120 to 200ms. Only big one-off reveals take longer, and everything respects reduced motion.
--easeStandard: quick start, soft landing--ease-outThings arriving on screen--ease-inThings leaving the screen--ease-snapBroadcast wipe: hard in, hard outWith reduced motion switched on in the device settings, these jump straight to the end. The site does the same everywhere.
| Token | Points to | Value | Use |
|---|---|---|---|
--dur-instant | Base value | 80ms | Colour changes on press |
--dur-fast | Base value | 120ms | Hover colour, press nudge |
--dur | Base value | 200ms | Most transitions |
--dur-slow | Base value | 320ms | Panels and drawers |
--dur-slower | Base value | 600ms | Broadcast wipes, one-off reveals |
--dur-pulse | Base value | 1600ms | The live dot, loading shimmer |
--ease | Base value | cubic-bezier(0.2, 0.7, 0.2, 1) | Standard: quick start, soft landing |
--ease-out | Base value | cubic-bezier(0.16, 1, 0.3, 1) | Things arriving on screen |
--ease-in | Base value | cubic-bezier(0.7, 0, 0.84, 0) | Things leaving the screen |
--ease-snap | Base value | cubic-bezier(0.7, 0, 0.2, 1) | Broadcast wipe: hard in, hard out |
Page transitions live in components/transitions/ and sit on --z-transition. See them all at the transitions lab.
One set of simple line icons on a 24px grid, 2px stroke, square ends. They inherit the text colour. Icons support a label; they never replace one, except for universal controls (close, menu, play).
playarrow-rightarrow-leftexternalmenuclosecalendarpinclockmicbagheartcarpentvheadphonescheckinfoflamehomenewspaperplay-boxmoreswatchsparkleplusmaillockalertimageprinterchevron-downdownloadfolderchevron-leftchevron-rightpauseskip-backskip-forwardvolumevolume-offmaximizeminimizeresizeshufflesharearrow-uparrow-up-rightyoutubepodcastinstagramxtiktokfacebookthreadsdiscordIcon propsname56 names, aboveRequired.size20 (px)16 in small buttons, 18 to 20 inline, 24 for standalone controls.labelstringPass it when the icon stands alone and means something. Otherwise it is hidden from screen readers.strokeWidth23 for tiny check and close marks.The NHL-broadcast energy, made from our own parts: speed slashes, score bugs, jersey numerals, ice and the red line. No league or team logos, wordmarks, crests or jersey designs, ever.
<SpeedSlash variant tone side wipe />Game night
Harvest Fundraiser
Latest episodes
Use for
Not for
<ScoreBug label primary secondary tone live size />Use for
Not for
<JerseyNumeral value label tone size filled shadow />Use for
Not for
<PlayerArt number frame size />The art on every player card, chip and sheet is the red alternate sweater seen from the back, the number big across it (Brent’s pick, 2026-10-20). On a card with an NHL head photo the sweater steps back, dimmed and to the right, so the whole number clears his head. Shown at the sizes the site draws it: the phone grid (173px), the desktop grid (260px), the player sheet, and the chips in leader rows, lines and goal cards.
<IceTexture as line padded />Ice, plain
Next live show
Ice with the centre line
Mugmobile sightings
Use for
Not for
<BoardsRule label tone />Use for
Not for
Every shared component, live, in every variant and state. States are the same everywhere: hover, pressed, focus-visible, selected, disabled and loading, all from tokens.
Minimum height 44px. One primary per view. Links get the same look; external links add an icon and say they open a new tab.
Primary. The one main action: Shop the drop, Publish.
Secondary. Strong but second: Watch, See the schedule.
Outline. Quiet actions next to a primary.
Ghost. Low-key text actions: Cancel, See all.
Inverse. Primary action on black or red bands.
Outline inverse. Second action on black or red bands.
Button propsvariant'primary' | 'secondary' | 'outline' | 'ghost' | 'inverse' | 'outline-inverse'Default primary.size'sm' | 'md' | 'lg'sm is 36px: only in dense toolbars.href / externalstring / boolRenders a link. http links open a new tab.icon / iconRightIcon nameblockboolFull width.loadingboolSpinner, aria-busy, no clicks. Keep the label.selectedboolToggle buttons: sets aria-pressed.disabledboolButtons are disabled; links become inert with aria-disabled.Text links outside articles use .text-link. Inside .prose, links are styled automatically. Every state reads a context token (--link-color, --link-color-hover, --link-color-active), so on black and red bands the whole set switches together.
Text link
Text link on black
Small uppercase chips. Plain labels by default; with href or onClick they become links or toggles with full states and a 44px tap area.
Tag propstone'red' | 'black' | 'white' | 'outline' | 'muted' | 'live' | 'sample''live' adds a pulsing dot.size'sm' | 'md'href / onClickstring / functionMakes the tag a link or a toggle.selectedboolaria-current on links, aria-pressed on toggles.SampleLabelsizeRequired on every piece of demo content.The base surface. Feature cards (article, video, product, event) are built on the same look.
Light
The default surface
Dark
For video and stats
Red
Campaigns only
Interactive card (href)
Card propstone'light' | 'dark' | 'red'hrefstringThe whole card links. Turns on interactive.interactiveboolHover lift, pressed, focus ring.selectedboolRed frame for the chosen card in a set.padded / asbool / element48px tall, 16px text (so phones never zoom), a 3:1 border on white, a blue focus halo, and errors in words, not just red.
Hints sit under the field.
Enter a full email address.
File input
Field, Input, TextArea, Select, FileInput propsField: id, label, hint, error, optionalWires the label, hint and error to the control.Input / TextArea / Select: invalid, error, hintbool / stringSets aria-invalid and aria-describedby.FileInput: id, label, note, fileName, invalid, disabledA dashed drop zone around a real file input.Segmented tabs switch views of the same thing. Underline tabs switch sibling pages. The masthead at the top of this page and the phone tab bar are the live site navigation: white links, a white bar under the current page, gold focus rings on black.
Tab states
Tabs propsitems[{ id, label, icon?, href?, disabled? }]href makes link tabs (aria-current="page").value / onChangeid / functionButton tabs use aria-pressed.variant'segmented' | 'underline'labelstringAccessible name of the group.




From the writers
Demo mode: posts you publish here are saved in this browser only. In the real build they save to your own database.
Presented by:Your partner here
Presented by:Your partner here
Full props for every component, including the feature cards, are in DEVELOPERS.md.
How the parts combine into the blocks every page is made of. These are the real components with real feed data.
Every section index opens with PageHero. Show pages pass their show accent.
Watch
Full episodes, practice clips and post-game reaction from the Coming In Hot crew.
A .grid-3 of ArticleCards. The lead story uses the lead variant above it on the home page.
Opening night is October 3 at Scotiabank Arena. This is where the preview column runs.
Chabot wears the C, the season opens in Toronto on October 3 and the home opener is October 8.
The Coming In Hot crew broke down the captaincy news on the show. This is where the written take runs.
Practice and post-practice clips in a .rail: swipe on phones, columns on desktop.
Live from cihmerch.com. Every card links out; checkout stays on Shopify.

$39.00
Buy on cihmerch.com (opens the Coming In Hot Shopify store in a new tab)
$39.00
Buy on cihmerch.com (opens the Coming In Hot Shopify store in a new tab)
$25.00
Buy on cihmerch.com (opens the Coming In Hot Shopify store in a new tab)
$25.00
Buy on cihmerch.com (opens the Coming In Hot Shopify store in a new tab)Black strip from the public NHL schedule. Team names as text, never logos.
Whole screens are part of the system too, so the writer tools feel like the same brand as the site.
Preview: in the real build the team signs in here with email and a password, or a link sent to their email. Not active in the demo.
We sent a sign-in link to graeme@example.com. Open it on this device and you are in. The link works once.
Preview only: no email was sent.
Signed in as graeme@example.com.
Preview only: nothing is saved.
The signed-in back end, in the same broadcast look: a black page band, panels with a black head and a red rule, score-bug tiles and roster rows. The rules are in DEVELOPERS.md, under Studio style guide.
Studio
Write or paste your story, add a photo, and publish. Everything saves as you type.
How your name reads
Visitors
1,204 (sample number)
Unique each day, added up
Read through
64% (sample number)
Reached 75% of the story
Sign-ups
– (sample number)
A dash when a number is not known
Nothing scheduled
Pick a date and time when you publish, and the post goes live on its own.
Where writers paste from Google Docs or Word, add a photo, pick an author and show, preview and publish. Shown live and scaled down.
Open the editorThe words are part of the brand. These rules apply to every page, post, button and commit.
Use a full stop, a comma or a colon. Feeds and pasted posts are cleaned automatically.
Demo articles and events carry the yellow Sample label until real content replaces them.
Coming In Hot is independent. "Award-winning" is fine; it is the show’s own claim.
No made-up quotes, stats, injuries or trades about real people. Sample body copy says it is a sample.
Ad and sponsor slots stay generic placeholders until a partner signs. No third-party logos.
Short sentences, plain words, Ottawa first. "Watch the full episode", not "Explore our content offerings".
Most Sens fans will open this on a phone, outside, one-handed. Accessible is the same thing as usable.
Body text meets 4.5:1 and UI parts 3:1. 57 of 57 declared pairs pass, and the build fails if one drops. See the pairs.
A 3px ring on every interactive part: blue on light, gold on black and red. Tab through this page to try it.
Every button, tab and interactive tag is at least 44px tall. Small chips get an invisible larger hit area.
With reduced motion on, animations and transitions cut to near zero, and page transitions become a short crossfade.
Alt text on photos, labels on every field, errors in words, icon-only buttons with a label, and external links that say they open a new tab.
Reading text is 16 to 18px with generous line height. Nothing goes below 12px, and fields use 16px so phones never zoom on focus.