- InputInspector hangs off the label's element and EdgeInspector off a point in flow coordinates (ViewportPortal), both re-measured every frame while open and hidden once the anchor leaves the view. A label that leaves the canvas closes its popover. - Dock: below md a bar across the width, flush with the bottom edge (bg-card, top border, safe-area inset, no shadow); from md the floating pill as before. The flow dock drops the Run caption and the dashboard editor clears the bar with its own padding, so the editing state fits one row from 360px. - Tests: the search spec checks the popover rides along with a pan; the mobile spec checks both docks are a full-width bar flush with the bottom.
9.1 KiB
Fluksio design guidelines
UI reference for the app, its dashboard editor and documentation site. Paths below are relative to this repository.
Design language
Use neutral white and near-black surfaces with a slate-blue primary accent.
Reserve terracotta for one secondary action per view. --card is the main
surface. Separate surfaces with borders and elevation. Controls are pills;
cards and panels use a 16px radius.
Colour tokens
| Token | Light | Dark | Use |
|---|---|---|---|
--background |
#ffffff |
#0a0a0a |
page |
--foreground |
#333232 |
#f5f5f5 |
body text |
--card / --popover |
#ffffff |
#1a1a1a |
dominant surface; dark lifts above the page |
--card-foreground / --popover-foreground |
#333232 |
#f5f5f5 |
text on those surfaces |
--primary |
#4a7189 |
#7ba3b8 |
primary fills, active state, focus ring |
--primary-foreground |
#ffffff |
#0a0a0a |
text on --primary |
--primary-nested |
#152128 |
#345160 |
a reading drawn inside a --primary fill |
--brand-secondary |
#de8f6e |
#e5a184 |
the one secondary call-to-action per view |
--brand-secondary-foreground |
#333232 |
#0a0a0a |
text on --brand-secondary |
--secondary / --muted |
#f2f2f2 |
#232323 |
recessed surfaces |
--secondary-foreground |
#333232 |
#f5f5f5 |
text on those |
--muted-foreground |
#6b6b6b |
#a3a3a3 |
secondary text, section headers |
--accent |
#f5f5f5 |
#262626 |
hover / selected surface |
--accent-foreground |
#333232 |
#f5f5f5 |
text on --accent |
--destructive |
#b94d4d |
#cf6666 |
destructive actions, failing nodes |
--status-success |
#5e8b6d |
#87b596 |
a node's last run succeeded |
--status-warning |
#b45309 |
#e8a33d |
a node that works, but degraded, such as a device that is offline |
--chart-1 … --chart-8 |
see Chart palette | chart series, in slot order | |
--border / --input |
#e4e4e4 |
#2a2a2a |
hairlines, field borders |
--ring |
#4a7189 |
#7ba3b8 |
focus ring; always equals --primary |
--sidebar* |
mirrors card / accent / primary | sidebar surface set |
The identity blue #59849b stays in the wordmark. Use --primary for controls
so text on primary fills meets contrast requirements. --primary-nested is
for stacked readings inside a primary fill, separated by a gutter.
- Use tokens in UI components. Custom SVG glyphs may use
currentColor. - Keep
--sidebar-accentequal to--accent. - Use one
--brand-secondaryaction per view. - Name every status in text. Running uses
--primary, success--status-success, failure--destructive, and degraded--status-warning. Idle has no indicator. - Home's brain graph uses primary discs as its identity. Its outer ring uses
--foregroundfor runnable nodes and terracotta for flow faults. Failed nodes keep a terracotta disc until acknowledged. Persistent faults keep their labels visible.
Chart palette
| Slot | Hue | Light | Dark |
|---|---|---|---|
| 1 | --primary |
#4a7189 |
#7ba3b8 |
| 2 | orange | #e26f00 |
#d26600 |
| 3 | violet | #6f49ca |
#8d71e6 |
| 4 | green | #00721b |
#55c15e |
| 5 | orchid | #c66ae1 |
#dd8bf6 |
| 6 | ochre | #966a00 |
#ae7c00 |
| 7 | teal | #00a093 |
#00c0b1 |
| 8 | magenta | #b32474 |
#bf4483 |
Take colours in slot order. Line charts stop at five series and show a legend when there is more than one. Reserve red, sage green and amber for status. The terminal UI uses the terminal's named ANSI colours instead.
The dashboard is exempt
User-defined dashboards can choose fluksio, material or glass under
frontend/src/components/Dashboard/ui/. The default fluksio look follows
these guidelines. Other looks may change surfaces and motion.
Behavior, keyboard handling and accessibility stay in ui/core/.
A dashboard palette overrides tokens on its own canvas, with colours assigned
by position. It must preserve contrast, named status, 44px touch targets and
reduced-motion behavior. Fault and success colours keep their meanings.
Widgets use container queries to follow their tile size.
Dashboard motion presets live in ui/core/motion.ts; the app shell uses
frontend/src/lib/motion.ts.
Typography
Use the self-hosted Inter variable font at
frontend/public/fonts/inter-latin-variable.woff2 for body text, headings and
wordmarks. .font-display selects it for display text. Use the system
monospace stack through font-mono for code, message names and payloads.
Use Tailwind's size scale. Arbitrary text-[…] sizes are banned and checked
by the no-arbitrary-font-size hook.
Section headers use:
text-xs font-medium uppercase tracking-[0.5px] text-muted-foreground
Radius
| Utility | Value | Use |
|---|---|---|
rounded-full |
pill | buttons, inputs, toggles, chips, badges |
rounded-sm |
8px | checkboxes, small chips, nested items |
rounded-md |
12px | menus, popovers, tooltips, textareas |
rounded-lg |
16px | cards, panels, dialogs, alerts, sidebar |
rounded-xl |
28px | large containers |
Elevation
Use shadow-e1 for resting cards, shadow-e2 for floating surfaces, and
shadow-e3 for modals. Avoid arbitrary or stock shadow utilities on surfaces.
Borders use border border-border, or border-input on fields.
Motion
Use the motion package with presets from frontend/src/lib/motion.ts.
Keep JavaScript timings aligned with the CSS easing and duration tokens.
- Easing is
emphasizedfor entering or expressive movement,standardfor neutral transitions. - Durations are 150, 200 and 300 ms. One-shot activity feedback uses 500 ms.
- Variants are
fadeIn,scaleIn,slideUp,overlayFadeandlistStagger. - Wrap the app in
<MotionConfig reducedMotion="user">.
Microcopy
Keep captions short. Put explanations in InfoTip, PanelSection.help or
FieldLabel.help. A tooltip holds one or two sentences; longer instructions
belong in the docs. Help must work by tap as well as hover.
Visible supporting text describes state, such as an empty list, a stopped flow or an error. A page may have one descriptive line below its heading. Say what a control does. Keep design rationale in repository documentation.
Avoid em dashes in UI strings. Empty-value placeholders and structural
separators are allowed. The no-em-dash hook checks these rules, with
separators listed in frontend/scripts/no-em-dash.allow.json.
Iconography
Use Lucide. Custom glyphs belong in frontend/src/components/icons/ as 24×24
SVGs with no fill, currentColor strokes, weight 2 and round caps.
Shells
The authenticated app uses a bg-card region with a floating translucent
sidebar. The flow editor floats its controls over the node canvas. Both use
bg-card/80 backdrop-blur-md, a token border and shadow-e2.
Responsive
Use md at 768px as the mobile boundary. Keep useIsMobile() and Tailwind's
md: usage aligned, with stock breakpoints.
- Keep the page within the viewport. Give wide tables and charts their own
overflow-x-autoregion. Usemin-w-0on flex and grid children when content could stretch the page.frontend/tests/mobile.spec.tschecks this. - Cap floating controls with
max-w-[calc(100vw-2rem)]and allow wrapping. The editors' dock is the exception belowmd: a bar across the full width, flush with the bottom edge (bg-card, top border, no shadow, safe-area inset), whose brand button drops its caption so the usual row fits at 44px. - Use 44px touch targets below
md, 32px above. Useicon-touch,sm-touchor matching responsive utilities. - Turn floating panels into full-screen sheets on phones with the shared
Flow/SidePanel. Gate canvas side insets behindmd:. - Provide tap or visible alternatives to hover actions.
- Keep dragging, placing and resizing on desktop. Flow layout is automatic, left-to-right on desktop and top-to-bottom on phones. Rectangular dashboard grids stack to one column on phones.
Use grid gap-* sm:grid-cols-2 lg:grid-cols-N for card grids and
p-4 sm:p-6 md:p-8 for page gutters.
Overlay surfaces & content chips
Floating surfaces use bg-card/80 backdrop-blur-md, a token border and
shadow-e2. Use token alpha for translucency; panel opacity also fades text.
Content chips are transparent at rest and fill on hover or selection.
Use hover:bg-accent/50 where hover and selection need distinct states.
Segmented controls use one bordered pill with no dividers, transparent
segments and bg-accent for the selected segment.
UI configuration
frontend/components.json uses shadcn style new-york, CSS variables and
Lucide icons. Components under frontend/src/components/ui/ are owned by
this repository. Update token definitions, component usage and this reference
together when changing the design.
UX
- Keep common actions easy to reach without context menus.
- Apply theme through the
.darkclass on<html>and the pre-paint theme script so the first frame uses the chosen theme. - A dashboard subtree can use
.theme-lightor.theme-darkindependently. Menus and tooltips rendered outside it must carry that theme class too.