Skip to content

Commit 2ba415d

Browse files
committed
Document Meshpoint design system
Adds docs/DESIGN-SYSTEM.md as the canonical visual style guide for frontend/: brand identity, color tokens, semantic palette, typography, layout primitives, BEM naming convention, file organization, a tech-debt note on the legacy token namespace in messaging.css and radio.css, and an anti-patterns list. CONTRIBUTING.md gains a one-line pointer in the Style section so the doc is discoverable from the file every contributor reads.
1 parent 3ef96c0 commit 2ba415d

2 files changed

Lines changed: 385 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ Rules:
116116

117117
## Style
118118

119+
- PRs that touch `frontend/` MUST conform to [docs/DESIGN-SYSTEM.md](docs/DESIGN-SYSTEM.md). Read it before opening the PR.
119120
- Keep changes small
120121
- Avoid unrelated cleanup
121122
- Prefer readability

docs/DESIGN-SYSTEM.md

Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
1+
# Design System
2+
3+
This is the canonical visual style guide for the Meshpoint local
4+
dashboard (`frontend/`). Read it before opening any pull request that
5+
touches `frontend/`. Every PR that adds or changes UI code is
6+
reviewed against this document.
7+
8+
Scope of this guide is the Meshpoint local dashboard only. The
9+
Meshradar cloud dashboard has its own (separate) design system.
10+
11+
---
12+
13+
## Brand Identity
14+
15+
UI strings, page titles, and any user-visible prose must follow the
16+
Meshpoint brand rules:
17+
18+
- The device is **Meshpoint**: one word, capital M. Never
19+
"Mesh Point", "MeshPoint", or "mesh point".
20+
- The platform is **Meshradar**: one word, capital M. Never
21+
"Mesh Radar", "MeshRadar", or "mesh radar".
22+
- Plurals are **Meshpoints** and **Meshradars**.
23+
- Code identifiers (CSS class names, JS variables, config keys) keep
24+
existing project conventions (`top-bar`, `meshpoint`, `mesh_point`).
25+
The branding rule applies to prose, docs, UI strings, and
26+
user-facing text only.
27+
28+
Writing style for UI copy:
29+
30+
- Never use em dashes (``). Use a colon (`:`) or rewrite the sentence.
31+
- Never use en dashes (``) as punctuation. Use a colon or rewrite.
32+
- Hyphens (`-`) are fine for compound words.
33+
34+
Page `<title>` rule:
35+
36+
- The `<title>` should read `Meshpoint Dashboard` (or a similarly
37+
Meshpoint-branded short string). It must not introduce a new
38+
tagline or marketing line.
39+
40+
> **Known bug, separate fix.** The current
41+
> `frontend/index.html` `<title>` reads `Mesh Radar - Mesh Point`,
42+
> which violates the brand rule. A polish PR will correct this.
43+
> Do not bundle that change with unrelated work.
44+
45+
---
46+
47+
## Color Tokens
48+
49+
The canonical token palette lives in `frontend/css/dashboard.css`
50+
in the `:root` block. New CSS in `frontend/` MUST use these tokens
51+
and MUST NOT redeclare or rename them.
52+
53+
### Backgrounds
54+
55+
| Token | Value | Usage |
56+
|---|---|---|
57+
| `--bg-primary` | `#0a0e17` | Page background, deep base |
58+
| `--bg-secondary` | `#111827` | Top bar, table headers, packet detail rows |
59+
| `--bg-card` | `#162033` | Stat cards, drawer header, control surfaces |
60+
| `--bg-glass` | `rgba(22, 32, 51, 0.7)` | Panel surfaces (with `backdrop-filter: blur(12px)`) |
61+
62+
### Borders
63+
64+
| Token | Value | Usage |
65+
|---|---|---|
66+
| `--border` | `#233049` | All static borders |
67+
| `--border-glow` | `rgba(6, 182, 212, 0.2)` | Hover borders on `.panel` |
68+
69+
### Text
70+
71+
| Token | Value | Usage |
72+
|---|---|---|
73+
| `--text-primary` | `#e2e8f0` | Body copy, headings, strong values |
74+
| `--text-secondary` | `#94a3b8` | Secondary copy, table cells |
75+
| `--text-muted` | `#64748b` | Labels, hints, captions, meta |
76+
77+
### Accents
78+
79+
| Token | Value | Usage |
80+
|---|---|---|
81+
| `--accent-cyan` | `#06b6d4` | Primary accent, hover, focus, mono numerics |
82+
| `--accent-green` | `#00e5a0` | Success, online, active device, relay stats |
83+
| `--accent-blue` | `#3b82f6` | Meshtastic protocol indicator |
84+
| `--accent-purple` | `#a855f7` | Meshcore protocol indicator |
85+
| `--accent-amber` | `#f59e0b` | Warnings, position packets, update badge |
86+
| `--accent-red` | `#ef4444` | Errors, encrypted packets, disconnected status |
87+
88+
### Effects and shape
89+
90+
| Token | Value | Usage |
91+
|---|---|---|
92+
| `--glow-cyan` | `0 0 8px rgba(6, 182, 212, 0.3)` | Cyan glow halo |
93+
| `--radius` | `8px` | Default corner radius for cards, panels, inputs |
94+
95+
---
96+
97+
## Semantic Color Mapping
98+
99+
These mappings already exist across the dashboard. New code that
100+
expresses the same concepts MUST reuse them rather than invent new
101+
mappings.
102+
103+
### Protocol colors
104+
105+
| Protocol | Token |
106+
|---|---|
107+
| Meshtastic | `--accent-blue` |
108+
| Meshcore | `--accent-purple` |
109+
110+
### Packet type colors
111+
112+
Lifted from `frontend/css/dashboard.css` (`.packet-table td.type-*`).
113+
114+
| Packet type | Token |
115+
|---|---|
116+
| `text` | `--accent-green` |
117+
| `position` | `--accent-amber` |
118+
| `telemetry` | `--accent-cyan` |
119+
| `nodeinfo` | `--accent-purple` |
120+
| `encrypted` | `--accent-red` |
121+
| `routing` | `--text-muted` |
122+
| `traceroute` | `--accent-blue` |
123+
| `neighborinfo` | `--accent-blue` |
124+
125+
### RSSI buckets
126+
127+
Used in the packet table cells.
128+
129+
| Bucket | Token |
130+
|---|---|
131+
| `rssi-good` | `--accent-green` |
132+
| `rssi-mid` | `--accent-amber` |
133+
| `rssi-bad` | `--accent-red` |
134+
135+
### Signal-quality buckets
136+
137+
Used in node card chips and messaging signal indicators. These are
138+
currently hardcoded hex (legacy), but the SEMANTIC mapping is
139+
canonical and MUST be preserved on any new component that displays
140+
signal quality.
141+
142+
| Bucket | Hex | Notes |
143+
|---|---|---|
144+
| `excellent` | `#00e676` | Brighter than `--accent-green`, kept for visual contrast in chip context |
145+
| `good` | `#4ecdc4` | Legacy teal, slightly different from `--accent-cyan` |
146+
| `fair` | `#ffd54f` | Legacy yellow |
147+
| `poor` | `#ff5252` | Legacy red |
148+
149+
A future cleanup PR will migrate these to tokens. Do not introduce
150+
NEW hardcoded colors; reuse one of these four if you need a quality
151+
bucket on a new component.
152+
153+
---
154+
155+
## Typography
156+
157+
Two font families are loaded from Google Fonts in `frontend/index.html`.
158+
Do not introduce additional font families.
159+
160+
| Token | Family | Usage |
161+
|---|---|---|
162+
| `--font-sans` | `Inter` (with system fallback) | All prose: body, headings, labels, buttons |
163+
| `--font-mono` | `JetBrains Mono` (with `Fira Code` fallback) | Numerics, IDs, hashes, packet data, signal values, table cells |
164+
165+
### Type scale (existing canonical sizes)
166+
167+
| Surface | Size | Weight | Casing |
168+
|---|---|---|---|
169+
| Top-bar `<h1>` | `1.05rem` | 600 | mixed |
170+
| Top-bar status | `0.8rem` mono | 600 (stats), 400 (label) | mixed |
171+
| Tab button | `0.85rem` | 500 | mixed |
172+
| Panel header | `0.7rem` | 600 | UPPERCASE, `0.05em` letter-spacing |
173+
| Stat card label | `0.6rem` | normal | UPPERCASE, `0.05em` letter-spacing |
174+
| Stat card value | `1.1rem` mono | 700 | mixed |
175+
| Packet table header | `0.7rem` mono | 600 | UPPERCASE, `0.04em` letter-spacing |
176+
| Packet table cell | `0.78rem` mono | 400 | mixed |
177+
| Node card name | `0.8rem` | 600 | mixed |
178+
| Node card chip | `0.6rem` mono | 600-700 | mixed (or UPPERCASE for quality) |
179+
180+
When in doubt, match the size and weight of an existing surface that
181+
plays the same role.
182+
183+
---
184+
185+
## Layout Primitives
186+
187+
The dashboard uses one shell pattern and a small set of container
188+
primitives. New work composes these. A change to the shell (for
189+
example, replacing the tab bar with a sidebar) is a design RFC, not
190+
a regular PR.
191+
192+
### Shell
193+
194+
```
195+
<header class="top-bar"> ... </header>
196+
<nav class="tab-bar"> ... </nav>
197+
<div id="tab-X" class="tab-content tab-content--active"> ... </div>
198+
<div id="tab-Y" class="tab-content"> ... </div>
199+
```
200+
201+
The top bar is sticky (`position: sticky; top: 0; z-index: 1000`).
202+
The tab bar is a single-row horizontal strip with one
203+
`tab-bar__btn--active` at a time. Each tab body is a
204+
`.tab-content` div, only the active one is shown.
205+
206+
### `.panel`
207+
208+
The single canonical container for any rectangular content surface.
209+
210+
```
211+
<div class="panel">
212+
<div class="panel__header">Title</div>
213+
<div class="panel__body"> ... </div>
214+
</div>
215+
```
216+
217+
Backed by `--bg-glass` with `backdrop-filter: blur(12px)`,
218+
`--border`, and `--radius`. Hover state lifts the border to
219+
`--border-glow`.
220+
221+
### `.stat-card`
222+
223+
Small horizontal cards in a horizontally-scrollable strip
224+
(`.dashboard__stats`). Each card has a label, a large mono value,
225+
and an optional sub-line. Modifiers (`.stat-card--relay`,
226+
`.stat-card--system`) tint the border and value color to convey
227+
category.
228+
229+
### Drawer (right-side slide-in)
230+
231+
```
232+
<div id="X-backdrop" class="nd-backdrop"></div>
233+
<div id="X-drawer" class="nd-drawer"> ... </div>
234+
```
235+
236+
Used for node detail today. Right-side, ~50% width, with a backdrop
237+
overlay. Reuse this primitive for any future detail-view that does
238+
not deserve its own tab.
239+
240+
---
241+
242+
## Naming Convention
243+
244+
All CSS class names follow **BEM**:
245+
246+
- `block` for the component root: `top-bar`, `panel`, `stat-card`
247+
- `block__element` for a child of the block: `top-bar__brand`,
248+
`panel__header`, `stat-card__value`
249+
- `block--modifier` for a state or variant: `tab-bar__btn--active`,
250+
`stat-card--relay`, `nc-chip--excellent`
251+
- kebab-case throughout (no `camelCase` and no `snake_case`)
252+
253+
Per-component prefixes already in use. New components MUST pick a
254+
prefix that does not collide with these and stick to BEM:
255+
256+
| Prefix | Owner |
257+
|---|---|
258+
| `top-bar`, `tab-bar`, `tab-content` | shell |
259+
| `dashboard`, `panel`, `stat-card` | dashboard surface |
260+
| `packet-` | packet table |
261+
| `nc-` | node cards |
262+
| `nd-` | node drawer |
263+
| `msg-` | messaging |
264+
| `radio-` | radio settings |
265+
| `ss-` | stats summary |
266+
| `terminal-` | terminal (when it lands) |
267+
268+
---
269+
270+
## File Organization
271+
272+
- One CSS file per major UI surface, under `frontend/css/`.
273+
Today: `dashboard.css`, `node_cards.css`, `node_drawer.css`,
274+
`messaging.css`, `radio.css`, `stats.css`.
275+
- Every CSS file is loaded from `<head>` in `frontend/index.html`
276+
via a separate `<link rel="stylesheet">`.
277+
- Inline `<style>` blocks in `frontend/index.html` are not allowed
278+
for design-system rules. Per-page micro-tweaks under 10 lines are
279+
tolerated; anything larger goes in a CSS file.
280+
- Files MUST be saved as UTF-8 without BOM. CSS comments use plain
281+
ASCII separators (for example `/* ---- Section ---- */`), never
282+
Unicode box-drawing characters that may be miscoded.
283+
284+
---
285+
286+
## Tech Debt: Legacy Token Drift
287+
288+
Two existing files predate the canonical `:root` block and still use
289+
a legacy fallback namespace:
290+
291+
- `frontend/css/messaging.css` and `frontend/css/radio.css` reference
292+
`var(--surface-0, #0f0f23)`, `var(--surface-1, #1a1a2e)`,
293+
`var(--surface-2, #22223a)`, `var(--accent, #4ecdc4)`,
294+
`var(--border, #2a2a4a)`. These fallback hex values are
295+
slightly different from the canonical tokens (the legacy teal
296+
`#4ecdc4` vs canonical cyan `#06b6d4`).
297+
- `frontend/css/node_cards.css` hardcodes a Material-style palette
298+
(`#00e676`, `#4ecdc4`, `#ffd54f`, `#ff5252`) for signal-quality
299+
chips.
300+
301+
These files are grandfathered. **Do not extend the legacy
302+
namespace in new code.** All new CSS uses the canonical
303+
`--bg-*` / `--text-*` / `--accent-*` tokens documented above. A
304+
future cleanup PR will refactor the legacy files to the canonical
305+
tokens; that work is intentionally not bundled with regular UI
306+
changes.
307+
308+
---
309+
310+
## Anti-Patterns (Do Not Do This)
311+
312+
The following patterns will be flagged in PR review and the PR will
313+
be sent back for changes. PR #35 (April 29 2026) is the canonical
314+
example for several of these.
315+
316+
1. **Do not redeclare `:root` design tokens with new names.** Adding
317+
`--bg-base`, `--amber`, `--teal`, `--text-hi`, etc. fragments the
318+
palette. Use the existing tokens.
319+
2. **Do not introduce new font families.** `Inter` and
320+
`JetBrains Mono` are the only families. Loading `Barlow`,
321+
`Barlow Condensed`, `Share Tech Mono`, etc. is rejected.
322+
3. **Do not put the design system in an inline `<style>` block in
323+
`index.html`.** The shell layout, color palette, and component
324+
styles live in `frontend/css/*.css` files.
325+
4. **Do not author CSS in non-UTF-8 encoding.** Save files as UTF-8
326+
and view the diff in GitHub before pushing. Codepage round-trips
327+
(most often from terminal-based tools on Windows) can silently
328+
replace `` with `ΓöÇ` and `·` with `┬╖`.
329+
5. **Do not change the page `<title>`, top-bar header text, or
330+
marketing copy** as part of a feature PR. Branding changes go
331+
in a separate, branding-only PR so they can be reviewed against
332+
`branding` rules in isolation.
333+
6. **Do not switch naming conventions inside a component.** A
334+
component is either BEM with double-underscore elements
335+
(`top-bar__brand`) or it is not. A mix of BEM and bare
336+
kebab-case (`panel-header`, `nav-item`, `top-stat-val`) inside
337+
one new component is rejected.
338+
7. **Do not replace the shell layout in a feature PR.** Changing
339+
`.top-bar` + `.tab-bar` + `.tab-content` to a sidebar shell or a
340+
different navigation primitive is a design RFC. Open an issue
341+
first; do not bundle a shell change with feature work.
342+
8. **Do not introduce a new color palette to "match a screenshot"
343+
from another product.** Meshpoint's identity is the cool
344+
cyan/green palette over deep navy. Warm amber/teal palettes,
345+
"hacker green" palettes, light-mode skins, etc. are out of scope.
346+
347+
---
348+
349+
## Review Checklist (PR Author)
350+
351+
Before opening a PR that touches `frontend/`, confirm:
352+
353+
- No new `:root` token redeclarations.
354+
- No new `<link>` to a Google Fonts family beyond Inter and
355+
JetBrains Mono.
356+
- No inline `<style>` block in `index.html` larger than 10 lines.
357+
- New CSS classes follow BEM with an existing or new component
358+
prefix listed above.
359+
- All color values are CSS variables, not hardcoded hex.
360+
Exception: the four signal-quality buckets and the legacy
361+
`messaging.css` / `radio.css` / `node_cards.css` files are
362+
grandfathered.
363+
- Page `<title>`, top-bar header, and marketing strings unchanged
364+
unless this is explicitly a branding PR.
365+
- UI strings honor the brand rules above (`Meshpoint`, no em/en
366+
dashes).
367+
- New CSS files saved as UTF-8 without BOM, no mojibake in
368+
comments.
369+
- Shell layout (`.top-bar` + `.tab-bar` + `.tab-content`)
370+
unchanged unless an RFC issue was opened first.
371+
372+
If you're not sure whether a change conforms, ask in the PR
373+
description rather than guessing. We would rather have the
374+
conversation early.
375+
376+
---
377+
378+
## Related Documents
379+
380+
- `.cursor/rules/branding.mdc` (private repo): brand voice and
381+
release-blurb style rules. UI strings here must obey the same
382+
rules.
383+
- `CONTRIBUTING.md`: general PR workflow, branch names, testing
384+
notes.

0 commit comments

Comments
 (0)