Brand and design a dashboard
Retint the runtime to your brand colors, embed your real logo and font, and elevate the chrome without breaking scheduled refresh.
A dashboard published straight from the authoring loop is correct but plain: default blue on white, every figure the same weight, a text-only header. This page turns that into something that looks like your company built it on purpose.
Ask your AI to use the dashies-design skill, which is bundled with the plugin,
and it follows exactly this procedure.
What you control, and what the runtime draws
A dashboard is one self-contained HTML file. Knowing who owns what is the whole job.
| Part | Who controls it |
|---|---|
| Header, cards, layout grid, section titles, backgrounds, spacing, typography | You. Plain HTML and your own <style>. The runtime only writes numbers into the slots you mark. |
| Charts, tables, filter controls, the default figure card | The runtime draws them, but every colour and font is a CSS custom property you can redefine. |
So the procedure is: retint the runtime to your brand, then build good chrome around it. Doing only the second is the single most recognisable "default template with a custom title" tell.
1. Decide the brand system first
Four decisions, written down before any CSS:
- Accent. One brand colour, used sparingly.
- Neutral temperature. Warm grey, cool slate, or near-black. This carries most of the design. Match it to the accent: a warm accent wants warm neutrals, a cool one wants slate. A mismatched temperature is a quiet ugliness.
- Type. Identify the real typeface, then decide whether you can embed it.
- Light or dark, and the mark.
2. Retint the runtime
Drop this into your <style> and swap the values. The example is an
indigo and near-black feel.
<style>
:root {
/* accent: chart series, line/bar/area, dots, focus */
--drt-blue: #635bff !important;
--drt-blue-700: #4b45d6 !important;
--drt-blue-soft: #f0efff !important;
/* text ramp: keep the greys descending and legible */
--drt-ink: #0a1636 !important;
--drt-muted: #45507a !important;
--drt-subtle: #6b769c !important;
--drt-faint: #9aa2be !important;
/* surfaces and hairlines */
--drt-surface: #ffffff !important;
--drt-sunken: #f7f8fc !important;
--drt-line: #e7e9f3 !important;
--drt-line-strong: #ced2e6 !important;
--drt-grid: #eef0f8 !important;
/* type */
--drt-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif !important;
--drt-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace !important;
}
/* the two colours that are not tokenized */
.drt-area { fill: color-mix(in srgb, var(--drt-blue) 12%, transparent) !important; }
.drt-select:focus-visible {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--drt-blue) 22%, transparent) !important;
}
</style>
The !important is load-bearing
The runtime injects its own <style> last, at boot, so a plain
:root { --drt-blue: ... } in your head loses the cascade to the runtime's
own :root. Marking your declarations !important wins regardless of order.
Two colours are hardcoded rather than tokenized, the area-chart fill and the select focus ring, which is why the two class overrides are there. That is the entire surface; there is nothing else to chase.
Reuse the same variables in your own chrome CSS, so the header and the charts
share one palette by construction. Tint neutrals a few percent toward the accent
rather than using pure greys: a canvas of
color-mix(in srgb, var(--drt-blue) 4%, white) reads as designed, #f5f5f5
reads as template.
3. Embed the real logo and font
The dashboard is served under a strict sandbox with no network access, so
external fonts, CDN scripts, and remote images do not load. The full boundary is
in
what a viewer's browser can and cannot do. Inline SVG and
data: URIs do, which means you can embed the real assets rather than
approximate them.
The logo: paste the company's official SVG straight into the header markup
and size it to roughly 20 to 36 pixels. A single-colour mark takes
fill: currentColor or the brand colour.
The font: embed it as base64 so it needs no network.
<style>
@font-face {
font-family: 'Brand';
font-weight: 400;
src: url(data:font/woff2;base64,d09GMgABAAAAAA...) format('woff2');
}
:root { --drt-sans: 'Brand', ui-sans-serif, system-ui, sans-serif !important; }
</style>
One @font-face per weight, and keep it to two or three weights: a Latin subset
is roughly 20 to 40 KB of base64 each. Setting the family on --drt-sans makes
the chrome and the runtime widgets share it.
Only embed a font you are licensed to embed
Open-licence fonts, Google Fonts, or a file you have the rights to. Most flagship brand typefaces are proprietary and not freely obtainable, so use the closest free match instead.
4. Elevate the chrome
Four moves, in order. Each is small and changes a lot.
- One thing is biggest. The default makes every figure identical, so the eye has nowhere to land. Pick the metric that matters most and make it dominant, roughly three times the size of its label, with a short supporting line. Demote the rest to a quieter row.
- Give the header an identity. A mark, one brand moment such as a thin accent rule or a subtle tinted band, and a tidy meta line with the title, a real subtitle, and the freshness stamp.
- Commit to one surface treatment. Either flat cards with a hairline border on a lightly tinted canvas, or lifted cards with one soft shadow and no border. Mixing borders and shadows at random is the classic tell. One radius value everywhere.
- Use a real type scale. Big jumps and weight contrast, not 14/16/18. Numbers in a monospace with tabular figures so columns align. Space on a consistent 8-pixel rhythm.
Spend the accent on the primary chart series, the hero figure, links, focus rings, and the header mark. Not on every border, label, and card background.
Two rules that keep refresh working
Do not touch the data island or replace the charts
- Never edit, wrap, or move
<script id="dashies-data">. The refresh cron rewrites that element by id, which is why your design survives every refresh. See The data island. Styling belongs in<head>and in your own markup. - Keep charts runtime-rendered. Retint them; do not replace them with hand-drawn SVG. A static chart cannot be updated by the refresh, so the page would show a stale picture beside fresh numbers.
- Never script-replace
<body>. That clobbers the badge injection.
Styling never changes the datasets, the compiled island, or how the dashboard refreshes, so you can restyle a live dashboard freely as long as you respect those three.
The quick path: the spec's theme field
For a light touch without writing CSS, the spec carries a theme block with
accent, font, density, and mode. It is the right tool for making a
dashboard the correct colour. It is not enough to make one look designed, which
is what the rest of this page is for.
What to avoid
These are the tells that make a dashboard read as generic:
- Shipping the runtime's default blue on the charts.
- Every figure the same size.
- A text-only header with no mark.
- Borders and shadows mixed at random across cards.
- Rainbow or multi-colour categorical bars. Dashies charts are single-accent by design; keep them so.
- Gradients everywhere, glassmorphism, neon glow, heavy drop shadows, radii above 20 pixels, emoji in the header, decorative icons carrying no meaning.
- Low-contrast grey-on-grey text.
- A coloured left-border stripe on every card "for variety".
- Card inside card inside pill. Two levels of surface nesting is the limit.
- Filler copy such as "Welcome" or "Get started".
- Deltas, trend arrows, or metrics the data does not actually contain.
Check it worked
- Open the published URL and look at a chart. If the series is still the
default blue, the retint did not land, which almost always means a missing
!important. - Open the browser console. There should be no content-security-policy violations. One means something is still trying to reach the network, usually a font or image URL that needs embedding.
- Trigger a refresh, then reload. The numbers change and your styling is untouched. If the styling disappeared, something was written into the data island.
- Check contrast. Text on your accent should clear WCAG AA, and
--drt-inkshould be genuinely dark.