mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
add otd-operations-brief live-artifact template (#794)
Adds a Mono Crimson Operations Brief live-artifact template under templates/live-artifacts/otd-operations-brief/. The template ships: - template.html: html_template_v1 source, fully unrolled (no data-od-repeat — daemon renderer is scalar-only) for 4 KPIs, 14 bar rows, and 8 lowest-OTD rows; - data.json: default sample with pre-computed bar fills, prior-year ticks, and CSS class names so the template binds purely as scalars; - artifact.json + provenance.json: stored-snapshot fixtures that mirror specs/2026-04-29-live-artifacts/examples/minimal-static/; - DESIGN.md: full Mono Crimson Operations Brief 9-section design spec (warm off-white canvas, charcoal bars, single-accent crimson); - index.html + preview.png: pre-rendered default display sample so reviewers can see the artifact without spinning up a daemon. Template-level only — no feature/code changes. Co-authored-by: joey <joey@joeydeMacBook-Air.local>
This commit is contained in:
parent
47a014d377
commit
063e3b59c2
8 changed files with 1875 additions and 0 deletions
418
templates/live-artifacts/otd-operations-brief/DESIGN.md
Normal file
418
templates/live-artifacts/otd-operations-brief/DESIGN.md
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
---
|
||||
version: alpha
|
||||
name: Mono Crimson Operations Brief
|
||||
description: >
|
||||
An editorial, single-accent operations dashboard system: warm off-white canvas,
|
||||
bright white flat cards with hairline borders, charcoal horizontal bars paired
|
||||
with vivid crimson prior-year ticks, tabular-figure KPI numbers, light-grey
|
||||
filter pills, and a strict mono-crimson accent reserved for negatives,
|
||||
prior-year markers, and "lowest" callouts — built for OTD / on-time-delivery,
|
||||
supply-chain, audit, finance and operations performance briefs.
|
||||
colors:
|
||||
surface: "#FFFFFF"
|
||||
surface-muted: "#F4F5F7"
|
||||
surface-subtle: "#F8F9FB"
|
||||
canvas: "#FAFAFB"
|
||||
border: "#E6E7EB"
|
||||
border-strong: "#D6D8DD"
|
||||
hairline: "#EEEFF2"
|
||||
text-primary: "#0B1220"
|
||||
text-secondary: "#4B5563"
|
||||
text-tertiary: "#6B7280"
|
||||
text-quiet: "#9CA3AF"
|
||||
text-quietest: "#B7BCC4"
|
||||
accent-crimson: "#C2272D"
|
||||
accent-crimson-strong: "#A11D22"
|
||||
accent-crimson-soft: "#FCE6E7"
|
||||
data-ink: "#0F1B2E"
|
||||
data-ink-soft: "#1E2A3D"
|
||||
data-track: "#E6E7EB"
|
||||
data-track-soft: "#EFF0F3"
|
||||
tier-low: "#F1F2F4"
|
||||
tier-mid: "#DDDFE3"
|
||||
tier-high: "#C9CBD0"
|
||||
pill-bg: "#F1F2F4"
|
||||
pill-fg: "#4B5563"
|
||||
status-up-fg: "#15803D"
|
||||
status-down-fg: "#C2272D"
|
||||
typography:
|
||||
display:
|
||||
fontFamily: "'Inter', 'IBM Plex Sans', system-ui, sans-serif"
|
||||
fontSize: 17px
|
||||
fontWeight: 700
|
||||
lineHeight: 1.25
|
||||
letterSpacing: "-0.005em"
|
||||
card-title:
|
||||
fontFamily: "'Inter', 'IBM Plex Sans', system-ui, sans-serif"
|
||||
fontSize: 12.5px
|
||||
fontWeight: 700
|
||||
lineHeight: 1.3
|
||||
letterSpacing: "0.04em"
|
||||
textTransform: "uppercase"
|
||||
kpi-label:
|
||||
fontFamily: "'Inter', 'IBM Plex Sans', system-ui, sans-serif"
|
||||
fontSize: 10.5px
|
||||
fontWeight: 600
|
||||
lineHeight: 1.3
|
||||
letterSpacing: "0.06em"
|
||||
textTransform: "uppercase"
|
||||
kpi-number:
|
||||
fontFamily: "'Inter', 'IBM Plex Sans', system-ui, sans-serif"
|
||||
fontSize: 34px
|
||||
fontWeight: 700
|
||||
lineHeight: 1.05
|
||||
letterSpacing: "-0.018em"
|
||||
fontFeatureSettings: "'tnum', 'lnum'"
|
||||
kpi-caption:
|
||||
fontFamily: "'Inter', 'IBM Plex Sans', system-ui, sans-serif"
|
||||
fontSize: 11.5px
|
||||
fontWeight: 500
|
||||
lineHeight: 1.4
|
||||
color: "{colors.text-tertiary}"
|
||||
body-md:
|
||||
fontFamily: "'Inter', system-ui, sans-serif"
|
||||
fontSize: 12.5px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.45
|
||||
body-sm:
|
||||
fontFamily: "'Inter', system-ui, sans-serif"
|
||||
fontSize: 11.5px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.45
|
||||
table-header:
|
||||
fontFamily: "'Inter', system-ui, sans-serif"
|
||||
fontSize: 10px
|
||||
fontWeight: 600
|
||||
lineHeight: 1.3
|
||||
letterSpacing: "0.07em"
|
||||
textTransform: "uppercase"
|
||||
color: "{colors.text-quiet}"
|
||||
pill:
|
||||
fontFamily: "'JetBrains Mono', 'IBM Plex Mono', 'SFMono-Regular', monospace"
|
||||
fontSize: 11px
|
||||
fontWeight: 500
|
||||
lineHeight: 1.3
|
||||
letterSpacing: "0.01em"
|
||||
numeric:
|
||||
fontFamily: "'Inter', system-ui, sans-serif"
|
||||
fontSize: 12px
|
||||
fontWeight: 500
|
||||
lineHeight: 1.3
|
||||
fontFeatureSettings: "'tnum', 'lnum'"
|
||||
numeric-strong:
|
||||
fontFamily: "'Inter', system-ui, sans-serif"
|
||||
fontSize: 12px
|
||||
fontWeight: 700
|
||||
lineHeight: 1.3
|
||||
fontFeatureSettings: "'tnum', 'lnum'"
|
||||
rounded:
|
||||
none: 0px
|
||||
xs: 3px
|
||||
sm: 4px
|
||||
md: 6px
|
||||
lg: 8px
|
||||
xl: 10px
|
||||
pill: 999px
|
||||
spacing:
|
||||
xxs: 2px
|
||||
xs: 4px
|
||||
sm: 6px
|
||||
md: 8px
|
||||
lg: 12px
|
||||
xl: 16px
|
||||
xxl: 20px
|
||||
xxxl: 24px
|
||||
section: 28px
|
||||
shadow:
|
||||
none: "none"
|
||||
flat: "0 0 0 1px {colors.border}"
|
||||
card: "0 1px 0 rgba(11,18,32,.02)"
|
||||
patterns:
|
||||
bar-track: "{colors.data-track}"
|
||||
components:
|
||||
app-shell:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
padding: 0
|
||||
page-card:
|
||||
backgroundColor: "{colors.surface}"
|
||||
rounded: "{rounded.md}"
|
||||
border: "1px solid {colors.border}"
|
||||
padding: 18px
|
||||
shadow: "{shadow.card}"
|
||||
page-header:
|
||||
typography: "{typography.display}"
|
||||
textColor: "{colors.text-primary}"
|
||||
paddingBottom: 14px
|
||||
filter-pill:
|
||||
backgroundColor: "{colors.pill-bg}"
|
||||
textColor: "{colors.pill-fg}"
|
||||
typography: "{typography.pill}"
|
||||
rounded: "{rounded.sm}"
|
||||
padding: "3px 8px"
|
||||
border: "1px solid {colors.hairline}"
|
||||
kpi-card:
|
||||
backgroundColor: "{colors.surface}"
|
||||
rounded: "{rounded.md}"
|
||||
border: "1px solid {colors.border}"
|
||||
padding: "14px 16px"
|
||||
shadow: "{shadow.card}"
|
||||
minHeight: "100px"
|
||||
kpi-card-label:
|
||||
typography: "{typography.kpi-label}"
|
||||
textColor: "{colors.text-secondary}"
|
||||
paddingBottom: 6px
|
||||
kpi-card-number:
|
||||
typography: "{typography.kpi-number}"
|
||||
textColor: "{colors.text-primary}"
|
||||
kpi-card-number-negative:
|
||||
typography: "{typography.kpi-number}"
|
||||
textColor: "{colors.accent-crimson}"
|
||||
kpi-card-caption:
|
||||
typography: "{typography.kpi-caption}"
|
||||
paddingTop: 6px
|
||||
card:
|
||||
backgroundColor: "{colors.surface}"
|
||||
rounded: "{rounded.md}"
|
||||
border: "1px solid {colors.border}"
|
||||
padding: "16px 18px 14px"
|
||||
shadow: "{shadow.card}"
|
||||
card-title:
|
||||
typography: "{typography.card-title}"
|
||||
textColor: "{colors.text-primary}"
|
||||
paddingBottom: 12px
|
||||
bar-row:
|
||||
height: "20px"
|
||||
paddingY: 4px
|
||||
typography: "{typography.body-sm}"
|
||||
bar-row-label:
|
||||
typography: "{typography.body-md}"
|
||||
textColor: "{colors.text-secondary}"
|
||||
width: "150px"
|
||||
align: "right"
|
||||
paddingRight: 12px
|
||||
bar-track:
|
||||
backgroundColor: "{colors.data-track}"
|
||||
rounded: "{rounded.xs}"
|
||||
height: "12px"
|
||||
bar-fill:
|
||||
backgroundColor: "{colors.data-ink}"
|
||||
rounded: "{rounded.xs}"
|
||||
height: "12px"
|
||||
bar-prior-tick:
|
||||
backgroundColor: "{colors.accent-crimson}"
|
||||
width: "2px"
|
||||
height: "16px"
|
||||
offsetY: "-2px"
|
||||
bar-row-value:
|
||||
typography: "{typography.numeric}"
|
||||
textColor: "{colors.text-quiet}"
|
||||
paddingLeft: 12px
|
||||
width: "150px"
|
||||
legend-row:
|
||||
typography: "{typography.body-sm}"
|
||||
textColor: "{colors.text-tertiary}"
|
||||
paddingTop: 12px
|
||||
borderTop: "1px solid {colors.hairline}"
|
||||
gap: 16px
|
||||
legend-swatch-bar:
|
||||
width: "18px"
|
||||
height: "10px"
|
||||
rounded: "{rounded.xs}"
|
||||
legend-swatch-tick:
|
||||
width: "2px"
|
||||
height: "12px"
|
||||
backgroundColor: "{colors.accent-crimson}"
|
||||
legend-swatch-tier:
|
||||
width: "22px"
|
||||
height: "10px"
|
||||
rounded: "{rounded.xs}"
|
||||
table-card:
|
||||
backgroundColor: "{colors.surface}"
|
||||
rounded: "{rounded.md}"
|
||||
border: "1px solid {colors.border}"
|
||||
padding: "16px 18px 4px"
|
||||
shadow: "{shadow.card}"
|
||||
table-header-cell:
|
||||
typography: "{typography.table-header}"
|
||||
paddingY: 8px
|
||||
paddingX: 4px
|
||||
table-row:
|
||||
paddingY: 10px
|
||||
borderBottom: "1px solid {colors.hairline}"
|
||||
typography: "{typography.body-md}"
|
||||
table-rank:
|
||||
typography: "{typography.numeric}"
|
||||
textColor: "{colors.text-quiet}"
|
||||
width: "20px"
|
||||
table-name:
|
||||
typography: "{typography.body-md}"
|
||||
textColor: "{colors.text-primary}"
|
||||
fontWeight: 500
|
||||
red-mini-bar-track:
|
||||
backgroundColor: "{colors.surface-muted}"
|
||||
rounded: "{rounded.xs}"
|
||||
height: "16px"
|
||||
red-mini-bar-fill:
|
||||
backgroundColor: "{colors.accent-crimson}"
|
||||
rounded: "{rounded.xs}"
|
||||
height: "16px"
|
||||
red-mini-bar-label:
|
||||
typography: "{typography.numeric-strong}"
|
||||
textColor: "{colors.surface}"
|
||||
paddingX: 6px
|
||||
align: "right"
|
||||
hairline:
|
||||
backgroundColor: "{colors.hairline}"
|
||||
height: "1px"
|
||||
---
|
||||
|
||||
## 1. Visual Theme & Atmosphere
|
||||
|
||||
**Mono Crimson Operations Brief** is an editorial, almost newspaper-feeling operations dashboard system tuned for **on-time delivery (OTD)**, supply-chain audits, finance variance reports, vendor performance, manufacturing quality briefs and any "what is our number this month" executive view.
|
||||
|
||||
The visual posture is **calm, factual, and slightly stern** — like a printed audit report. Surfaces are bright white with a 1px hairline; the canvas is a warm off-white. Numbers are big, charts are flat, and **a single vivid crimson is the only accent in the entire system**. Crimson is reserved for three things only: negative deltas, prior-year markers on bars, and the "lowest performers" mini-bars in the red-list. Everything else lives in a five-step charcoal-to-light-grey ramp.
|
||||
|
||||
The overall feeling is "page from a printed performance brief" rather than "modern SaaS dashboard": no gradients, no glass, no shadows beyond a 1px optical hairline, no rounded blobs, no decorative iconography.
|
||||
|
||||
- **Visual style:** editorial · audit · operations · monochrome
|
||||
- **Color stance:** mono-accent (crimson on greyscale)
|
||||
- **Design intent:** Make a single number, a single ranking, and a single comparison legible at a glance, on white, in print or in a meeting room projector.
|
||||
|
||||
## 2. Color
|
||||
|
||||
The palette is a **greyscale ramp + one crimson**.
|
||||
|
||||
- **Canvas `#FAFAFB`** — viewport background. Never use pure white as the canvas; the warm off-white separates cards from the page.
|
||||
- **Surface `#FFFFFF`** — every card, KPI tile, table, and the right-side ranking list.
|
||||
- **Surface-muted `#F4F5F7`** — the empty track behind crimson mini-bars in the lowest-OTD list, and the subtle hover state on table rows.
|
||||
- **Border `#E6E7EB`** — the universal 1px hairline that draws every card outline. Never use a thicker border, never use a colored border.
|
||||
- **Hairline `#EEEFF2`** — internal dividers between table rows and between the legend row and the chart body.
|
||||
- **Text-primary `#0B1220`** — page title, card titles, KPI numbers, table account names. Near-black, not pure black, to keep the page feeling printed.
|
||||
- **Text-secondary `#4B5563`** — bar-row labels (account names on the left of bars), filter-pill text.
|
||||
- **Text-tertiary `#6B7280`** — legend text, KPI captions ("August 2021 · EUR", "Current – Prior year").
|
||||
- **Text-quiet `#9CA3AF`** — the percentage values to the right of bars (e.g. `83.9% (PY 73.9%)`), table rank numbers, table OTD-lines column.
|
||||
- **Accent crimson `#C2272D`** — the only saturated color in the system. It appears in **exactly three** roles:
|
||||
1. Negative KPI numbers (e.g. `YOY Δ (PTS) -3.9 pts`).
|
||||
2. Vertical prior-year ticks on each horizontal bar.
|
||||
3. Mini-bars in the "Lowest OTD % accounts" list (with white tabular-figure label inside the fill).
|
||||
- **Data-ink `#0F1B2E`** — the charcoal fill of every horizontal bar. Slightly cooler than pure black; reads as "ink" against white.
|
||||
- **Data-track `#E6E7EB`** — the unfilled remainder of every horizontal bar, drawn as a single track that the dark fill sits on top of.
|
||||
|
||||
The legend at the bottom of the bar chart uses three tier swatches: `tier-low #F1F2F4` (0–60%), `tier-mid #DDDFE3` (60–80%), `tier-high #C9CBD0` (80–100%). These are quiet on purpose — they communicate "scale categories," not "data colors."
|
||||
|
||||
## 3. Typography
|
||||
|
||||
Use **Inter** (or **IBM Plex Sans** as a print-leaning alternative) as the only typeface family. The system uses three weights only: 400 (body), 500–600 (labels), and 700 (titles + KPI numbers). Filter pills may optionally use a monospace face (**JetBrains Mono / IBM Plex Mono**) to read like cell formula tokens (`Date = August 2021`, `Exchange Rate[From Currency] = "EUR"`).
|
||||
|
||||
- **Page title** (`On-Time Delivery Dashboard`) — 17px, weight 700, tight tracking. No subtitle, no decorative emoji.
|
||||
- **Card title** (`OTD % (LINES) BY KEY ACCOUNT`, `LOWEST OTD % ACCOUNTS (≥10 LINES)`) — 12.5px, weight 700, **uppercase, +0.04em tracking**. Always uppercase. This is the editorial signature.
|
||||
- **KPI label** (`OTD % (LINES)`, `YOY Δ (PTS)`) — 10.5px, weight 600, **uppercase, +0.06em tracking**, in `text-secondary`.
|
||||
- **KPI number** — 34px, weight 700, line-height 1.05, slightly negative tracking, **tabular numerals enabled** (`font-feature-settings: "tnum", "lnum"`). Negative numbers are rendered in `accent-crimson`; all others in `text-primary`.
|
||||
- **KPI caption** (`August 2021 · EUR`, `+5 457 vs PY (200 241)`) — 11.5px, weight 500, in `text-tertiary`. The middle dot `·` separates the period from the currency.
|
||||
- **Bar-row label** (`Antarctic Corporation`, `Government Contract`, …) — 12.5px, weight 400, in `text-secondary`, right-aligned to the bar.
|
||||
- **Bar-row value** (`83.9% (PY 73.9%)`) — 12px tabular, in `text-quiet`. Always `XX.X% (PY YY.Y%)` exactly — never reorder.
|
||||
- **Table header** (`#`, `ACCOUNT NAME`, `OTD %`, `OTD (LINES)`) — 10px, weight 600, **uppercase, +0.07em tracking**, in `text-quiet`.
|
||||
- **Filter pill** (`Date = August 2021`) — 11px monospace, weight 500, in `pill-fg`, on `pill-bg`. Always written as `Key = Value` with surrounding spaces; string values are quoted (`"EUR"`).
|
||||
|
||||
Tabular numerals are mandatory on every KPI number, every percentage, every count, every rank. Digits in adjacent cards must visually line up to the pixel.
|
||||
|
||||
## 4. Spacing & Grid
|
||||
|
||||
The page is built on a **6-column 12-gutter grid** inside a 20px outer canvas gutter. KPI strip occupies 4 equal columns. The body splits **5+3** columns: bar chart (left, 5/8) and lowest-accounts table (right, 3/8).
|
||||
|
||||
- Spacing scale: `2 / 4 / 6 / 8 / 12 / 16 / 20 / 24 / 28px`. Do not invent intermediate values.
|
||||
- Card padding: 14–18px (KPI tiles 14px vertical, 16px horizontal; bar chart card 16/18px).
|
||||
- Vertical gap between page header and the KPI strip: **12px**.
|
||||
- Vertical gap between the KPI strip and the body row: **14px**.
|
||||
- Horizontal gap between cards in any row: **12px**.
|
||||
- Bar-row height: **20px** (12px bar + 4px vertical breathing on each side).
|
||||
- Table-row height: **44–48px** (account name wraps to two lines).
|
||||
|
||||
## 5. Layout & Composition
|
||||
|
||||
The canonical page composition (top to bottom) is:
|
||||
|
||||
1. **Header row** (full width, 36px tall): page title on the far left + filter-pill cluster on the far right. No back-button, no breadcrumb, no avatar.
|
||||
2. **KPI strip** (4 equal cards): label → very large number → 1-line caption. Each card is independently bordered.
|
||||
3. **Body row** — two cards side-by-side:
|
||||
- **Left, 5/8 width:** "BY KEY ACCOUNT" — vertical stack of horizontal bars, ordered descending by current-period value, with prior-year ticks. A 3-swatch tier legend lives at the bottom.
|
||||
- **Right, 3/8 width:** "LOWEST OTD % ACCOUNTS (≥10 LINES)" — ranked table with red mini-bars and a tabular OTD-LINES count column.
|
||||
|
||||
The grid is rigid. Do **not** introduce a sidebar, a topbar, or a hero strip; this style is meant for a single full-bleed dashboard panel that can be embedded in a report page.
|
||||
|
||||
## 6. Components
|
||||
|
||||
### Page Header
|
||||
|
||||
A single line: 17px bold title on the left, **filter pills cluster** on the right. Pills are pill-shaped only at the corners; otherwise they are 4px-radius rectangles with `pill-bg` fill, `hairline` border, and monospace label. They communicate **filter context as expressions** (`Date = August 2021`, `Exchange Rate[From Currency] = "EUR"`). Never use them as clickable buttons in this style — they are read-only context tags.
|
||||
|
||||
### KPI Card (×4)
|
||||
|
||||
Bright white, 1px `border`, 6px radius, ~100px tall, padding 14×16. Three vertically stacked elements:
|
||||
|
||||
1. Tiny uppercase **label** in `text-secondary`.
|
||||
2. Very large **number** in `text-primary` — or `accent-crimson` if it carries a leading minus sign / represents a negative delta.
|
||||
3. One-line **caption** in `text-tertiary`. Caption may include a thin secondary number in parentheses (`+5 457 vs PY (200 241)`).
|
||||
|
||||
There is no icon, no overflow menu, no trend chip, no sparkline. The whole card is type-only.
|
||||
|
||||
### Bar Chart Card ("BY KEY ACCOUNT")
|
||||
|
||||
A single card with a top-aligned uppercase title and a vertical stack of bar rows. Each row has three slots:
|
||||
|
||||
- **Left slot (label, ~150px, right-aligned):** account name in 12.5px `text-secondary`.
|
||||
- **Middle slot (track + fill, fluid):** a 12px-tall `data-track` track filling the slot, with a `data-ink` fill from 0% to the current-period percentage. **Always overlaid by a 2px-wide, 16px-tall vertical crimson tick** at the prior-year percentage position. The tick extends 2px above and 2px below the bar so it visually punctures the track.
|
||||
- **Right slot (value, ~150px, left-aligned):** `XX.X% (PY YY.Y%)` in 12px tabular `text-quiet`.
|
||||
|
||||
Rows are separated by 8px of vertical padding (no row borders). At the bottom of the card, a single **legend row** documents the visual grammar: a small charcoal bar swatch labeled "OTD % (Lines) — Aug 2021", a 2px crimson tick swatch labeled "Prior year (Aug 2020)", and three tier swatches "0-60%", "60-80%", "80-100%". The legend row is separated from the chart by a 1px `hairline`.
|
||||
|
||||
### Lowest-OTD Table
|
||||
|
||||
A single card with the same border / radius as the bar card. Title row, then a 4-column header (`#`, `ACCOUNT NAME`, `OTD %`, `OTD (LINES)`) in `table-header` style with a 1px `hairline` underline, then 8 rows. Each row:
|
||||
|
||||
- **Rank cell** — 1- or 2-digit number in `text-quiet`, tabular.
|
||||
- **Name cell** — 12.5px `text-primary` weight 500, allowed to wrap to two lines (e.g. `The Ruby / Black Hole`).
|
||||
- **OTD% cell** — a 16px-tall **crimson mini-bar** filling roughly `value / 60%` of a `surface-muted` track, with the **value (`35.9%`, `40.7%`, …) inset in white tabular bold inside the right edge of the fill**. The fill always starts from the left edge of the track.
|
||||
- **OTD (LINES) cell** — small tabular count in `text-quiet`, right-aligned (`14`, `11`, `10`, …).
|
||||
|
||||
Rows are separated by `hairline` 1px dividers. There is no row hover state.
|
||||
|
||||
### Filter Pill
|
||||
|
||||
`pill-bg` fill, `hairline` border, 4px radius, 11px monospace text, 3×8px padding. The grammar is **always `Key = Value`** with single spaces around the `=`. String values are double-quoted. Do not use them as buttons; they signal "this view is filtered to these constraints."
|
||||
|
||||
## 7. Motion & Interaction
|
||||
|
||||
This style is intentionally **static**. It is meant to read like a printed page.
|
||||
|
||||
- No hover lift on cards.
|
||||
- No row hover fill on tables (a barely visible `surface-muted` background is permitted, but discouraged).
|
||||
- No skeleton shimmer. Use a `text-quietest` em-dash on missing values instead.
|
||||
- Tooltip on bar hover (optional): a small 4px-radius white card with a 1px `border`, listing exact `Current` and `Prior year` percentages and the LINES count. Never animate its appearance with anything heavier than `opacity 120ms ease-out`.
|
||||
|
||||
## 8. Voice & Brand
|
||||
|
||||
The voice is **factual, audit-style, and quietly authoritative**. Card titles are uppercase nouns (`BY KEY ACCOUNT`, `LOWEST OTD % ACCOUNTS (≥10 LINES)`). Captions describe constraints with parenthetical precision (`(≥10 LINES)`, `(PY 73.9%)`). The dashboard speaks like an analyst, not like a marketing hero.
|
||||
|
||||
- Never use exclamation marks.
|
||||
- Never use emoji.
|
||||
- Never use first-person ("our", "your", "we").
|
||||
- Currency / period in captions is separated by a middle dot (`August 2021 · EUR`).
|
||||
- Negative deltas are written `-3.9 pts`, not `(3.9)` or `-3.9%` — keep "pts" / "%" units explicit.
|
||||
|
||||
## 9. Anti-patterns
|
||||
|
||||
**Don't:**
|
||||
|
||||
- Use any color other than the greyscale ramp + crimson. No blue, green, amber, mint, purple — even for "info", "success", "warning". Negative is the only differentiated state, and it is crimson.
|
||||
- Add card shadows beyond the 1px optical hairline.
|
||||
- Use rounded corners larger than 6–8px on cards. Pills cap at 4px. Avatars and big rounded blobs are foreign to this system.
|
||||
- Use bold weights heavier than 700 or lighter than 400.
|
||||
- Substitute proportional digits for tabular digits anywhere a number appears.
|
||||
- Use crimson outside the three sanctioned roles (negative numbers, prior-year ticks, lowest-OTD mini-bars). Do not crimson-color a CTA, a heading underline, or a row hover.
|
||||
- Add gradients (no, not even subtle background washes).
|
||||
- Add icons to KPI cards or table cells. The system is type-only.
|
||||
- Switch to dark mode. There is no dark variant.
|
||||
- Use solid horizontal bars without the crimson prior-year tick — the tick is the point.
|
||||
- Reorder the bar-row value string. It is **always** `XX.X% (PY YY.Y%)`.
|
||||
- Use a sentence-case card title. Card titles are always uppercase tracked.
|
||||
73
templates/live-artifacts/otd-operations-brief/README.md
Normal file
73
templates/live-artifacts/otd-operations-brief/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# `otd-operations-brief` · live-artifact template
|
||||
|
||||
> Category: **Live Artifacts**
|
||||
> Family: operations / supply-chain / on-time-delivery / vendor performance
|
||||
> Style: **Mono Crimson Operations Brief** (warm-white canvas · charcoal bars · single-accent crimson tick · tabular figures)
|
||||
|
||||
A drop-in `html_template_v1` live-artifact template for an editorial On-Time Delivery brief. It ships:
|
||||
|
||||
- a tokenized HTML template (`template.html`) wired entirely with Open Design's scalar-only `{{data.X}}` bindings;
|
||||
- a default sample `data.json` covering a 14-account month-over-month OTD slice plus a "lowest 8" deep-dive;
|
||||
- the canonical `artifact.json` and `provenance.json` shapes the Open Design daemon expects;
|
||||
- a pre-rendered `index.html` and `preview.png` so reviewers can see the artifact without spinning up a daemon.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
templates/live-artifacts/otd-operations-brief/
|
||||
├── README.md ← this file
|
||||
├── DESIGN.md ← the Mono Crimson Operations Brief design spec (9-section schema)
|
||||
├── template.html ← html_template_v1 template (scalar {{data.X}} bindings only)
|
||||
├── data.json ← default sample data the template binds against
|
||||
├── artifact.json ← live-artifact stored snapshot (mirrors the spec fixture format)
|
||||
├── provenance.json ← provenance fixture noting that the figures are illustrative
|
||||
├── index.html ← pre-rendered preview = template.html × data.json (default display sample)
|
||||
└── preview.png ← 4:3 thumbnail of index.html for picker / gallery surfaces
|
||||
```
|
||||
|
||||
`index.html` is daemon-derived in production (see `apps/daemon/src/live-artifacts/render.ts`) — it is checked in here only to give reviewers a static preview. Do not edit it by hand; regenerate it from `template.html` + `data.json` if either changes.
|
||||
|
||||
## How it binds
|
||||
|
||||
Open Design's `html_template_v1` renderer is intentionally narrow:
|
||||
|
||||
- only `{{data.path.to.value}}` interpolation, paths must start with `data`;
|
||||
- bindings must resolve to scalars (no array or object values);
|
||||
- there is no repeat / loop / conditional directive — the template is **fully unrolled** for KPI 0..3, bar 0..13, and lowest-row 0..7;
|
||||
- the renderer rejects `<script>`, `<iframe>`, `srcdoc=`, event-handler attributes, `javascript:` URLs, and `data-od-html|raw|bind-html` directives.
|
||||
|
||||
Refresh callers writing into `data.json` should preserve the same shape and cardinality (`kpis[4]`, `byKeyAccount.rows[14]`, `lowestAccounts.rows[8]`) so the unrolled template keeps rendering cleanly. Pre-compute every value the template binds — bar widths, prior-year ticks, formatted strings, and CSS class names — because the renderer cannot evaluate expressions.
|
||||
|
||||
## When to use this template
|
||||
|
||||
Use this template when an agent is asked to produce a refreshable artifact in any of these shapes:
|
||||
|
||||
- monthly / weekly OTD or fill-rate brief;
|
||||
- supplier-performance or carrier-performance scorecard;
|
||||
- finance / procurement variance brief with prior-year comparison;
|
||||
- audit-style operations summary where the readability bar is "looks like a printed report".
|
||||
|
||||
The Mono Crimson Operations Brief style enforces a single chromatic accent (crimson) reserved for negatives, prior-year ticks, and "lowest" call-outs. See `DESIGN.md` for the full token grammar.
|
||||
|
||||
## When **not** to use this template
|
||||
|
||||
- consumer marketing dashboards or product analytics surfaces — too austere, no headroom for hero imagery or branded gradients;
|
||||
- multi-tenant home pages — designed for a single subject (one month, one currency, one division);
|
||||
- mobile-first layouts — the template is sized for a 1200 px desktop reading surface and does not collapse below ~960 px.
|
||||
|
||||
## Bringing this template into a project
|
||||
|
||||
```
|
||||
od/
|
||||
└── .live-artifacts/<artifact-id>/
|
||||
├── template.html ← copy from this folder
|
||||
├── data.json ← copy then refresh from your real source
|
||||
├── artifact.json ← copy then strip daemon-owned fields before sending to /tools live-artifacts create
|
||||
└── provenance.json ← rewrite with real source descriptors before publishing
|
||||
```
|
||||
|
||||
The `live-artifact` skill (see `skills/live-artifact/SKILL.md`) is the recommended on-ramp — point it at this folder and ask the agent to wire in your data source.
|
||||
|
||||
## Source attribution
|
||||
|
||||
Original visual reference: a 1200 × 960 OTD operations dashboard image extracted into `DESIGN.md` via the open-design 9-section spec. Sample account names, month, currency, and percentages are fictional and intentionally illustrative; do not treat them as ground truth.
|
||||
27
templates/live-artifacts/otd-operations-brief/artifact.json
Normal file
27
templates/live-artifacts/otd-operations-brief/artifact.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "live_otd_operations_brief_sample",
|
||||
"projectId": "project_fixture",
|
||||
"title": "On-Time Delivery Dashboard",
|
||||
"slug": "on-time-delivery-dashboard",
|
||||
"status": "active",
|
||||
"pinned": false,
|
||||
"preview": {
|
||||
"type": "html",
|
||||
"entry": "index.html"
|
||||
},
|
||||
"refreshStatus": "never",
|
||||
"createdAt": "2026-05-07T00:00:00.000Z",
|
||||
"updatedAt": "2026-05-07T00:00:00.000Z",
|
||||
"document": {
|
||||
"format": "html_template_v1",
|
||||
"templatePath": "template.html",
|
||||
"generatedPreviewPath": "index.html",
|
||||
"dataPath": "data.json",
|
||||
"dataJson": {
|
||||
"page": {
|
||||
"title": "On-Time Delivery Dashboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
templates/live-artifacts/otd-operations-brief/data.json
Normal file
74
templates/live-artifacts/otd-operations-brief/data.json
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"page": {
|
||||
"title": "On-Time Delivery Dashboard",
|
||||
"filters": [
|
||||
{ "label": "Date = August 2021" },
|
||||
{ "label": "Exchange Rate[From Currency] = \"EUR\"" }
|
||||
]
|
||||
},
|
||||
"kpis": [
|
||||
{
|
||||
"label": "OTD % (LINES)",
|
||||
"value": "70.0%",
|
||||
"numberClass": "kpi__number",
|
||||
"caption": "August 2021 · EUR"
|
||||
},
|
||||
{
|
||||
"label": "OTD % (LINES) PY",
|
||||
"value": "73.9%",
|
||||
"numberClass": "kpi__number",
|
||||
"caption": "August 2020 · EUR"
|
||||
},
|
||||
{
|
||||
"label": "YOY Δ (PTS)",
|
||||
"value": "-3.9 pts",
|
||||
"numberClass": "kpi__number kpi__number--negative",
|
||||
"caption": "Current – Prior year"
|
||||
},
|
||||
{
|
||||
"label": "OTD (LINES)",
|
||||
"value": "205 698",
|
||||
"numberClass": "kpi__number",
|
||||
"caption": "+5 457 vs PY (200 241)"
|
||||
}
|
||||
],
|
||||
"byKeyAccount": {
|
||||
"title": "OTD % (LINES) BY KEY ACCOUNT",
|
||||
"rows": [
|
||||
{ "name": "Antarctic Corporation", "barFillPct": 83.9, "priorTickPct": 73.9, "valueLabel": "83.9% (PY 73.9%)" },
|
||||
{ "name": "Government Contract", "barFillPct": 70.8, "priorTickPct": 75.2, "valueLabel": "70.8% (PY 75.2%)" },
|
||||
{ "name": "Pioneering Systems", "barFillPct": 70.2, "priorTickPct": 75.1, "valueLabel": "70.2% (PY 75.1%)" },
|
||||
{ "name": "Black Flag Spaceships", "barFillPct": 69.2, "priorTickPct": 75.2, "valueLabel": "69.2% (PY 75.2%)" },
|
||||
{ "name": "Macrohard Systems", "barFillPct": 69.0, "priorTickPct": 73.7, "valueLabel": "69.0% (PY 73.7%)" },
|
||||
{ "name": "Sigil Interstellar", "barFillPct": 68.7, "priorTickPct": 74.5, "valueLabel": "68.7% (PY 74.5%)" },
|
||||
{ "name": "Private Contract", "barFillPct": 68.5, "priorTickPct": 73.0, "valueLabel": "68.5% (PY 73.0%)" },
|
||||
{ "name": "No Key Account", "barFillPct": 68.3, "priorTickPct": 73.1, "valueLabel": "68.3% (PY 73.1%)" },
|
||||
{ "name": "Aminu Kano Navy", "barFillPct": 68.2, "priorTickPct": 74.1, "valueLabel": "68.2% (PY 74.1%)" },
|
||||
{ "name": "Andromeda Shipyards", "barFillPct": 67.5, "priorTickPct": 74.0, "valueLabel": "67.5% (PY 74.0%)" },
|
||||
{ "name": "Miller Space", "barFillPct": 67.3, "priorTickPct": 72.3, "valueLabel": "67.3% (PY 72.3%)" },
|
||||
{ "name": "Wel Walla Ship Repair", "barFillPct": 67.0, "priorTickPct": 72.6, "valueLabel": "67.0% (PY 72.6%)" },
|
||||
{ "name": "Elysium Aerospace", "barFillPct": 66.8, "priorTickPct": 73.0, "valueLabel": "66.8% (PY 73.0%)" },
|
||||
{ "name": "Supernova Spaceparts", "barFillPct": 66.1, "priorTickPct": 72.9, "valueLabel": "66.1% (PY 72.9%)" }
|
||||
],
|
||||
"legend": {
|
||||
"bar": "OTD % (Lines) — Aug 2021",
|
||||
"tick": "Prior year (Aug 2020)",
|
||||
"tierLow": "0-60%",
|
||||
"tierMid": "60-80%",
|
||||
"tierHigh": "80-100%"
|
||||
}
|
||||
},
|
||||
"lowestAccounts": {
|
||||
"title": "LOWEST OTD % ACCOUNTS (≥10 LINES)",
|
||||
"rows": [
|
||||
{ "rank": "1", "name": "Błażej Kamieniarz", "otdLabel": "35.9%", "barFillPct": 59.83, "lines": "14" },
|
||||
{ "rank": "2", "name": "Danielle Bailly", "otdLabel": "40.7%", "barFillPct": 67.83, "lines": "11" },
|
||||
{ "rank": "3", "name": "The Ruby Black Hole", "otdLabel": "43.5%", "barFillPct": 72.50, "lines": "10" },
|
||||
{ "rank": "4", "name": "Hagalbar's Carriers", "otdLabel": "44.4%", "barFillPct": 74.00, "lines": "12" },
|
||||
{ "rank": "5", "name": "Rosalinda's Reparations","otdLabel": "44.8%", "barFillPct": 74.67, "lines": "13" },
|
||||
{ "rank": "6", "name": "Black Exports", "otdLabel": "45.2%", "barFillPct": 75.33, "lines": "14" },
|
||||
{ "rank": "7", "name": "The Emerald Prospector", "otdLabel": "46.5%", "barFillPct": 77.50, "lines": "20" },
|
||||
{ "rank": "8", "name": "Kelly's Planets", "otdLabel": "47.6%", "barFillPct": 79.33, "lines": "10" }
|
||||
]
|
||||
}
|
||||
}
|
||||
636
templates/live-artifacts/otd-operations-brief/index.html
Normal file
636
templates/live-artifacts/otd-operations-brief/index.html
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1240" />
|
||||
<title>On-Time Delivery Dashboard</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
/* ===========================================================
|
||||
Mono Crimson Operations Brief — design tokens.
|
||||
See ./DESIGN.md for the full specification. CSS custom-property
|
||||
names mirror the DESIGN.md frontmatter token names so any
|
||||
downstream styling can rely on the same grammar.
|
||||
=========================================================== */
|
||||
:root {
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-muted: #f4f5f7;
|
||||
--color-canvas: #fafafb;
|
||||
--color-border: #e6e7eb;
|
||||
--color-hairline: #eeeff2;
|
||||
--color-text-primary: #0b1220;
|
||||
--color-text-secondary: #4b5563;
|
||||
--color-text-tertiary: #6b7280;
|
||||
--color-text-quiet: #9ca3af;
|
||||
--color-accent-crimson: #c2272d;
|
||||
--color-data-ink: #0f1b2e;
|
||||
--color-data-track: #e6e7eb;
|
||||
--color-tier-low: #f1f2f4;
|
||||
--color-tier-mid: #dddfe3;
|
||||
--color-tier-high: #c9cbd0;
|
||||
--color-pill-bg: #f1f2f4;
|
||||
--color-pill-fg: #4b5563;
|
||||
|
||||
--rounded-xs: 3px;
|
||||
--rounded-sm: 4px;
|
||||
--rounded-md: 6px;
|
||||
|
||||
--shadow-card: 0 1px 0 rgba(11, 18, 32, 0.02);
|
||||
|
||||
--font-sans: "Inter", "IBM Plex Sans", system-ui, sans-serif;
|
||||
--font-mono: "JetBrains Mono", "IBM Plex Mono", "SFMono-Regular",
|
||||
monospace;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-primary);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
.shell {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 22px 24px 28px;
|
||||
}
|
||||
|
||||
/* Header ---------------------------------------------------- */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.header__title {
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.005em;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.header__filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.pill {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
color: var(--color-pill-fg);
|
||||
background: var(--color-pill-bg);
|
||||
border: 1px solid var(--color-hairline);
|
||||
border-radius: var(--rounded-sm);
|
||||
padding: 3px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* KPI strip ------------------------------------------------- */
|
||||
.kpis {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.kpi {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--rounded-md);
|
||||
padding: 14px 16px;
|
||||
box-shadow: var(--shadow-card);
|
||||
min-height: 102px;
|
||||
}
|
||||
.kpi__label {
|
||||
font-size: 10.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-secondary);
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.kpi__number {
|
||||
font-size: 34px;
|
||||
font-weight: 700;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.018em;
|
||||
color: var(--color-text-primary);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.kpi__number--negative {
|
||||
color: var(--color-accent-crimson);
|
||||
}
|
||||
.kpi__caption {
|
||||
margin-top: 6px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-tertiary);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
/* Body grid ------------------------------------------------- */
|
||||
.body {
|
||||
display: grid;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--rounded-md);
|
||||
padding: 16px 18px 14px;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.card__title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Bar chart ------------------------------------------------- */
|
||||
.bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 4px 0 12px;
|
||||
}
|
||||
.bar {
|
||||
display: grid;
|
||||
grid-template-columns: 150px 1fr 130px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.bar__label {
|
||||
font-size: 12.5px;
|
||||
font-weight: 400;
|
||||
color: var(--color-text-secondary);
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.bar__track {
|
||||
position: relative;
|
||||
height: 12px;
|
||||
background: var(--color-data-track);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.bar__fill {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: var(--color-data-ink);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.bar__tick {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
bottom: -2px;
|
||||
width: 2px;
|
||||
background: var(--color-accent-crimson);
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
.bar__value {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-quiet);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
/* Legend ---------------------------------------------------- */
|
||||
.legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--color-hairline);
|
||||
font-size: 11.5px;
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
.legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.legend__sw-bar {
|
||||
width: 18px;
|
||||
height: 10px;
|
||||
background: var(--color-data-ink);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.legend__sw-tick {
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
background: var(--color-accent-crimson);
|
||||
}
|
||||
.legend__sw-tier {
|
||||
width: 22px;
|
||||
height: 10px;
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.legend__sw-tier--low {
|
||||
background: var(--color-tier-low);
|
||||
}
|
||||
.legend__sw-tier--mid {
|
||||
background: var(--color-tier-mid);
|
||||
}
|
||||
.legend__sw-tier--high {
|
||||
background: var(--color-tier-high);
|
||||
}
|
||||
|
||||
/* Lowest table ---------------------------------------------- */
|
||||
.table {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr 130px 60px;
|
||||
column-gap: 12px;
|
||||
}
|
||||
.table__head > div {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-quiet);
|
||||
padding: 0 0 8px;
|
||||
border-bottom: 1px solid var(--color-hairline);
|
||||
}
|
||||
.table__head .col-otdl {
|
||||
text-align: right;
|
||||
}
|
||||
.table__head,
|
||||
.table__row {
|
||||
display: contents;
|
||||
}
|
||||
.table__row > div {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--color-hairline);
|
||||
align-self: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.table__rank {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-quiet);
|
||||
justify-content: flex-start;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.table__name {
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.25;
|
||||
word-break: normal;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 100%;
|
||||
}
|
||||
.table__bar-cell {
|
||||
position: relative;
|
||||
}
|
||||
.table__bar-track {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 18px;
|
||||
background: var(--color-surface-muted);
|
||||
border-radius: var(--rounded-xs);
|
||||
overflow: hidden;
|
||||
}
|
||||
.table__bar-fill {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: var(--color-accent-crimson);
|
||||
border-radius: var(--rounded-xs);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 6px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.table__lines {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-quiet);
|
||||
justify-content: flex-end;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="shell">
|
||||
<!-- ============================================================
|
||||
NOTE on bindings.
|
||||
Open Design's html_template_v1 renderer (apps/daemon/src/
|
||||
live-artifacts/render.ts) does scalar-only
|
||||
interpolation: bindings must start with `data`, must resolve
|
||||
to a scalar, and there is no array repeat directive. The
|
||||
template is therefore fully unrolled — KPI 0..3, bar 0..13,
|
||||
lowest 0..7. Refresh callers should keep the same shape and
|
||||
cardinality in data.json.
|
||||
============================================================ -->
|
||||
|
||||
<header class="header">
|
||||
<h1 class="header__title">On-Time Delivery Dashboard</h1>
|
||||
<div class="header__filters">
|
||||
<span class="pill">Date = August 2021</span>
|
||||
<span class="pill">Exchange Rate[From Currency] = "EUR"</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- KPI strip ----------------------------------------------------- -->
|
||||
<section class="kpis" aria-label="Key performance indicators">
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">OTD % (LINES)</p>
|
||||
<div class="kpi__number">70.0%</div>
|
||||
<p class="kpi__caption">August 2021 · EUR</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">OTD % (LINES) PY</p>
|
||||
<div class="kpi__number">73.9%</div>
|
||||
<p class="kpi__caption">August 2020 · EUR</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">YOY Δ (PTS)</p>
|
||||
<div class="kpi__number kpi__number--negative">-3.9 pts</div>
|
||||
<p class="kpi__caption">Current – Prior year</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">OTD (LINES)</p>
|
||||
<div class="kpi__number">205 698</div>
|
||||
<p class="kpi__caption">+5 457 vs PY (200 241)</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Body --------------------------------------------------------- -->
|
||||
<section class="body">
|
||||
<!-- Bar-by-account card ---------------------------------- -->
|
||||
<article class="card">
|
||||
<h2 class="card__title">OTD % (LINES) BY KEY ACCOUNT</h2>
|
||||
<div class="bars">
|
||||
<div class="bar">
|
||||
<div class="bar__label">Antarctic Corporation</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 83.9%"></div>
|
||||
<div class="bar__tick" style="left: 73.9%"></div>
|
||||
</div>
|
||||
<div class="bar__value">83.9% (PY 73.9%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Government Contract</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 70.8%"></div>
|
||||
<div class="bar__tick" style="left: 75.2%"></div>
|
||||
</div>
|
||||
<div class="bar__value">70.8% (PY 75.2%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Pioneering Systems</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 70.2%"></div>
|
||||
<div class="bar__tick" style="left: 75.1%"></div>
|
||||
</div>
|
||||
<div class="bar__value">70.2% (PY 75.1%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Black Flag Spaceships</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 69.2%"></div>
|
||||
<div class="bar__tick" style="left: 75.2%"></div>
|
||||
</div>
|
||||
<div class="bar__value">69.2% (PY 75.2%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Macrohard Systems</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 69.0%"></div>
|
||||
<div class="bar__tick" style="left: 73.7%"></div>
|
||||
</div>
|
||||
<div class="bar__value">69.0% (PY 73.7%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Sigil Interstellar</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 68.7%"></div>
|
||||
<div class="bar__tick" style="left: 74.5%"></div>
|
||||
</div>
|
||||
<div class="bar__value">68.7% (PY 74.5%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Private Contract</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 68.5%"></div>
|
||||
<div class="bar__tick" style="left: 73.0%"></div>
|
||||
</div>
|
||||
<div class="bar__value">68.5% (PY 73.0%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">No Key Account</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 68.3%"></div>
|
||||
<div class="bar__tick" style="left: 73.1%"></div>
|
||||
</div>
|
||||
<div class="bar__value">68.3% (PY 73.1%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Aminu Kano Navy</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 68.2%"></div>
|
||||
<div class="bar__tick" style="left: 74.1%"></div>
|
||||
</div>
|
||||
<div class="bar__value">68.2% (PY 74.1%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Andromeda Shipyards</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 67.5%"></div>
|
||||
<div class="bar__tick" style="left: 74.0%"></div>
|
||||
</div>
|
||||
<div class="bar__value">67.5% (PY 74.0%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Miller Space</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 67.3%"></div>
|
||||
<div class="bar__tick" style="left: 72.3%"></div>
|
||||
</div>
|
||||
<div class="bar__value">67.3% (PY 72.3%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Wel Walla Ship Repair</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 67.0%"></div>
|
||||
<div class="bar__tick" style="left: 72.6%"></div>
|
||||
</div>
|
||||
<div class="bar__value">67.0% (PY 72.6%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Elysium Aerospace</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 66.8%"></div>
|
||||
<div class="bar__tick" style="left: 73.0%"></div>
|
||||
</div>
|
||||
<div class="bar__value">66.8% (PY 73.0%)</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">Supernova Spaceparts</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: 66.1%"></div>
|
||||
<div class="bar__tick" style="left: 72.9%"></div>
|
||||
</div>
|
||||
<div class="bar__value">66.1% (PY 72.9%)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="legend" aria-label="Chart legend">
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-bar"></span>
|
||||
<span>OTD % (Lines) — Aug 2021</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tick"></span>
|
||||
<span>Prior year (Aug 2020)</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--low"></span>
|
||||
<span>0-60%</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--mid"></span>
|
||||
<span>60-80%</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--high"></span>
|
||||
<span>80-100%</span>
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Lowest-OTD table -------------------------------------- -->
|
||||
<article class="card">
|
||||
<h2 class="card__title">LOWEST OTD % ACCOUNTS (≥10 LINES)</h2>
|
||||
<div class="table">
|
||||
<div class="table__head">
|
||||
<div>#</div>
|
||||
<div>ACCOUNT NAME</div>
|
||||
<div>OTD %</div>
|
||||
<div class="col-otdl">OTD (LINES)</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">1</div>
|
||||
<div class="table__name">Błażej Kamieniarz</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 59.83%">35.9%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">14</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">2</div>
|
||||
<div class="table__name">Danielle Bailly</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 67.83%">40.7%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">11</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">3</div>
|
||||
<div class="table__name">The Ruby Black Hole</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 72.5%">43.5%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">10</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">4</div>
|
||||
<div class="table__name">Hagalbar's Carriers</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 74.0%">44.4%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">12</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">5</div>
|
||||
<div class="table__name">Rosalinda's Reparations</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 74.67%">44.8%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">13</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">6</div>
|
||||
<div class="table__name">Black Exports</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 75.33%">45.2%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">14</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">7</div>
|
||||
<div class="table__name">The Emerald Prospector</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 77.5%">46.5%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">20</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">8</div>
|
||||
<div class="table__name">Kelly's Planets</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: 79.33%">47.6%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">10</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
BIN
templates/live-artifacts/otd-operations-brief/preview.png
Normal file
BIN
templates/live-artifacts/otd-operations-brief/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"generatedAt": "2026-05-07T00:00:00.000Z",
|
||||
"generatedBy": "agent",
|
||||
"notes": "Template fixture for the Mono Crimson Operations Brief style. data.json holds illustrative figures only — they are not derived from any real account, ERP, or carrier data. Replace with real values before treating refreshes as ground truth.",
|
||||
"sources": [
|
||||
{
|
||||
"label": "Template author input",
|
||||
"type": "user_input"
|
||||
}
|
||||
]
|
||||
}
|
||||
636
templates/live-artifacts/otd-operations-brief/template.html
Normal file
636
templates/live-artifacts/otd-operations-brief/template.html
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1240" />
|
||||
<title>{{data.page.title}}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
/* ===========================================================
|
||||
Mono Crimson Operations Brief — design tokens.
|
||||
See ./DESIGN.md for the full specification. CSS custom-property
|
||||
names mirror the DESIGN.md frontmatter token names so any
|
||||
downstream styling can rely on the same grammar.
|
||||
=========================================================== */
|
||||
:root {
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-muted: #f4f5f7;
|
||||
--color-canvas: #fafafb;
|
||||
--color-border: #e6e7eb;
|
||||
--color-hairline: #eeeff2;
|
||||
--color-text-primary: #0b1220;
|
||||
--color-text-secondary: #4b5563;
|
||||
--color-text-tertiary: #6b7280;
|
||||
--color-text-quiet: #9ca3af;
|
||||
--color-accent-crimson: #c2272d;
|
||||
--color-data-ink: #0f1b2e;
|
||||
--color-data-track: #e6e7eb;
|
||||
--color-tier-low: #f1f2f4;
|
||||
--color-tier-mid: #dddfe3;
|
||||
--color-tier-high: #c9cbd0;
|
||||
--color-pill-bg: #f1f2f4;
|
||||
--color-pill-fg: #4b5563;
|
||||
|
||||
--rounded-xs: 3px;
|
||||
--rounded-sm: 4px;
|
||||
--rounded-md: 6px;
|
||||
|
||||
--shadow-card: 0 1px 0 rgba(11, 18, 32, 0.02);
|
||||
|
||||
--font-sans: "Inter", "IBM Plex Sans", system-ui, sans-serif;
|
||||
--font-mono: "JetBrains Mono", "IBM Plex Mono", "SFMono-Regular",
|
||||
monospace;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-primary);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
.shell {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 22px 24px 28px;
|
||||
}
|
||||
|
||||
/* Header ---------------------------------------------------- */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.header__title {
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.005em;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.header__filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.pill {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
color: var(--color-pill-fg);
|
||||
background: var(--color-pill-bg);
|
||||
border: 1px solid var(--color-hairline);
|
||||
border-radius: var(--rounded-sm);
|
||||
padding: 3px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* KPI strip ------------------------------------------------- */
|
||||
.kpis {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.kpi {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--rounded-md);
|
||||
padding: 14px 16px;
|
||||
box-shadow: var(--shadow-card);
|
||||
min-height: 102px;
|
||||
}
|
||||
.kpi__label {
|
||||
font-size: 10.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-secondary);
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
.kpi__number {
|
||||
font-size: 34px;
|
||||
font-weight: 700;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.018em;
|
||||
color: var(--color-text-primary);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.kpi__number--negative {
|
||||
color: var(--color-accent-crimson);
|
||||
}
|
||||
.kpi__caption {
|
||||
margin-top: 6px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-tertiary);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
/* Body grid ------------------------------------------------- */
|
||||
.body {
|
||||
display: grid;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--rounded-md);
|
||||
padding: 16px 18px 14px;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.card__title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Bar chart ------------------------------------------------- */
|
||||
.bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 4px 0 12px;
|
||||
}
|
||||
.bar {
|
||||
display: grid;
|
||||
grid-template-columns: 150px 1fr 130px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.bar__label {
|
||||
font-size: 12.5px;
|
||||
font-weight: 400;
|
||||
color: var(--color-text-secondary);
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.bar__track {
|
||||
position: relative;
|
||||
height: 12px;
|
||||
background: var(--color-data-track);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.bar__fill {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: var(--color-data-ink);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.bar__tick {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
bottom: -2px;
|
||||
width: 2px;
|
||||
background: var(--color-accent-crimson);
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
.bar__value {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-quiet);
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
/* Legend ---------------------------------------------------- */
|
||||
.legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--color-hairline);
|
||||
font-size: 11.5px;
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
.legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.legend__sw-bar {
|
||||
width: 18px;
|
||||
height: 10px;
|
||||
background: var(--color-data-ink);
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.legend__sw-tick {
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
background: var(--color-accent-crimson);
|
||||
}
|
||||
.legend__sw-tier {
|
||||
width: 22px;
|
||||
height: 10px;
|
||||
border-radius: var(--rounded-xs);
|
||||
}
|
||||
.legend__sw-tier--low {
|
||||
background: var(--color-tier-low);
|
||||
}
|
||||
.legend__sw-tier--mid {
|
||||
background: var(--color-tier-mid);
|
||||
}
|
||||
.legend__sw-tier--high {
|
||||
background: var(--color-tier-high);
|
||||
}
|
||||
|
||||
/* Lowest table ---------------------------------------------- */
|
||||
.table {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr 130px 60px;
|
||||
column-gap: 12px;
|
||||
}
|
||||
.table__head > div {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-quiet);
|
||||
padding: 0 0 8px;
|
||||
border-bottom: 1px solid var(--color-hairline);
|
||||
}
|
||||
.table__head .col-otdl {
|
||||
text-align: right;
|
||||
}
|
||||
.table__head,
|
||||
.table__row {
|
||||
display: contents;
|
||||
}
|
||||
.table__row > div {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--color-hairline);
|
||||
align-self: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.table__rank {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-quiet);
|
||||
justify-content: flex-start;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.table__name {
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
line-height: 1.25;
|
||||
word-break: normal;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 100%;
|
||||
}
|
||||
.table__bar-cell {
|
||||
position: relative;
|
||||
}
|
||||
.table__bar-track {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 18px;
|
||||
background: var(--color-surface-muted);
|
||||
border-radius: var(--rounded-xs);
|
||||
overflow: hidden;
|
||||
}
|
||||
.table__bar-fill {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: var(--color-accent-crimson);
|
||||
border-radius: var(--rounded-xs);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 6px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
.table__lines {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-quiet);
|
||||
justify-content: flex-end;
|
||||
font-feature-settings: "tnum", "lnum";
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="shell">
|
||||
<!-- ============================================================
|
||||
NOTE on bindings.
|
||||
Open Design's html_template_v1 renderer (apps/daemon/src/
|
||||
live-artifacts/render.ts) does scalar-only {{data.X}}
|
||||
interpolation: bindings must start with `data`, must resolve
|
||||
to a scalar, and there is no array repeat directive. The
|
||||
template is therefore fully unrolled — KPI 0..3, bar 0..13,
|
||||
lowest 0..7. Refresh callers should keep the same shape and
|
||||
cardinality in data.json.
|
||||
============================================================ -->
|
||||
|
||||
<header class="header">
|
||||
<h1 class="header__title">{{data.page.title}}</h1>
|
||||
<div class="header__filters">
|
||||
<span class="pill">{{data.page.filters.0.label}}</span>
|
||||
<span class="pill">{{data.page.filters.1.label}}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- KPI strip ----------------------------------------------------- -->
|
||||
<section class="kpis" aria-label="Key performance indicators">
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">{{data.kpis.0.label}}</p>
|
||||
<div class="{{data.kpis.0.numberClass}}">{{data.kpis.0.value}}</div>
|
||||
<p class="kpi__caption">{{data.kpis.0.caption}}</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">{{data.kpis.1.label}}</p>
|
||||
<div class="{{data.kpis.1.numberClass}}">{{data.kpis.1.value}}</div>
|
||||
<p class="kpi__caption">{{data.kpis.1.caption}}</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">{{data.kpis.2.label}}</p>
|
||||
<div class="{{data.kpis.2.numberClass}}">{{data.kpis.2.value}}</div>
|
||||
<p class="kpi__caption">{{data.kpis.2.caption}}</p>
|
||||
</article>
|
||||
<article class="kpi">
|
||||
<p class="kpi__label">{{data.kpis.3.label}}</p>
|
||||
<div class="{{data.kpis.3.numberClass}}">{{data.kpis.3.value}}</div>
|
||||
<p class="kpi__caption">{{data.kpis.3.caption}}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Body --------------------------------------------------------- -->
|
||||
<section class="body">
|
||||
<!-- Bar-by-account card ---------------------------------- -->
|
||||
<article class="card">
|
||||
<h2 class="card__title">{{data.byKeyAccount.title}}</h2>
|
||||
<div class="bars">
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.0.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.0.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.0.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.0.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.1.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.1.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.1.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.1.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.2.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.2.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.2.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.2.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.3.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.3.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.3.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.3.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.4.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.4.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.4.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.4.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.5.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.5.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.5.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.5.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.6.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.6.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.6.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.6.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.7.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.7.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.7.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.7.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.8.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.8.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.8.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.8.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.9.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.9.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.9.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.9.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.10.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.10.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.10.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.10.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.11.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.11.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.11.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.11.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.12.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.12.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.12.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.12.valueLabel}}</div>
|
||||
</div>
|
||||
<div class="bar">
|
||||
<div class="bar__label">{{data.byKeyAccount.rows.13.name}}</div>
|
||||
<div class="bar__track">
|
||||
<div class="bar__fill" style="width: {{data.byKeyAccount.rows.13.barFillPct}}%"></div>
|
||||
<div class="bar__tick" style="left: {{data.byKeyAccount.rows.13.priorTickPct}}%"></div>
|
||||
</div>
|
||||
<div class="bar__value">{{data.byKeyAccount.rows.13.valueLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="legend" aria-label="Chart legend">
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-bar"></span>
|
||||
<span>{{data.byKeyAccount.legend.bar}}</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tick"></span>
|
||||
<span>{{data.byKeyAccount.legend.tick}}</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--low"></span>
|
||||
<span>{{data.byKeyAccount.legend.tierLow}}</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--mid"></span>
|
||||
<span>{{data.byKeyAccount.legend.tierMid}}</span>
|
||||
</span>
|
||||
<span class="legend__item">
|
||||
<span class="legend__sw-tier legend__sw-tier--high"></span>
|
||||
<span>{{data.byKeyAccount.legend.tierHigh}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Lowest-OTD table -------------------------------------- -->
|
||||
<article class="card">
|
||||
<h2 class="card__title">{{data.lowestAccounts.title}}</h2>
|
||||
<div class="table">
|
||||
<div class="table__head">
|
||||
<div>#</div>
|
||||
<div>ACCOUNT NAME</div>
|
||||
<div>OTD %</div>
|
||||
<div class="col-otdl">OTD (LINES)</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.0.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.0.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.0.barFillPct}}%">{{data.lowestAccounts.rows.0.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.0.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.1.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.1.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.1.barFillPct}}%">{{data.lowestAccounts.rows.1.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.1.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.2.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.2.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.2.barFillPct}}%">{{data.lowestAccounts.rows.2.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.2.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.3.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.3.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.3.barFillPct}}%">{{data.lowestAccounts.rows.3.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.3.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.4.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.4.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.4.barFillPct}}%">{{data.lowestAccounts.rows.4.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.4.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.5.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.5.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.5.barFillPct}}%">{{data.lowestAccounts.rows.5.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.5.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.6.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.6.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.6.barFillPct}}%">{{data.lowestAccounts.rows.6.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.6.lines}}</div>
|
||||
</div>
|
||||
<div class="table__row">
|
||||
<div class="table__rank">{{data.lowestAccounts.rows.7.rank}}</div>
|
||||
<div class="table__name">{{data.lowestAccounts.rows.7.name}}</div>
|
||||
<div class="table__bar-cell">
|
||||
<div class="table__bar-track">
|
||||
<div class="table__bar-fill" style="width: {{data.lowestAccounts.rows.7.barFillPct}}%">{{data.lowestAccounts.rows.7.otdLabel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table__lines">{{data.lowestAccounts.rows.7.lines}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue