One system, two themes, four colours that mean something.
OntarioPulse follows the Front Dashboard v2 system that runs tools.centilio.com: Inter at 14px, headings at 600, a single electric blue for action, green for good, red for attention, and black surfaces in dark mode so those three colours carry. Everything below is live CSS from the dashboard, not a mockup.
Colour
Four brand colours plus neutrals. Blue is the only colour that means "click me". Green and red are reserved for meaning (better or worse than the state, passed or failed) and never used decoratively. Purple and teal are chart accents only.
Dark theme (black)
Surfaces go to near-black with a hint of blue (#06080F page, #0E121A cards, 7% white hairlines) and the accents stay exactly the same as light mode, so blue, green and red read the same on both. The only thing that flips is the CTA: navy on white becomes white on black.
Typography
Inter only, loaded at 400, 500, 600 and 700. Headings are 600, never 800. Numbers are 700 with tabular figures so columns line up. Labels that describe a number (KPI titles, table headers, card subtitles) are the Front "text-cap": 11.4px, 600, uppercase, 0.5px letter-spacing.
--fs-h1 · store name, page titles--fs-h2 · document sections--fs-h3 · dashboard section headers--fs-h4 · card and table titles--fs-num · KPI values, gauges--fs-body · copy, subtitles, tips--fs-sm · meta pills, notes--fs-xs · KPI labels, table headersComponents
Front card rules: white surface, 1px #E7EAF3 border, 12px radius, 6px/12px soft shadow; on hover the card lifts 3px and the shadow deepens. Data blocks add the running conic border (the same technique as the Sessions add-to-cart button) so a table you are pointing at is unmistakable.
Buttons
One primary per view. Ghost buttons live in the dock. Pills carry facts, never actions.
Badges and deltas
Soft badges: 10% tint background, solid text. Deltas always carry an arrow; green means good for the store, whatever the direction of the number.
Progress in tables
Excel-style data bars: a tinted fill sits behind the number inside the cell, so the bar, the number and the column header share one right edge. The fill grows from zero when the table scrolls into view, 40ms later per row. Green for money, blue for units, teal for store counts and shares; the price index bar goes green when the store is cheaper than the state and red when dearer, matching the delta chips.
Table
| Rank | Product | Category | Est. Sales | Stores | You |
|---|---|---|---|---|---|
| 1 | Boysenberry WYLD | Edible 100mg | $147.3K | 169 | Yes |
| 2 | Golden Goat 28.0g Natty Rems | Flower 28.0g | $116.9K | 20 | Yes |
| 3 | Tropical Storm BATCH | Vape 1000mg | $101.3K | 23 | No |
Header: panel background, text-cap in body grey; every header sorts on click (descending, ascending, off). Rows: 1px hairline, hover tint, the store's own row highlighted with a blue edge. Rank chips gold, silver, bronze for the top 3 with navy text, neutral after; the rank stays with the row when you sort. Prices drop the .00 when whole. Product cells: photo, brand mark, name, brand tag in subtle grey. Hover the block to see the running border and the expand control; sections open in Focus (one table in front, the rest peeking behind, arrows or keyboard to cycle) with Grid one click away, and any table opens full screen.
Motion
Motion explains, it never decorates. Everything respects prefers-reduced-motion.
Fill on arrival
Bars, meters and gauges start at zero and grow to their value the moment their card enters the viewport (IntersectionObserver, 0.9s, ease-out). KPI numbers count up over 0.95s.
Running border
Cards and table blocks show a conic-gradient beam (blue into green) travelling round the edge on hover or focus, 2.4s per lap. It marks focus, so only one block glows at a time.
Loading with the truth
The loading screen lists the five steps the server actually runs and ticks them as time passes, with a flux gradient bar. After 9s it explains why chains take a minute. Never a blank screen, never a fake percentage.
Dock and focus stack
Header actions sit in a glass dock and swell up to 13% toward the cursor. Table sections stack like a deck: the front table is live, the two behind peek at 70% and 40%, and next/previous slides them forward in 450ms.
Empty states
When something is not configured or has no data, an illustration and one sentence say why and what will change it. No empty boxes, no "N/A".
Demo
The tour spotlights one section at a time with a 350ms slide between steps, keyboard navigable, and closes on Esc.
Do and don't
Do
- Use tokens. A hex literal in a component is a bug.
- One blue for actions; green and red only for meaning.
- Headings 600, numbers 700, everything else 400 or 500.
- Every number gets a bar, a meter or a delta so it can be read at a glance.
- Show the store's own row wherever it ranks, even when locked rows surround it.
- Say where a number came from (CannMenus, Google, the store site).
Don't
- No navy table headers, no gradients on text, no 800 weights, no brown text on the gold chip.
- No purple in the product; blue and purple side by side read as two different actions.
- No fake data, placeholders or teased Pro+ cards; hidden means hidden.
- No animation longer than 1.3s, nothing that loops except the beam and the loader.
- No Sessions Cannabis colours, names or hostnames anywhere in this product.
- No unicode dashes or emoji in UI copy.