Frontend redesign in progress: updated styles, layout, and components across all pages to align with new design system. Includes Jira API compliance specs, property tests, and load test script.
STEAM Security Design System
A design system for the STEAM Security Dashboard — a self-hosted vulnerability management workbench used by the NTS-AEO-STEAM and NTS-AEO-ACCESS-ENG business units. This repo captures the visual language, content patterns, tokens, and UI kit needed to extend or rebuild the product without drifting from its established look.
What the product is
The STEAM Security Dashboard centralises:
- CVE tracking — searchable, filterable, vendor-aware CVE list with NVD auto-fill, document attachment, and group-based ownership
- Ivanti / RiskSense host findings — live remediation queue with FP / Archer / CARD workflows, inline editing, per-finding notes, and a personal "Ivanti Queue" staging list
- AEO compliance posture — weekly xlsx upload with drift detection, diff preview, per-team metric health cards, device-level violation tracking, and timestamped notes
- Archer EXC tickets — risk-acceptance ticket tracking linked to CVE / vendor pairs
- Knowledge base — internal document library (PDF, Markdown, Office, etc.) for runbooks, advisories, and policies
- Admin panel — user / group management, audit log, system info — all gated behind an Admin group
Four user groups (Admin, Standard_User, Leadership, Read_Only) define every permission boundary, and every state-changing action is audit-logged.
The 6 pages
- Home / Dashboard — CVE list, filters, calendar widget for due dates
- Reporting — Ivanti host findings, charts, queue, export
- Compliance — AEO posture, metric health cards, device drill-in
- Knowledge Base — document library
- Exports — bulk export tools (group-gated)
- Admin Panel — user management, audit log, system info (Admin only)
Sources
- Codebase:
https://vulcan.apophisnetworking.net/jramos/cve-dashboard(Gitea, master branch). Auth required; raw file fetch is gated. The repo's ownREADME.md(fetched via the source viewer) is the most accurate functional spec we had access to and is the basis for this system. - Existing design ref:
DESIGN_SYSTEM.md(290 lines, in-repo) — referenced in the audit but not directly accessible from the host. - Component audit provided in the project brief: 29 components, 5 primitives, 14 composites, 5 pages, 1 context provider.
- Stack: React 19, lucide-react, recharts, react-markdown + rehype-sanitize, mermaid, xlsx. Backend Express 5 / SQLite3.
Index — what's in this folder
| Path | What it is |
|---|---|
README.md |
This file — context, content + visual foundations, iconography |
SKILL.md |
Agent Skill manifest for Claude Code compatibility |
colors_and_type.css |
Source-of-truth tokens — color, type, spacing, radii, elevation |
fonts/ |
Font references (Outfit + JetBrains Mono via Google Fonts CDN) |
assets/ |
Logo mark, brand SVGs, severity icons |
preview/ |
Design System tab cards — registered as assets |
ui_kits/cve-dashboard/ |
High-fidelity recreation of the dashboard, focused on Knowledge Base |
CONTENT FUNDAMENTALS
The product is a tactical operations console for security engineers. Copy is dense, terse, and assumes a reader who already knows what a CVE, EXC ticket, FP workflow, and BU filter are. There is no marketing voice, no onboarding nudges, and no exclamation marks.
Voice & tone
- Operational, not editorial. Buttons say "Sync", "Confirm Upload", "Reconcile Config", "Add to Queue". Never "Let's get started" or "You're all set".
- Imperative for actions, declarative for state. "Save", "Delete", "Hide Selected" — never "Saving your changes…" with three dots and a heart.
- No emoji. Status is communicated through colour-coded badges and short text labels.
- Title Case for navigation and headers, Sentence case for body and inline labels. Tabs and buttons:
User Management,Audit Log,System Info. Helper text:Filter tickets by CVE ID, vendor, or status.
Person & address
- Second-person sparingly — only when the system is talking about the user's data: "your login", "your filtered view", "your queue". Never "Welcome back, {name}".
- First-person plural never. No "We've updated" or "Let us know".
- Errors are direct, no apology. "SESSION_SECRET environment variable must be set." "Login rate limited — wait 15 minutes." Never "Oops! Something went wrong."
Casing
- CVE IDs: uppercase with hyphen —
CVE-2024-12345. Validated against/^CVE-\d{4}-\d{4,}$/. - EXC numbers: uppercase —
EXC-12345. Validated/^EXC-\d+$/. - Severity labels: Title Case —
Critical,High,Medium,Low. Status labels:Open,Addressed,In Progress,Resolved. - Workflow state badges: SHOUT CASE for SLA states only —
OVERDUE,AT_RISK,WITHIN_SLA. Everything else is Title Case. - Group names: snake_case in code (
Standard_User), Title Case in UI (Standard User).
Density and units
- Numerical metrics are bare integers ("12 findings", "47 devices"). Percentages always carry the % sign with no space.
- Dates are explicit, no relative time except "Last sync: 4h ago" patterns.
- Column headers are short —
Host,IP Address,DNS,BU,SLA— neverHost Name (Editable).
Specific copy conventions seen in product
- "— empty —" as a filter option for empty cells
- "Hidden (N)" pattern for counted UI states
- "+N" badge for overflow (e.g., 2 CVEs shown, "+5" badge)
- "↻" revert glyph next to overridden cells, with a small amber dot ● for the overridden state
- Tooltips appear after a 300ms delay and are session-cached
- "View in Reporting →" inline link pattern with a literal arrow
What NOT to write
- No motivational copy ("Great work!", "You're crushing it")
- No question-mark headlines ("Need help?")
- No marketing CTAs ("Upgrade now", "Try premium")
- No mascot or persona — the system is the system
VISUAL FOUNDATIONS
The dashboard reads as a dark tactical intelligence console — slate / graphite backgrounds, sky-blue as the primary accent and ambient glow, severity colours used like signal flags, animated pulse-glow status dots, and information density prioritised over breathing room. The aesthetic is closer to a SOC / NOC mission display than to a flat enterprise SaaS.
Colour vibe
- Dark slate base.
#0F172A(deep slate) for the page,#1E293Bfor surfaces,#334155for elevated surfaces and borders. Almost black, never pure black. The cool tone is consistent — no warm shadows. - Sky blue is the brand accent —
#38BDF8is the primary action / link / focused state colour. It appears in buttons, active nav items, link text, and the "create" badge in the audit log. - Severity is a fixed semantic system — the colours below MUST mean what they mean and nothing else.
- Critical → Red
#EF4444 - High → Amber
#F59E0B - Medium → Sky
#38BDF8 - Low → Emerald
#10B981
- Critical → Red
- Neutral text scale —
#F1F5F9(primary fg),#CBD5E1(secondary),#94A3B8(muted),#64748B(placeholder / disabled). Never pure white. - Group badges — Admin red, Standard_User accent blue, Leadership amber, Read_Only muted grey. The same severity language reappears here for status urgency.
Typography
- Outfit for all UI (headers, body, buttons, navigation). Geometric sans, friendly but precise; weights 400 / 500 / 600 / 700.
- JetBrains Mono for data — CVE IDs, IP addresses, hostnames, EXC numbers, finding IDs, code blocks. Anything you'd grep for.
- Scale is compact. Page titles 24–28px / 600 weight; section headers 16–18px / 600; body 14px / 400; data table cells 13px / 400 mono. Line-height stays tight (1.4) to preserve density.
Spacing
- 4 / 8 / 12 / 16 / 24 / 32 / 48 — a roughly 4px grid. Cards have 16–20px internal padding; rows in dense tables have 8–10px vertical padding; modals have 24px internal padding.
- Section gaps are 24–32px. Between siblings, 12–16px is the dominant rhythm.
Backgrounds
- No imagery. No hero photographs, illustrations, or marketing visuals. The page is solid
#0F172A. - Subtle sky-blue grid is allowed. A 20×20px grid at
rgba(14,165,233,0.025)(.grid-bgutility) sits behind hero / empty regions. It is barely visible and never dominates. - Surfaces use diagonal gradients, not flat fills —
linear-gradient(135deg, rgba(30,41,59,0.95), rgba(51,65,85,0.9))is the canonical card surface.
Cards and surfaces (intel-card)
- Background: diagonal gradient
135deg, rgba(30,41,59,0.95) 0%, rgba(51,65,85,0.9) 50%, rgba(30,41,59,0.95) 100% - Border: 1.5px solid
rgba(14,165,233,0.30)— sky-blue at low alpha, not slate grey - Radius: 8px (default) / 12px (modals) / 4px (chips)
- Internal padding: 16–20px
- Resting shadow:
0 4px 12px rgba(0,0,0,0.4)+0 2px 6px rgba(0,0,0,0.3)+ inset0 1px 0 rgba(14,165,233,0.10)(sky highlight on top edge) - Hover: border opacity climbs to
0.50, the card liftstranslateY(-2px), and gains a0 0 30px rgba(14,165,233,0.10)ambient glow. A::aftershimmer sweeps left→right on entry. - Stat cards add a 2px
linear-gradient(90deg, transparent, #0EA5E9, transparent)rail on the top edge.
Borders
- Sky-blue at low alpha is the dominant border treatment —
rgba(14,165,233,0.15)for subtle dividers,0.25for default,0.40for strong / hover. Pure slate#334155borders appear only on tables and inputs at rest. - Focus state: 2px sky-blue ring
0 0 0 2px rgba(14,165,233,0.15)plus the border swaps to solid#0EA5E9. - Severity-tinted left borders are NOT a pattern — colour is carried by badges, dots, and glow.
Animation
- Pulse-glow on status dots is canonical. Every severity / SLA badge has an 8px circle that pulses
box-shadow: 0 0 5px → 15px currentColoron a 2s ease-in-out loop (@keyframes pulse-glow). - Card hover lift is 300ms cubic-bezier(0.4,0,0.2,1) with a
::aftershimmer sweep —linear-gradient(90deg, transparent, rgba(14,165,233,0.08), transparent)translating fromleft:-100%to100%over 500ms. - Buttons have a circular ripple
::beforethat scales from 0×0 to 300×300 on hover (500ms). - Modal entry: 200ms fade + slight translate. Slide-out panels: 240ms ease-out from the right.
- Tooltips have a deliberate 300ms hover delay before appearing.
- A
.scan-lineutility (3s loop) is available for hero / loading affordances — used sparingly.
Hover states
- Cards lift
-2px, border opacity climbs from0.30→0.50, and a sky-blue ambient glow0 0 30px rgba(14,165,233,0.10)appears. - Buttons brighten their gradient fill from
0.15/0.10to0.25/0.20alpha, gain a0 0 20pxbrand-color glow, and lift-1px. - Text links lighten from
#38BDF8to#7DD3FCand the bottom border brightens to match. - Table rows get a
rgba(0,217,255,0.06)wash plus0 2px 8px rgba(0,217,255,0.10)sub-shadow. - The audit notes a current anti-pattern: hover states implemented via
onMouseEnter/onMouseLeaveJS handlers. The design system standard is CSS:hoverpseudo-classes — JS hover is a defect to migrate away from.
Press / active states
- Button: shifts to
#0EA5E9(slightly darker than hover), no shrink, no shadow change. Press is a colour signal, not a physics signal. - Rows / interactive cards:
#475569background on:active.
Transparency & blur
- Modal backdrops:
rgba(10, 14, 39, 0.97)withbackdrop-filter: blur(12px). The blur is heavy and the backdrop is near-opaque — modals fully obscure the background. - Tooltips: gradient
linear-gradient(135deg, #334155, #475569)with a sky-blue border and0 4px 12px+0 0 16px rgba(14,165,233,0.15)glow. - Inputs: translucent
rgba(30,41,59,0.6)background withinset 0 2px 4px rgba(0,0,0,0.2)for a subtle recessed feel.
Inner / outer shadows
- Both are used. Cards combine outer drop + inner sky-blue highlight:
0 4px 12px rgba(0,0,0,0.4), inset 0 1px 0 rgba(14,165,233,0.10). - Inputs are recessed —
inset 0 2px 4px rgba(0,0,0,0.2)plus a0 1px 0 rgba(255,255,255,0.03)top sheen. - Document items (within KB / vendor lists) use a stronger inset
inset 0 2px 4px rgba(0,0,0,0.3)to read as nested / pressed-in. - Modals lift on
0 20px 60px rgba(0,0,0,0.6) + 0 10px 30px rgba(14,165,233,0.10)— heavier than most enterprise products, but the brand glow is the signature.
Layout rules
- Full-width fluid above 1024px — the dashboard fills the viewport, with content max-width capping at ~1600px on very wide displays.
- Top app bar is fixed — height 56px, contains brand mark, page nav, and
UserMenu. Sits above all content withz-index: 50. - Side nav drawer (NavDrawer) slides from the left on icon click; it does not push content (overlay model).
- Slide-out panels (Atlas, Compliance Detail) come from the right, ~480px wide on desktop, full-width on narrow viewports.
- Modals are centered, max-width 640px (small) or 960px (wizard / upload), with the standard backdrop.
Severity language
This is the most important visual rule in the product. Severity badges use the status-badge pattern:
- 2px solid border at
0.6alpha - Diagonal gradient fill at
0.20 / 0.15alpha - Lighter text for legibility —
#FCA5A5(critical),#FCD34D(high),#7DD3FC(medium),#6EE7B7(low) — not the raw severity colour - Text-shadow
0 0 8pxbrand-color at0.4alpha - 8px filled circle dot with a pulsing
box-shadow: 0 0 12px / 0 0 6pxglow on a 2s loop 0 4px 8px rgba(0,0,0,0.4)outer shadow- Always JetBrains Mono, uppercase, 0.5px letter-spacing
Secondary references can use simpler tinted pills (rgba(brand,0.12) background + brand text, no border, no glow). Single coloured dots ● next to numeric scores are also valid. The colour-to-severity mapping is fixed across every component.
Headings — the brand glow
Page titles and section headers are JetBrains Mono, uppercase, sky-blue #38BDF8, with text-shadow: 0 0 16px rgba(14,165,233,0.30), 0 0 32px rgba(14,165,233,0.15). This is the most identifiable single signal in the product — every page header reads as a glowing terminal title. Outfit is reserved for body, helper, and table cell text. The Knowledge Base markdown viewer continues this language: h1 sky-blue, h2 emerald, h3 amber — a deliberate severity-coloured hierarchy.
ICONOGRAPHY
The product uses lucide-react as its sole icon system. Lucide is a 1.5px-stroke, geometric, open-source icon set — clean, restrained, and perfectly aligned with the dark tactical aesthetic.
Rules
- All icons are line / stroke style — never filled glyphs (with one exception: the calendar's red due-date dot is a filled circle, but it's a status indicator, not an icon).
- Stroke width: 1.5–2px (lucide default). 1.5px on small icons (≤16px), 2px on larger icons.
- Sizes: 14px (inline with text), 16px (default UI), 20px (nav items, prominent buttons), 24px (page-header icons).
- Colour: inherits
currentColor— text-foreground for default,#38BDF8for active / accent, severity colours when used as a status indicator. - No emoji anywhere. Status, severity, and category use icons + colour; never
🔴or⚠️. - No unicode-as-icon shortcuts beyond
●(status dot),↻(revert / cycle),↱(redirect),⊙(filter handle),→(inline link),+N(count badge). These are part of the typography, not stand-ins for missing icons.
Brand mark
The product has no published logo file in the repo (the audit references AtlasIcon as a custom SVG brand icon — Atlas appears to be the action-plan integration, not the dashboard's own brand). For this design system the brand mark is a typographic stack: STEAM in Outfit 700 with a sky-blue underline accent and a small shield glyph (lucide Shield) to the left. See assets/logo.svg and assets/atlas-shield.svg.
Substitutions flagged
- Atlas action-plan brand icon is recreated as a generic shield (lucide
Shield) tinted sky-blue. If you have the realAtlasIconSVG, please attach it — the in-product version is custom and not available from the repo URL. - Fonts (Outfit, JetBrains Mono) load from Google Fonts CDN. If you need offline font files, attach the woff2s and we'll bundle them into
fonts/.
Icons used per page (from README)
- Home: Calendar (CalendarWidget), Search, Filter, Plus, Upload, Edit, Trash, X (close)
- Reporting: RefreshCw (Sync), Eye / EyeOff (row visibility), Check, Filter (⊙ in column header), Columns, Download (Export), MoreHorizontal
- Compliance: Upload, AlertTriangle (drift breaking), AlertCircle (drift silent-miss), Info, ChevronRight, FileText
- Knowledge Base: FileText, FilePlus, Folder, Download, Eye
- Admin: Users, ScrollText (audit log), Activity (system info), Shield (admin badge)
- Universal: ChevronDown, ChevronUp, Check, X, Loader, ExternalLink
When picking an icon, prefer the lucide-react name from this list before introducing a new one.
UI Kits
| Kit | Path | What it covers |
|---|---|---|
cve-dashboard |
ui_kits/cve-dashboard/ |
App shell (top bar, nav drawer, user menu), Knowledge Base page + viewer, primitives (Button, Badge, Pill, Input, Select, Modal shell, SlideOutPanel, DataTable, GroupBadge, SeverityBadge, EmptyState, LoadingState) |
The Knowledge Base page is the focused recreation. Other surfaces (Reporting, Compliance, Admin) are intentionally not built out — the primitives + shell are sufficient to compose them.
How to use this system
- Tokens first. Import
colors_and_type.cssinto the root of any HTML file. All colour, type, radius, shadow, and spacing decisions should pull from these CSS custom properties. - Pick a primitive before inventing. Severity badges, group badges, status pills, table row, modal shell, slide-out panel — they all live in
ui_kits/cve-dashboard/. - Match the density. When in doubt, tighter is more on-brand than airier.
- Lucide for icons. Use the lucide-react CDN or copy individual SVGs from the lucide site. Do not draw your own.
- No emoji, no gradients, no illustration, no marketing copy. The product is a console.