Design System Foundations
How our software applications look, behave and feel across the product suite — providing a unified, familiar experience for planning, production and reporting tools.
Principles
Our key UX principles we must abide by for our customers' benefit.
Clarity over complexity
Reveal only what is relevant, gradually displaying complexity progressively when required.
Consistency builds confidence
The same action, agnostic of application or service, must behave the same way.
Efficiency under pressure
Beyond minimising clicks — reduce friction and pain points wherever possible.
Safety by design
Always ensure users are clearly informed of destructive and irreversible actions.
Transparency in feedback
Always display visually what the system is doing. Never hide important information in the background.
Tone & Personality
At Deswik we strive for a professional and calm digital presence, reflective in our software and how it is designed and how it functions. Our users perform precise, data-driven actions and our solutions should aid them as best as possible.
Because our users work abroad, we avoid internal jargon and slang that is not industry-wide knowledge. We use clear and direct language when our systems communicate on our behalf.
Naming
The names we give CSS variables, design tokens, and components have to survive growth, redesigns, and new features. Four priorities drive every naming decision; a single structure pattern keeps tokens predictable; and a small list of preferences resolves the recurring "which word do we use" debates.
Priorities
Semantics
Names should explain what something does or represents, not just how it looks. Don't lock meaning to appearance — appearance changes; meaning shouldn't have to.
Consistency
The same concept gets the same name everywhere — across CSS, Figma tokens, component classes, and product code.
Growth
The naming system has to survive new features, redesigns, and new themes without forcing a global rename.
Searchable
Prefixes matter. A name should be findable by category, with theme expressed either in the name itself or in the grouping it sits in.
Structure
Token and class names follow a consistent left-to-right pattern, narrowing from broad to specific.
--{category}-{property}-{variant}-{state}-{size modifier}
Examples
--validation-toast-success-bright
--button-danger-hover-small
--field-number-focus
Categories
Rules
- All lower case.
- Keep Figma design tokens and CSS names the same — one source of truth, two surfaces.
- Prefer full words over abbreviations:
backgroundnotbg;buttonnotbtn.
Preferences
When two reasonable words mean the same thing, we pick one and stick with it. The chosen term is in bold.
| Concept | Candidates | Use |
|---|---|---|
| Spacing | spacing · space · gap · padding · margin · size |
space (between objects) and size (of the object itself) |
| Colour hierarchy | active · primary · accent |
brand, then primary / secondary / tertiary, then active |
| Corner radius | radius · corner-radius · border-radius |
border-radius |
| Foreground colour | color · fill |
color |
| Validation — bad | error · danger |
danger |
| Validation — caution | warning · caution |
warning |
| Validation — good | success · positive |
success |
| Validation — neutral | information · neutral |
info |
| Validation — partial | — | incomplete |
| Form control | input · field |
field |
Theme & Consistency
- Always design in light theme first.
- Allow users to switch between light, dark, or system preference.
Colors
Our target is WCAG 2.x AA contrast across the whole system — accessible, readable, and inclusive interfaces regardless of theme. The ratio you need depends on what the colour is doing.
Body text
The quick brown fox jumps over the lazy dog.
Paragraph copy, table cells, helper text — anything below 18 px.
Large text
Large text — 18 px+
18 px and above, or 14 px bold — headings, hero copy, callouts.
UI components & graphics
Icons, chips, borders, focus rings, status pills, and form-control outlines.
Product / accent colour
The product (or accent) colour changes per product. It carries the product's visual identity — brand chrome, hero accents, the product glyph — without touching any of the system's interactive states. Two examples shipping today:
Greyscale
Scales are generated from lightness values at 98, 96, 94, 90, 85, 80, 60, 40, 20 and 10. The active color can be used on backgrounds of --gray-1 and --gray-2. Backgrounds of --gray-3 and higher will fail contrast without shading the active color.
Usage notes
The first six steps build surfaces, fills, hovers and borders — the
architecture the page is constructed from. The last four steps carry text,
stepping from quietest helper copy at --gray-7 to the
highest-contrast headings at --gray-10.
Surfaces, fills & borders
- --gray-1 — Card-1 backgrounds, banded table rows, code-block fills, properties-panel and primary-nav surfaces.
- --gray-2 — Card-2 backgrounds, default and transparent button hover, accordion hover, disabled-row fill, version-tag chip.
- --gray-3 — Default button background, table row hover, ribbon-active fill, low-emphasis status tags.
- --gray-4 — Tertiary button hover state.
- --gray-5 — Tertiary button focus, slider and toggle tracks, light dividers.
- --gray-6 — Standard border colour for inputs, cards, dividers, the close-button icon at rest.
Text colours
- --gray-7 — Muted / secondary text — captions, helper copy, placeholder text, chart axis labels, file-list metadata.
- --gray-8 — Stronger muted text — toolbar icons, sidebar nav labels, scrollbar thumb, hover state on close-button icons.
- --gray-9 —
<h2>/<h3>headings, the link:hovercolour, code-pill text, axis values. - --gray-10 — Highest-contrast text —
<h1>, the weighted headings<h4>–<h6>, button hover text, site title.
Active
The active colour signals interactivity — buttons, links, form-control states (checkboxes, radios, focus rings). Because no single hue holds 4.5 : 1 against every possible background, the active colour is shaded for its specific role: one tone for the resting state, one for hover, one for focus, and a separate tone for active text shown on a selected background.
Usage
- --active — Buttons, links and form-control resting state. Light:
#1676d6. Dark:#1676d6. - --active-hover — Button / link hover. Light:
#1260ae. Dark:#1260ae. - --active-focus — Button / link focus. Light:
#0f4f8f. Dark:#0f4f8f. - --active-on-selected — Active text rendered on a selected background. Light:
#0f4f8f. Dark:#6ca9e5.
--active-primary remains as an alias for --active — it's the
token to reach for when you specifically need the contrast-safe variant for white text on
an active-coloured background (the Primary button).
Validation — three variants
Pick whichever suits your application best. You can use the validation classes for general text highlighting, but use .warning-text rather than .warning as the warning colour does not pass contrast on light backgrounds.
Validation text colors
You can use the validation classes for general text highlighting, but use .warning-text as opposed to .warning as the warning colour does not pass contrast rules on light backgrounds.
Incomplete — .incomplete-text
Info — .info-text
Success — .success-text
Warning — .warning-text
Danger — .danger-text
Colourful
Plain
Bright
Typography
Headings are sized proportional to the base text size. Line heights are calculated at 1.25× the font size and may be rounded to the nearest pixel if fractional sizes are not supported.
Headings
Heading 1 — Display
Heading 2 — Section
Heading 3 — Subsection
Heading 4 — Group
Heading 5 — Label
Heading 6 — Micro
Font size modifiers
Font bigger — .font-bigger
Font big — .font-big
Default body text — base size.
Font small — .font-small
Font smaller — .font-smaller
Font weights
Light weight (200) — .font-light
Regular weight (400) — .font-regular
Semibold weight (600) — .font-semibold
Bold weight (700) — .font-bold
Inline text
Use strong or bold to highlight specific words. Use
links for navigation. Use inline code or
samples in monospace. Subscript H2O and superscript E = mc2.
Date & time formats
Unless specified otherwise, display dates and times in the local format. The standard user-facing format is MMM DD, YYYY, HH:MM AM/PM — identified as the fastest to scan visually.
For tables, lists, and anywhere dates appear stacked, the digits must line up by column
even when the dates themselves vary. The cleanest way to do that without breaking out of
the page font is font-variant-numeric: tabular-nums: it keeps Inter as the
family but engages the font's tabular-figure OpenType feature, so every digit takes the
same advance width. The result reads as ordinary body text yet every comma, colon, and
AM/PM lands in the same horizontal column down the list.
Don't — proportional digits
Mar 17, 2023, 11:28 AM
Aug 08, 2024, 02:30 PM
Dec 31, 2024, 10:00 PM
Feb 04, 2025, 12:45 PM
Sep 22, 2025, 05:00 AM
Inter's proportional digits make 1 narrower than 0 — every comma, colon, and meridiem token drifts row to row.
Do — tabular-nums (still Inter)
Mar 17, 2023, 11:28 AM
Aug 08, 2024, 02:30 PM
Dec 31, 2024, 10:00 PM
Feb 04, 2025, 12:45 PM
Sep 22, 2025, 05:00 AM
Same Inter family as the rest of the page — only the digit width has changed. The time portion locks to a perfect column.
One caveat: the MMM month abbreviation is still proportional
(Jan, Mar, and Sep have different widths in Inter),
so the comma after the day number can shift a pixel or two between rows. For columns
where perfect start-to-end alignment is non-negotiable — reconciliation tables, audit
logs, batched timestamps — use a numeric format like
YYYY-MM-DD HH:MM or
MM/DD/YYYY HH:MM AM/PM with tabular-nums.
Every character then comes from the digit set or fixed punctuation.
Implementation
Apply font-variant-numeric: tabular-nums on the cell, the column's
td rule, or use the existing .tabular-nums utility from the
product layer. The foundation already wires this
on td.num and th.num in the
Tables & Grids section, so the «Tonnes» and
«Grade» columns there are already tabular without any per-cell styling.
Code
Code snippets and inline formulas use Fira Code — a monospace family with
programming ligatures for the arrows, equality signs, and comparison glyphs that
engineering input contains. The generic monospace keyword is the fallback,
so terminals, embedded views, and offline contexts still render with a fixed-width family
when Fira Code isn't available.
Example — formula
Snippet — declaration
code, samp, kbd, pre {
font-family: 'Fira Code', monospace;
}
Full typography stack — @font-face declarations
The four font families used across the system are loaded locally via
@font-face so the visual identity stays consistent regardless of the
user's network or device font availability. Inter is the variable body face, Fira
Code carries monospace contexts, Noto Serif SC handles Simplified Chinese, and
Sandvik Sans is reserved for Sandvik-branded surfaces.
@font-face {
font-family: 'Inter';
src: url('Inter-VariableFont_opsz,wght.ttf') format('truetype');
font-weight: 100 900;
font-stretch: 50% 100%;
font-display: swap;
}
@font-face {
font-family: 'Fira Code';
src: url('FiraCode-Regular.ttf') format('truetype');
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: 'Noto Serif SC';
src: url('NotoSerifSC-Regular.ttf') format('truetype');
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: 'Sandvik Sans';
src: url('SandvikSans-Regular.ttf') format('truetype');
font-weight: 400;
font-display: swap;
}
Font-family utility classes
Each face is reachable as a utility class so non-default contexts (a code block in a marketing page, a Chinese-language heading inside an English page, a Sandvik co-branded panel) can opt in without rewiring the whole element tree.
body, .font-body {
font-family: 'Inter', sans-serif;
}
code, samp, kbd, pre, .font-code {
font-family: 'Fira Code', monospace;
}
.font-chinese {
font-family: 'Noto Serif SC', serif;
}
.font-sandvik {
font-family: 'Sandvik Sans', sans-serif;
}
Special cases
When typography needs to display content in a language whose characters aren't well-served by Inter, switch to a face designed for that script. The default body font handles Latin glyphs cleanly but can't represent ideographic or other non-Latin scripts authentically — using a face designed for the script keeps the strokes, proportions, and visual rhythm that native readers expect.
For Simplified Chinese, the system uses
Noto Serif Simplified Chinese (Google Fonts:
Noto Serif SC) — a serif designed specifically for the character forms
of simplified Chinese. Its stroke contrast and proportions reflect the script's
construction in ways a Latin-first family cannot. Mark up the affected content with
lang="zh-Hans" and the foundation's :lang(zh-Hans) rule
swaps the family in automatically.
Example — Simplified Chinese
矿山规划设计系统
使用 Noto Serif 简体中文呈现汉字时,笔画结构和字形比例能够更加清晰准确地表现出来,适合阅读密集的内容。
Snippet — declaration
:lang(zh-Hans) {
font-family: 'Noto Serif SC', serif;
}
For Sandvik branding, use Sandvik Sans — Sandvik's
own corporate sans-serif. Apply it to surfaces that carry the Sandvik visual identity
(co-branded pages, partner widgets, equipment displays). The generic
sans-serif keyword is the fallback, so machines or kiosks without the
licensed font still render cleanly. Mark the affected element with
data-brand="sandvik" and the foundation's
[data-brand="sandvik"] rule swaps the family in automatically.
Example — Sandvik branding
Sandvik · Mine Planning Toolkit
Surfaces that carry Sandvik's visual identity render in Sandvik Sans. The fallback
sans-serif keeps the type legible when the licensed font isn't
available on the device.
Snippet — declaration
[data-brand="sandvik"] {
font-family: 'Sandvik Sans', sans-serif;
}
Scale
Sizing and spacing are foundational layout primitives, but they describe
different things: sizing is the dimensions of an
element (icon size, font size, component height), while spacing
is the gap between elements (gap, padding, margin). They share a
numeric scale so the values feel consistent, and at the call site they
appear under two parallel token names — --size-N for the
dimension of a thing, --space-N for the breathing room
between things.
When a product only ever needs three to five values per role, an optional
set of t-shirt aliases (--gap-sm, --padding-md,
--margin-lg, etc.) is documented under
Add Ons → Spacing Variables.
The scale
--size-1px—1 px--size-2px—2 px--size-10.25rem4 px--size-20.5rem8 px--size-30.75rem12 px--size-40.875rem14 px--size-51rem16 px--size-61.25rem20 px--size-71.5rem24 px--size-81.75rem28 px--size-92rem32 px--size-102.5rem40 px
Special case — borders. Borders use fixed pixel values
(--size-1px, --size-2px) for visual precision. A
rem-based 1 px border can blur or vanish at certain zoom levels; a pixel
value renders the same hairline on every device.
Sizing — element dimensions
Reach for --size-N when the value describes the element
itself — its font size, icon size, height, or border width.
Typography
- --size-3 —
<h6>; smaller default font size for model-space applications. - --size-4 — Base text size,
<h5>. - --size-6 —
<h4>. - --size-7 —
<h3>. - --size-8 —
<h2>. - --size-9 —
<h1>.
Icons, components & borders
- --size-1px — Standard border width (table cells, dividers, input borders).
- --size-2px — Heavier border (table-header underline, focus rings).
- --size-3 —
.icon-sm. - --size-4 — Default icon size (
.icon-md). - --size-7 — Sidebar / navigation icons (
.icon-lg). - --size-10 — Feature-card icons.
Spacing — between elements
Reach for --space-N when the value is the gap, padding or
margin around an element. Same numeric values as --size-N;
the parallel name signals layout intent at the call site so a reviewer can
tell a gap from a font-size at a glance.
| Token | rem | px | Typical use |
|---|---|---|---|
--space-1px | — | 1 | Fixed pixel space. |
--space-2px | — | 2 | Fixed pixel space. |
--space-1 | 0.25 | 4 | Data-grid padding. |
--space-2 | 0.5 | 8 | Input / button padding. |
--space-3 | 0.75 | 12 | Field and input gaps. |
--space-4 | 0.875 | 14 | Equivalent to text size. |
--space-5 | 1 | 16 | — |
--space-6 | 1.25 | 20 | — |
--space-7 | 1.5 | 24 | — |
--space-8 | 1.75 | 28 | — |
--space-9 | 2 | 32 | Body / large component padding. |
--space-10 | 2.5 | 40 | Feature-card padding. |
When using these in Figma files, use pixels; in production code, prefer the
relative rem values so the layout scales with the user's root
font size.
Size modifiers
Size modifiers scale a whole component while preserving its proportions — the height, internal padding, font size and icon size all step together. Apply them to entire components (buttons, inputs, cards), not to individual properties.
- Extra small (
x-small) — dense surfaces (data grids, toolbars). - Small (
small) — secondary actions, compact lists, side panels. - Medium (
medium) — the default for almost everything; buttons in forms, page-level controls. - Large (
large) — primary call-to-action on a hero or empty state. - Extra large (
x-large) — feature cards, marketing surfaces, single-action splash screens.
Font size modifiers
Where the size modifiers above scale a whole component, font size modifiers
scale a single piece of text relative to the element's own base size.
They drive the same --font-size-scale variable that the
foundation uses internally for headings, so a piece of text inside a
heading still scales proportionally rather than jumping to a fixed pixel
value.
.text-x-small — 0.75 × the element's base size.
.text-small — 0.875 × the element's base size.
Default body text — 1 × the element's base size.
.text-large — 1.25 × the element's base size.
.text-x-large — 1.5 × the element's base size.
.text-x-small— fine print, hint copy, captions in dense surfaces..text-small— secondary copy, helper text, table footnotes..text-large— emphasised body text, lead paragraphs in articles..text-x-large— pull quotes, hero subheads beneath an<h1>.
Reach for --font-size-scale directly only when none of the
four modifier classes fits. Otherwise use the named class so the value
stays anchored to the t-shirt scale shared with component size modifiers.
Tables and Data Grids
Table — used when space is available, visual display is more important and when data has a complex structure. Data grid — used when high density is needed, with inline editing as a key feature, where Excel would be a substitution.
Table (banded, with row states)
| Block | Material | Tonnes (t) | Grade (g/t) | Dimensions | Status | |
|---|---|---|---|---|---|---|
| BX-01 | Ore | 12,450.00 | 2.45 | 100 × 80 | OK | |
| BX-02 | Ore | 9,820.50 | 3.10 | 100 × 80 | Reviewing | |
| BX-03 | Waste | 21,300.00 | 0.15 | 120 × 90 | Check | |
| BX-04 | Waste | 8,775.25 | 0.08 | 100 × 80 | Failed | |
| BX-05 | Ore | 15,612.75 | 2.90 | 110 × 85 | OK |
Data grid (high density)
| ID | Description | Length (m) | Width (m) | Depth (m) | Volume (m³) | Status |
|---|---|---|---|---|---|---|
| D-001 | Drift heading north | 128.40 | 5.50 | 5.20 | 3,672.18 | OK |
| D-002 | Cross-cut east | 42.10 | 4.80 | 4.80 | 970.30 | Warning |
| D-003 | Stope access | 88.65 | 5.50 | 5.20 | 2,536.95 | Danger |
| D-004 | Return airway | 204.00 | 3.50 | 3.50 | 2,499.00 | Info |
| D-005 | Service decline | 312.75 | 6.00 | 5.50 | 10,320.75 | Incomplete |
Alignment rules
- Numerical data is right-aligned. Textual data is left-aligned. Headers align with their data.
- Use center alignment for small controls — checkboxes, validation icons, numbered rows.
- Multi-part values (e.g. 100 × 80) align by the separator; match decimal places.
- Include the unit in the header so it isn't repeated in every cell.
- Use
font-variant-numeric: tabular-numsfor clean number alignment.
Clickable cell text
When a cell value navigates to a record's own page, render the text in --text-color by default and only colour it on hover. A row of clickable cells in their default --active state competes with chips, status pills, and the row's own selection state for attention; pulling the colour back to body text keeps the table calm and reserves the active colour for the moment of intent.
Example
| Schedule | Owner | Status |
|---|---|---|
| Pit West — Schedule v3 | Jane Smith | Approved |
| Pit East — Schedule v2 | Marcus Khan | In review |
| Combined — Schedule v1 | Avani Barr | Draft |
Hover any name to see the link colour and underline appear; the cell stays neutral the rest of the time so the chips and selection state read first.
Snippet — declaration
table td a {
color: var(--text-color);
text-decoration: none;
}
table td a:hover {
color: var(--active);
text-decoration: underline;
}
Form Inputs
Form inputs are minimally styled — browser defaults control the main appearance. Inputs and
buttons share the same height for visual consistency. Inputs should be sized to the content they
accept. Labels are inline with fields, right-justified, and linked via for/id.
Cards
Card containers group content. Use .card-1 or .card-2 for tinted backgrounds.
Default card
A simple container — no background tint.
Card 1
Tinted with --gray-1 for subtle separation.
Card 2
Tinted with --gray-2 for stronger separation.
Page Layouts
Two primary page layout variants for product UIs.
Top header & navigation layout
Provides navigation in the top header without a side navigation. Allows native display with labels, without taking up additional space. The header consists of the site title, switchable account/company control, and a menu of transparent buttons (collapsible to icons).
Side navigation layout
Familiar app-style side navigation in a collapsible menu. Click expand to show page titles —
the state should persist across pages and sessions. Long company or user names should display
outside the <nav> component.
Layout templates
Icons
Icons are a visual representation of a function or message that helps reinforce it. They act as accelerators for advanced users who can recognise the symbol without reading the accompanying text.
Purpose and philosophy
- Icons should support the meaning, not replace it.
- Use icons to reinforce familiar, unambiguous actions or statuses where the symbol is widely understood and adds speed or clarity.
- If an icon does not meaningfully improve recognition or scanability, do not include it — rely on text instead.
- Do not use icons just because there is space, or to make a UI appear more interesting. Icons must serve a purpose.
Structure
Use <i> tags with Font Awesome classes. Always include
.fa-fw so icons share a fixed width and align cleanly when listed,
stacked, or paired with text.
<i class="fal fa-fw fa-house"></i>
Vue and React FA packages use the same icon names with a shorthand:
Vue
<FontAwesomeIcon :icon="byPrefixAndName.fal['house']" />
React
<FontAwesomeIcon icon={byPrefixAndName.fal['house']} />
Variants
Each product may use whichever variant or combination of variants it chooses — they need only be consistent within the product.
| Sample | Class | When to use |
|---|---|---|
.fal |
Lightweight icons — when regular feels too bold for the surface. | |
.far |
Regular weight — preferred for most contexts. | |
.fas |
Solid — suits icons used inside buttons or for alert statuses. | |
.fa-sharp |
Do not use. Reserved — kept here for reference only. |
Stacked icons
Some icons (especially validation icons) contain a cutout that shows the surface colour through. Stack icons to place a solid white or black backplate behind the shape, making the symbol clearer against any background.
Apply .fa-stack to the parent element of the two icons, and add
.fa-stack-1x or .fa-stack-2x to the icons themselves
to size them within the stack.
Warning — black backplate
<span class="fa-stack">
<i class="fa-solid fa-triangle fa-stack-2x black"></i>
<i class="fa-solid fa-triangle-exclamation fa-stack-2x warning"></i>
</span>
Success — surface backplate
<span class="fa-stack">
<i class="fa-solid fa-circle fa-stack-2x background"></i>
<i class="fa-solid fa-circle-check fa-stack-2x success"></i>
</span>
States, behaviours, and interactions
Icons inherit states (hover, focus, active, disabled) and behaviours / interactions from their parent component. Don't re-style icons individually — let the button, link, chip, or row that hosts them drive their colour and feedback.
App and Model Space Considerations
General app considerations
- Handling files generically: create new, open, see recent, save, save as, share, download, search, comment, see activity/details, export.
- Generic header: consistent use of the main app icon, primary icons and other elements; show submenus, validation or functions directly from the menu.
- In-flight validation: extending NOVA's framework — Incomplete / Info / Warning / Error / OK.
Modal space considerations
- Icon library: CAD's extensive icon library reflects desktop heritage; a reduced palette or thematic colours may suit a modern cloud platform while reusing Suite content for familiarity.
- Layer tree / object browser: common functions and layouts for hierarchical model objects — grouping, indenting, ghosting, visibility, row options.