Prometheus Design System

Inconsistencies

9 inconsistencies identified during the visual audit. Each carries a canonical decision and a concrete fix.

INC-001 minor duplicated-logic

Empty-state Alert duplicates title and body with near-identical text

Canonical: Keep either a single-line title-only alert OR a distinct title+body pair where the body adds new information. Do not restate the title as the body.

Why: Duplicating the same message at two visual prominences adds noise and gives users nothing to read. Screen readers will announce it twice. Either drop the body to get a one-line alert, or put actionable info in the body (e.g. 'Configure alert rules in prometheus.yml under the `rule_files` key').

Fix: Delete the body prop on all four call-sites, or replace the body with a link to the relevant docs page.

Variants across pages
PageImplementation
/alertstitle='No rules found' / body='No rules found.'
/rulestitle='No rules found' / body='No rules found.'
/alertmanager-discovery (Active)title='No active alertmanagers' / body='No active alertmanagers found.'
/alertmanager-discovery (Dropped)title='No dropped alertmanagers' / body='No dropped alertmanagers found.'
INC-002 major missing-normalization

Success/health state is pill-badge in some places and plain text in others

Canonical: Route every boolean/enum state field through a single `HealthStatusBadge` component. Map 'Successful', 'UP', 'Ready', etc. → green variant; 'Failed', 'DOWN', 'Unhealthy' → red variant; 'Pending', 'Warning' → yellow variant.

Why: Users skim status pages for problems. Plain black 'Successful' looks identical to plain black 'Failed' — the UI fails the glanceable-status bar. Meanwhile /targets correctly uses a green pill that pops visually.

Fix: Introduce a `<HealthBadge value={...}/>` wrapper in the UI package. Replace the raw 'Successful'/'Failed' <td> content on /status (and any other /runtime field representing state) with the badge.

Variants across pages
PageImplementation
/targetsPer-target state 'UP' and 'DOWN' rendered as pill badges (green.1 bg + green.8 text for UP; red.1/red.8 for DOWN).
/status'Configuration Reload: Successful' rendered as plain black <td> text (rgb(0,0,0), fontWeight 400). No visual affordance — would look identical to 'Failed'.
INC-003 major implementation-drift

Label name/value pairs rendered three different ways across pages

Canonical: Use Label Badge for every label-name or label-name=value display. A flag like `--config.file` is a label name and should render in the same pill-style as `job="demo"`. Drop bare <code>.

Why: The same underlying concept (a label or identifier) wearing three different visual uniforms teaches users to re-parse each page. Unifying on the pill form also makes these elements trivially clickable/filterable later.

Fix: Replace `<code>{name}</code>` in Flags.tsx and TSDBStatus.tsx with `<LabelBadge>{name}</LabelBadge>`. Keep the pill shape; accept a monospace variant prop if the design team prefers that typographic treatment inside the pill.

Variants across pages
PageImplementation
/targetsFull Label Badge component: pill shape, bg rgb(241,243,245), padding 0 10px, fontSize 11px / fontWeight 700. Renders 'instance="..."' and 'job="demo"'.
/service-discoverySame Label Badge as /targets.
/tsdb-statusLabel names rendered as bare <code> (browser-default monospace, no bg, no padding, no border). Example: '__name__', 'le', 'handler'.
/flagsFlag names (which are effectively label names) rendered as bare <code> — same as /tsdb-status.
INC-004 minor implementation-drift

Sortable columns exist only on /flags — other same-shape tables are not sortable

Canonical: Default every Mantine Table to sortable columns with up/down chevrons, regardless of row count.

Why: Users see sort icons on /flags and then expect them on /tsdb-status (where rows have numeric 'Count' values). Because the Top-N tables are already ordered by count, sortability is virtually free to add and aligns the behavior.

Fix: Flip the `sortable` default to true in the Table wrapper component. Opt-out case-by-case if a specific column genuinely must keep insertion order.

Variants across pages
PageImplementation
/flagsBoth th have stacked up/down chevron icon; clicking sorts the table.
/statusSame Mantine Table but no sort icons — Build/Runtime tables cannot be sorted.
/tsdb-statusSame Mantine Table but no sort icons. The 'Top 10 label names' tables would benefit from sort-by-count.
INC-005 minor style-drift

Card title treatment drifts: with-icon vs without-icon vs no-title

Canonical: Every Card must have a 'Card Title with Icon'. The icon comes from the same Tabler mapping used on /status (database for 'TSDB', flag for 'Command-line flags', file-text for 'Configuration', etc.). Wrap /config and /flags content in Card too so the page-template is uniform.

Why: Icons act as scannable anchors when a page has multiple cards. /tsdb-status has four stacked plain-text cards that blur together; a single tabler-icon prefix makes each card individually identifiable. /flags and /config breaking the Card pattern makes them visually lighter than they should be — the page-chrome is inconsistent.

Fix: Add `<CardTitle icon={Icon}>{title}</CardTitle>` to every card section. Wrap the Flags table and Config codeblock in the same Card component used on /status so every Status sub-page has the same outer shape.

Variants across pages
PageImplementation
/statusCard title uses 'Card Title with Icon' — Tabler icon + heading text. Both cards on this page follow this pattern.
/alertmanager-discoverySame 'Card Title with Icon' as /status.
/tsdb-statusCard titles use 'Card Title without Icon' — all four cards are plain text headings with no leading icon.
/flagsCard has NO title at all. It opens with a Search Input and dumps into the Sortable Table.
/configNo Card wrapper and no title — the YAML CodeHighlight pre sits raw in the page body.
INC-006 cosmetic style-drift

Syntax-highlighted code: semantic color roles invert between light and dark themes

Canonical: Pick a semantic role for each color and keep it stable across themes. E.g. keys are always green, strings are always blue (or vice versa). Use luminance variants (green.4 / green.9) rather than swapping the hue entirely.

Why: A user toggling theme should keep their learned associations. Today, a reader who learned 'green = YAML key' in light theme has to relearn 'blue = YAML key' in dark theme. Role-to-color should be the invariant; luminance is the variant.

Fix: Replace the default highlight.js dark stylesheet with a custom one that mirrors the light role-to-color mapping: .hljs-attr stays green family across themes, .hljs-string stays blue family across themes, etc.

Variants across pages
PageImplementation
/config (light)hljs-attr (YAML keys): rgb(92, 148, 13) — GREEN. hljs-string (string values): rgb(24, 100, 171) — BLUE. hljs-literal (true/false): rgb(34, 139, 230) — BLUE.
/config (dark)hljs-attr (YAML keys): rgb(165, 216, 255) — BLUE. hljs-string (string values): rgb(64, 192, 87) — GREEN. hljs-literal: rgb(34, 139, 230) — BLUE.
INC-007 minor missing-normalization

/alerts and /rules share identical 'No rules found' empty-state copy for different rule types

Canonical: 'No alerting rules found' on /alerts and 'No recording rules found' on /rules, with a link to the respective docs section in the body.

Why: A user who toggles between /alerts and /rules and sees identical copy cannot tell whether their alerting rules and their recording rules are both missing, or whether they are on the same page twice.

Fix: Parameterize the empty-state Alert component with a `ruleType` prop and emit the specific wording.

Variants across pages
PageImplementation
/alerts'No rules found' — but this page shows ALERTING rules; the word 'rules' here refers to alerting rules.
/rules'No rules found' — but this page shows RECORDING rules; the same wording overlaps with /alerts.
INC-008 cosmetic hardcoded-values

<pre> uses bare 'monospace' keyword — no explicit font-family stack

Canonical: Define a single CSS variable `--font-family-mono` and apply it to pre, code, and inline code everywhere. Value: `ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace`.

Why: Bare `monospace` falls back to Courier New on Windows and Courier on macOS — neither matches modern code-editor defaults. A proper stack gives SF Mono / Cascadia / Consolas on users who have them.

Fix: Add `--font-family-mono` to the Mantine theme override and reference it in `pre, code { font-family: var(--font-family-mono); }`.

Variants across pages
PageImplementation
/configgetComputedStyle(pre).fontFamily === 'monospace' — the literal CSS keyword. Browsers resolve this to Courier-family on most OSes, giving poor rendering.
/tsdb-status and /flags (<code>)Inline <code> also inherits bare monospace — same concern.
INC-009 cosmetic style-drift

Stats Badge has conflicting textTransform at root vs inner label element

Canonical: Decide per variant whether uppercase is desired: Health Status Badge uses uppercase (UP/DOWN); Stats Badge shows mixed-case content so it should set textTransform: 'none' at the root, not fight itself by overriding at label level.

Why: Today the CSS specificity accidentally produces the correct visual by overriding at the inner element, but a reader of the source sees a badge that claims to be uppercase and isn't — the intent is ambiguous.

Fix: In the Stats Badge variant, set `text-transform: none` at the root (or use a distinct Mantine variant name 'stats' that sets it explicitly).

Variants across pages
PageImplementation
/targets (root .mantine-Badge-root)getComputedStyle gives textTransform: 'uppercase'. Inherited from the Mantine Badge default.
/targets (inner .mantine-Badge-label)getComputedStyle gives textTransform: 'none'. So the visible content ('991ms ago', '573ms') is NOT uppercased even though the root says it should be.