busy-office-ui
Menu

The design language

This framework is a design philosophy for enterprise software first and a stylesheet second. The philosophy fits in one question — not "how can this look better?" but "what can I remove so the essential thing becomes obvious?" — and six principles below turn that question into rules a screen either follows or doesn't. Components emerge from the principles, never the other way around.

Design the decision, not the screen

Every screen exists to serve one decision its user must make. "The payroll manager needs to know whether payroll is safe to release" is a design brief; "we need a payroll screen" is not. The system may run fifty validations — the user reads Ready · 428 employees · 2 exceptions. Complexity lives under the interface, never in the user's mental model.

Every screen must answer, in order: what is this → what should I look at → what should I do. And a screen is one step of a journey (request → validation → exception → resolution → approval → execution → confirmation) — a beautiful screen inside a terrible workflow is still a bad product, so each screen's exit lands the user at the next decision with their context intact. The pattern pages are built this way: each opens with who uses it and what "done" looks like, before any markup.

The six rules

Area Principle Where the framework enforces it
Hierarchy One primary action per screen; secondary actions subordinate; destructive actions require explicit intent. One plain bo-btn per screen — everything else takes --secondary, --ghost, or --danger-ghost. Measured, not aspirational (live DOM at rest, all 21 pattern pages, 2026-08-21): every screen shows ≤1 visible primary action — the wizard's Next/Submit pair is never both visible, the grid's per-row Saves stay hidden until a row is dirty, and master-detail's two demos (panel and dialog) carry one each.
Typography Large = meaning, medium = context, small = supporting information. Size IS the hierarchy. The type-scale tokens — components consume the scale, never free-pick sizes; density remaps one tier and every component follows.
Colour Neutral by default. Colour communicates state — never decoration. Semantic tones only (danger/warning/success), each AA-contrast-gated in both themes at build time, and every colour signal is two-channel: a visible non-colour cue plus a programmatic one, so meaning never rides on hue alone. See the accessibility model and conditional cell tone — the tone repeats a meaning already present in text.
Spacing Consistent rhythm. Whitespace separates concepts; proximity groups related things. The spacing scale in design tokens plus layout primitives (bo-cluster, bo-stack, bo-grid) that carry the rhythm — pages compose primitives instead of hand-tuning margins.
Interaction Direct manipulation; avoid needless dialogs; preserve context; immediate feedback. Inline row editing over modal forms (data-row-edit), errors that re-render in place with the user's work intact (concurrency & conflicts), and focus restored across swaps so a keyboard user is never dumped on <body>.
Content Human language. Explain the consequence, not the mechanism. Processing Status: 04 is refused; "Ready to release" with detail underneath is the shape. Status strings name meanings, technology stays invisible — the user experiences "I clicked and the page responded," not an HTMX partial update. See the HTMX guide: markup carries structure, the server carries intelligence.

The field matrix — which design, per field type and context

Reusability decides what ships; suitability decides what a screen uses. When they conflict, the suitable design wins — an unsuitable control damages the UX no matter how reusable it is — and the resolution is always a suitable reusable primitive from this matrix, never a one-off. Contexts follow the editing-design chooser: entry (bordered), read-mostly correction (seamless + row Save/Cancel), and display.

Field type Standalone form (entry) Read-mostly record/table Editable table (entry grid) Display
Date Native <input type="date"> in a form field — nothing to add Seamless type="date" cell Bordered type="date" cell Text; status (overdue) as a worded badge — the date contract
Number / rate / % .bo-input--numeric Seamless numeric Bordered numeric Amount
Money Currency selectable → Money ( currency | amount ); fixed → numeric, currency in the label Seamless numeric; the fixed currency shown as adjacent text Currency is its own column (fixed per row) → bordered numeric; currency changeable inline (rare) → the joined Money control in the cell Amount with __currency
Quantity Joined ( qty | unit ) — Quantity; touch/warehouse adds the steppers Seamless input; unit as text Unit is its own column → bordered input; unit changeable inline (rare) → the joined control. Steppers never — they belong to touch, not dense grids Amount with __unit

Two axes generate every cell: the screen's job picks the chrome (bordered / seamless / plain text), and the qualifier's mutability picks joined-versus-split (changeable next to the value → joined control; fixed → label, adjacent text, or its own column). Nothing in the matrix is a new component — every cell is a setting of the four primitives.

Subtraction is the method

For every element — button, field, colour, screen, feature — the test is: if I remove this, does the user's ability to accomplish the task materially decrease? If no, it goes. Then ask again: 20 things → 12 → 7 → 4 — then ask whether 4 is enough. Simplicity is not minimalism: minimalism is "there aren't many things," simplicity is "I understand what is happening." A dense ERP grid can be simple; a sparse screen can be confusing.

This test is applied to the framework itself, not just to screens built with it. Additions are refused when they serve exactly one scenario or add a second way to do something that already works — and the refusals are recorded with reasons, the same as the things that shipped. If something needs a long explanation to justify its existence, that is the signal it should not exist.

Details are the design

Simplicity takes more effort, not less. Loading, empty, and error states are designed surfaces, not afterthoughts (state patterns); every pattern page documents them in a states table because screens spend most of their life there. Keyboard behaviour, focus order, wording, and print output get the same treatment as the happy path — good design feels inevitable, not impressive.

Scoring alignment — one number per component

Every component carries a Design-System Alignment score: each of the six rules above, plus context-fit against the field matrix, scored 0-3 with a checkable citation (a build gate, a grep, a measured check — never an opinion). Rules that don't apply to a component leave the denominator instead of penalizing it, and the result reads as a percentage: how on-direction this component's design is. The score decides where alignment work goes — it is deliberately a different question from whether a component is worth its surface, which a separate keep/improve/deprecate review answers. Scores live with the review records and are re-taken when a component's design changes.

The shipping filter

Before a screen ships, it faces ten questions — is the purpose statable in one sentence, is the primary action obvious, can anything be removed, does every element have a purpose, does it explain itself, is complexity hidden, is there decoration, are states understandable, does interaction preserve context, and would it look like this built from zero today. A "no" carries a consequence (remove, reword, simplify, redesign) — not a note. Every pattern in this gallery has been through it, and the deeper principle behind all ten:

Design is the process of deciding what deserves to exist. Don't design screens — design decisions. Don't expose processes — expose outcomes. Don't add functionality — remove friction. Don't make complex systems look simple — make them feel simple.

↑↓ move Enter open Esc close