Brand system

Download markdown

The working reference for everything Stitch puts in front of a person — the mark, the palette, the type, the surfaces that hold content, and the charts that carry the numbers. Light is the ground. Dark is opt-in. Ink is the voice; violet is an accent

Suisse Intl Ink accent Light & dark Get the assets

Colour palette

A greyscale system with three loud colours held in reserve. Ink does the work

Every swatch copies. Click any chip to put its hex on your clipboard.

Neutrals

Nine steps from white to ink. These flip between themes — the token stays, the value moves.

Accents

Fixed in both themes, and ranked by how much of the interface each one may touch. Width carries the rank — of eight columns, primary takes four, secondary two, and each tertiary one.

Primary

Secondary

Tertiary

ColourTokenWhat it means
Brand violet--brandHighlight fill in charts, focus ring, dark-mode toggle. Never type.
Amber--amberWarning, cost, lapse, needs action. Replaces red in the interface, and --danger now points at it.
Neon--signalPositive, live, on, protected. Type on neon is always --signal-ink.
Electric--electricCold stop on heatmap ramps. Nothing else.

Accent ramps

Every accent carries a dark end for type and a light end for fills. The base sits in the middle, and the two tertiary ramps share slate.

Primary

Indigo is --brand-ink, and it is type on lavender and mist only. On base violet it falls to 2.8:1, so type there stays --paper.

Secondary

Bronze is --amber-ink. It clears AA on amber itself at 5.03:1, where paper manages only 3.01:1 — so bronze, not paper, is the type on amber.

Tertiary — neon

Slate is --signal-ink, spring is the light tint. Spring sits at the same luminance as paper, so on the light stage it reads as hue alone and needs the inset hairline to hold an edge.

Tertiary — electric

The same slate opens the electric ramp, with ice as the cold tint.

InkTokenGoes onContrast
Indigo--brand-inkLavender, mist9.96 / 13.05
Bronze--amber-inkAmber, peach5.03 / 12.11
Slate--signal-inkNeon, spring, electric, ice14.03 / 15.53 / 12.79 / 14.87
Paper--paperBase violet only5.86

Surfaces

How the neutrals assemble into a screen.

TokenLightDarkWhere it goes
--bg--paper #fcfbfb--paper #0f0c15The stage. Everything sits on it.
--raised--white #ffffff--white #16131cCards and the sidebar. Solid, no blur.
--surface-glasswhite 62%white 68%Overlays, drawers, tooltips.
--chrome-glasswhite 78%white 84%Sidebar, rail, topbar, popovers.
--gridink 8%#f5f4f4 9%Chart gridlines and layout guides.
--linetransparenttransparentThere are no 1 px borders. Edges come from the inset hairline.

Colour rules

  • Ink is the voice. Primary buttons, selected thumbs, links and "ok" cells are all ink. A button is never --brand — see Buttons.
  • Selection is platinum, not ink. A selected card or an active nav item fills with --platinum, keeps its hairline and takes ink type. An ink fill at that size reads as a button.
  • Colour lives on fills, strokes and legend swatches. It reaches type only to correlate with a graph, or as --amber on a negative figure in a table — never on a chart title, a KPI value, or body copy.
  • Status is never colour alone. Every state carries a label.
  • Up is neon, warning is amber. No green, and no red at all — --danger resolves to --amber.

Status states

Up 12.4% Needs action Flat Selected
StateFillType
Up / ok / livesignal 55%--signal-ink
Down / warn--warn-s--amber
Flatink 6%--ink2
Selected--ink--ink-reversed

Typography

Suisse Intl, and only Suisse Intl. One family for display, interface, body and numerals

The family

Four licensed cuts, and no separate mono — Suisse's tabular figures do that job. Weight 500 is aliased to Regular: no Medium file exists, and synthesising one is not permitted.

Aa
Light Weight 300
Aa
Regular Weight 400, aliased to 500
Aa
Semibold Weight 600
Aa
Black Weight 900

Character set

ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz

0123456789

R $ € £ & @ # % · ()[]{} — – - .,:;!?'"

Typescale

Body is 16 px with -0.011em tracking. Tracking tightens as size grows and opens up for small caps-style labels.

Hero600 · −0.05em
One platform for every payment
Page title1.75rem · 600 · −0.03em
Collections overview
Section head1.75rem · 600 · title + headline
Collections overview Where the money lands, by bank and by hour
Section title1.35rem · 600 · −0.03em
Where volume comes from
Subhead1.25rem · 600 · −0.02em
Approved colour usage
Card title1rem · 600 · −0.02em
Acceptance rate by bank
KPI value2.5rem · 400 · −0.02em · lining
R 1 284 990
Lede1.125rem · 400 · −0.011em
The working reference for everything Stitch puts in front of a person
Body1rem · 400 · −0.011em
Settlement lands the next business day for every bank on the network.
Label / eyebrow0.75rem · 600 · 0.02em
Gross processed volume
Table header0.6875rem · 600 · 0.06em
Merchant · Volume · Share
Button0.75rem · 600 · −0.02em
Create a payment link
Chart axis10.5px · 400
Jan Feb Mar Apr May Jun — 0 25 50 75 100%

Colour in type

Four levels of ink, and nothing else. Type never takes a brand colour.

Primary

--ink · 100%

Headings, KPI values, table first column, active nav.

Secondary

--ink2 · ink 62%

Body copy, descriptions, labels, table cells.

Tertiary

--ink3 · steel / titanium

Table headers, axis ticks, placeholders, captions.

Muted

--muted · ink 45%

Section headlines only, at 24 px and up. It clears AA for large type at 3.05:1, so it never carries body copy.

Type rules

  • Tabular numerals where figures stack. Anything read down a column — tables, chart values — carries tabular-nums. KPI values are lining figures.
  • One family. Suisse Intl carries display, UI, body and code.
  • Emphasis is 600. Never 900. Black is a display weight — it belongs at hero and page-title size, never inside a sentence.
  • Sentence case for headings and buttons. Uppercase is for labels, eyebrows and table headers only.
  • No full stop on a headline. Titles, section headlines, ledes, labels and button text end without one. A period closes a thought; a headline is meant to stay open. Internal punctuation is fine — it is only the last character that goes.
  • Measure caps at 62 ch for body copy.

Never

  • Violet, amber or neon type. Colour is a fill, not a voice.
  • A synthesised Medium, Italic or Bold — only the four licensed cuts.
  • font-weight: bold or 700. There is no 700 cut, so the browser resolves it upward to Black. Always write 600.
  • Aeonik, Neue Rational, Reckless, or a system fallback in shipped work.
  • Tabular figures in running text. Even widths are what make a column align; in a sentence they just leave gaps.
  • Letter-spacing on body copy beyond the -0.011em default.
  • Underlines on anything but inline links.

Download

Web cuts for anything that renders in a browser; desktop cuts for Figma, Illustrator and Keynote.

Suisse Intl LightWeight 300
Suisse Intl RegularWeight 400, aliased to 500
Suisse Intl SemiboldWeight 600 · headings
Suisse Intl BlackWeight 900 · display only
@font-face {
  font-family: 'Suisse Intl';
  src: url('./fonts/SuisseIntl-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
/* No Medium cut is licensed — alias 500 to Regular. */
@font-face {
  font-family: 'Suisse Intl';
  src: url('./fonts/SuisseIntl-Regular.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}

body {
  font-family: 'Suisse Intl', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 16px;
  letter-spacing: -0.011em;
}

/* figures read down a column, and nowhere else */
table,
.bar__val {
  font-variant-numeric: tabular-nums;
}

Licensing. Suisse Intl is licensed from Swiss Typefaces. Load the web cuts with @font-face from your own origin — do not ask people to install the desktop cuts to view a page, and do not redistribute either set outside Stitch.

Cards and the inset shadow

Stitch surfaces have no borders. An edge is a hairline pressed into the inside of the shape

The recipe

Two inset shadows, 0.75 px each, on opposite diagonals — light catching the top-left, shadow settling into the bottom-right. It reads as a physical lip rather than a drawn line, and it survives on glass where a 1 px border would not. The colour is ink at 20% so it holds in both themes.

Exaggerated 4× The hairline

Blown up so you can see which edge lights and which edge sinks.

At production scale the effect is almost subliminal — that is the point. It defines the shape without adding a line to the composition.

:root {
  --inset-edge: color-mix(in srgb, var(--ink) 20%, transparent);

  --inset:
    inset -0.75px -0.75px 0.5px 0 var(--inset-edge),
    inset  0.75px  0.75px 0.5px 0 var(--inset-edge);

  --inset-strong:
    inset -0.75px -0.75px 0.5px 0 color-mix(in srgb, var(--ink) 30%, transparent),
    inset  0.75px  0.75px 0.5px 0 color-mix(in srgb, var(--ink) 30%, transparent);
}

.card--glass {
  background: var(--surface-glass);
  backdrop-filter: blur(20px) saturate(140%);
  border-radius: 1.5rem;
  box-shadow: var(--inset);
  border: 0;
}

Asset cards

For specimens: a mark, a colour, a type cut. The plate is full-bleed and carries the card's own radius, so the card shows through at its two lower corners.

Title Subtitle, in --muted
.asset {
  position: relative;
  border-radius: var(--r);
  background: var(--raised);
}

/* Hairline above the plate, not behind it. */
.asset::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--inset);
  pointer-events: none;
}

.asset__plate {
  width: 100%;
  aspect-ratio: 3 / 2;
  max-height: 220px;
  border-radius: var(--r);
  overflow: hidden;
}

.asset__label {
  padding: 1.1rem 1.25rem 1.25rem;
  line-height: 1.3;
}
  • Plate radius equals card radius — both --r. The lower corners curve inward and reveal the card.
  • The plate is flush to the card's top, left and right. Only the label block is padded.
  • The inset hairline is drawn on top by ::after, so it reads across the plate edge rather than hiding behind it.
  • Label is a two-line pair — title in --ink over subtitle in --muted, same size, no gap. The same pairing as a section head.
  • The ink plate carries its own hairline at paper 16%, because ink on a carbon card has no edge in dark mode. The paper plate carries none.

Two tiers

The tier is decided by what the surface is for, not by how it looks.

Tier 1 — content card

No shadow
Gross volume R 4.8m Up 12.4%

Solid --raised--white in light, --carbon in dark. No blur, no border. It reads as a sheet laid on the --paper stage, and content sits directly on it.

Tier 2 — chrome card

Inset hairline
Filters Last 30 days

Glass fill, blurred backdrop, hairline edge.

Glass fill plus the inset, for surfaces that float over content: popovers, drawers, tooltips, buttons, chips and controls. The sidebar and topbar are chrome but sit beside content rather than over it, so they take the solid --raised fill and keep only their directional hairline.

Directional insets

Chrome that meets the viewport only takes the hairline on the edge that faces content.

SurfaceInsetDeclaration
Card, popover, drawer, tooltipAll four edgesvar(--inset)
SidebarRight onlyinset -0.75px 0 0.5px 0 var(--inset-edge)
RailRight onlyinset -0.75px 0 0.5px 0 var(--inset-edge)
TopbarBottom onlyinset 0 -0.75px 0.5px 0 var(--inset-edge)
Table headerBottom onlyinset 0 -0.75px 0.5px 0 var(--inset-edge)
Selected table rowLeft, 3 pxinset 3px 0 0 0 var(--ink)

Radius and spacing

Four radii. Nothing in between.

24 px

--r · cards, drawers, overlays, the composer.

16 px

--r-m · a card inside a card, and the demo surfaces in this guide.

12 px

--r-s · inner plates, inputs, code blocks, media.

999 px

Pills · buttons, chips, toggles, legend swatches, bar tracks.

States

The hairline strengthens on hover. Selection swaps the fill to platinum and keeps the hairline — an ink fill would read as a button, and a selected card is not something you press.

Rest

--inset · ink 20%

Hover

--inset-strong · ink 30%

Selected

--platinum fill, ink type

Focus

2 px --brand, 2 px offset

What the inset replaces

Side by side against the conventions it exists to avoid.

1 px border

Avoid A hard line competes with content and breaks over glass.

Drop shadow

Avoid Reserved for tooltips and the Ask bubble. Never on a card.

Inset hairline

Use Defines the edge from inside. Works on any fill, in either theme.

Layout note. Never leave a hole larger than about 48 px under a card's last child. If a card looks empty at the bottom, the padding is wrong or the content belongs somewhere else.

Buttons

Ink for the interface, amber and neon for the call to action. Violet is never something a person presses

The four

One interface button, two CTAs and one secondary. Everything else is a size or an icon variant of these.

Fills and labels

Ink, amber, neon, hollow
ButtonFillLabelWhere it goes
Primary--ink--ink-reversedEvery action inside the product. This is the default and it covers most cases.
CTA — B2B--amber--ink-reversedMarketing calls to action aimed at businesses.
CTA — B2C--signal--inkMarketing calls to action aimed at consumers.
SecondaryNone--inkHollow, carrying only the inset hairline. Pairs with any of the three above.
  • Never --brand. Violet is the chart highlight and the focus ring. It is not a button fill, in either theme.
  • A CTA is not an interface button. Amber and neon belong on marketing surfaces. Inside the product, the primary action is ink.
  • One CTA to a view. Two competing fills means neither is the call.
  • No third label colour. A button label is --ink or --ink-reversed. Not muted, not an accent, not a ramp ink.

A label is only ever --ink or --ink-reversed. The two are a mirror — #0f0c15 and #f5f4f4 — and they swap places with the theme, so the primary button is dark on light and light on dark at 17.65:1 either way. Amber and neon hold the same value in both themes, so their labels are frozen at the light-mode pair rather than swapping: --ink on neon at 16.92:1, --ink-reversed on amber at 2.83:1.

Chips

A short categorical label in a pill of air. No fill, no colour — just the inset hairline and ink

Anatomy

A chip is transparent. The edge is the same inset hairline that defines a card, bent around a pill, so a row of chips reads as structure rather than as buttons.

Standalone

Under a label, in a content column

Products live & launching

Pay by bank Card Payouts Refunds

Card footer

Pinned to the bottom, one step quieter

Chips in a card sit last and stay last, so a row of cards lines its chips up on one baseline no matter how much copy sits above them.

Slack Snowflake Xero

The rules

Inherited wholesale from Kantha. The chip is a label, not a control — nothing about it should invite a press.

PropertyRule
ShapePill, 999px
Fillnone
Edgevar(--inset)
Type0.75rem, weight 600, --ink
Type as a card footerSteps down to 0.6875rem on .card--chips
Layout.chips is a wrapping flex row
  • Never a filled chip. No --brand, no --amber, no neon. The moment a chip takes a fill it reads as a CTA.
  • Chip type is always --ink. Not muted, not an accent. A chip that needs a colour is a pill.
  • Labels, not sentences. A few words. Products, channels, tags, categories.

The hairline is --inset-edge, ink at 20%, so it inverts with the theme and the chip holds its edge on either ground without ever carrying a fill.

Chip or pill?

They look alike and they are not interchangeable. The difference is whether the label carries a state.

Chip

Pay by bank Payouts

Neutral and countable. A chip names a thing. Every chip in a row carries the same weight, so the set reads as a list.

Pill

Use Avoid Draft

Carries a state. A pill takes a fill and a colour precisely because it is saying something about status, so it is never neutral.

Markup

<p class="chips__head">Products live &amp; launching</p>
<div class="chips chips--tight">
  <span class="chip">Pay by bank</span>
  <span class="chip">Card</span>
</div>

<!-- as a card footer -->
<div class="card card--chips">
  …
  <div class="chips"><span class="chip">Slack</span></div>
</div>

Data visualisation

Charts sit on white cards on the paper stage. Colour lives on fills and strokes, and reaches type only where it has to tie back to a graph

Chart ramps

Two scales, seven steps each. Violet for categories that need ranking or mix; ink-grey for quiet series. Both invert between themes on their middle step, so a step means the same thing in either one — index 0 always carries, index 6 is always the quietest.

Categorical — violet

--c0 … --c6

Channels, payment methods, stacked areas. The brand ramp interpolated to seven steps, from --indigo through --brand to --mist, pivoting on --c3. Series should be drawn from --c1 to --c4; --c0 is the extreme and is held back for emphasis, and the last two steps are too close to the stage to carry a series on their own.

Sequential — ink

--s0 … --s6

Greyscale series where nothing needs to shout. Seven neutrals from --ink to --silver, with --carbon between --ink and --graphite, pivoting on --s3. The tail sinks into the surface on purpose — in dark mode --s5 is the card fill and --s6 is the stage, so they read as absence rather than as a step. Default bar fill is --silver — a bar only takes colour when it is making a point.

The fade

The one rule that makes a Stitch chart recognisable. Violet and amber fills ramp from full opacity to 15%. Direction depends on the shape.

Bars and pillars — solid at the tip

Card48%
Cost of acceptance2.1%

The value end is solid; the origin dissolves. A horizontal bar growing right uses a 270deg gradient.

Areas — solid at the series

Under a line the fade runs the other way — full opacity where it meets the stroke, 15% at the baseline.

Do not fade line strokes, legend swatches, ring segments, scatter bubbles or cohort cells. They stay solid.

Horizontal stat bars

Track 7–9 px, pill radius. Default fill is --silver — a bar only takes colour when it is making a point.

Volume by payment method

Last 30 days
Pay by bankR 2.41m
CardR 1.38m
Capitec PayR 0.86m
EFTR 0.31m

The 1.5 px tick on the first bar is a reference marker — last period, target, or category median.

Bar variants

VariantFillUse
Default--silverQuiet comparison
Violet--brand fadeThe series in focus
Amber--amber fadeCost, warning, lapse
Steel--s3 / --steelLong tail, "other"
Ranked--bar-fade by val/maxOrdered league tables

Vertical pillars and lines

Pillars take rx="3" and fade top to base. Lines are 2 px with round caps and joins.

Transactions per month

Thousands

Acceptance rate

Two series

Composition

Stacked areas and rings both draw from the categorical ramp. On a stack, only the bottom band fades.

Channel mix

100% stacked

Settlement split

Solid strokes

Change over a period

Waterfalls and divergence bars both compare against a baseline rather than a total.

Volume bridge

Q1 → Q2

Where it sells

Online lean vs in-store

Density and correlation

Heatmaps ramp electric → neon → amber with opacity from 10% to 100%. Cohort cells quantise onto the categorical ramp. Scatter bubbles stay solid.

Volume by hour

Mon–Sun
QuietPeak

Retention by cohort

Month 0–7

Retention quantises onto the categorical ramp — --c0 at month 0, --c6 once a cohort is spent. Type is picked per cell against the fill, so the grid inverts cleanly with the theme.

Volume against margin

Four quadrants

Chart rules

  • Grey bars are silver, and silver goes darker in dark mode.
  • Filled violet and amber fade 100% → 15%. Bars solid at the tip, areas solid at the series.
  • Rings, bubbles, legend chips and lines stay solid.
  • Legend swatches are pills matching the series before any fade is applied.
  • Axis ticks are --ink3 at 10.5 px, gridlines are --grid.

Colour on type

Type is ink by default. Colour reaches it in two places only — a label that has to be read against a graph, and a negative figure in a table.

Settlement by region

Change on Q1
RegionVolumeChange
South AfricaR 4.82 bn+12.4%
NigeriaR 1.19 bn−3.8%
KenyaR 840 m+6.1%
GhanaR 312 m−18.2%

Negatives are --amber and keep their minus sign. Amber on a white card is 3.11:1, so the sign is what carries the meaning — the colour only makes the column scannable.

When type takes colour

  • A label read against a graph. A series name, an inline value, a legend label — anything the eye has to match to a fill or a stroke takes that series colour.
  • A negative value in a table. --amber, with the minus sign kept. The one place colour lands on type with no chart beside it.
  • Chart titles, KPI values and body copy. Nothing correlates to these, so colour on them is decoration.
  • Violet type, anywhere. Brand is a fill and a focus ring. It has no voice, so it never becomes a letterform — use the series colour from the ramp instead.
  • Colour as the only signal. A red-green delta with no sign and no label is unreadable to a third of people. There is no green here anyway.

Downloads

Everything in this guide, packaged — with the written specifications alongside as markdown

Logo pack

Wordmark, symbol, reversed and single-colour variants in SVG, PNG, PDF and AI.

stitch-logos.zip

Type pack

Suisse Intl in four cuts — WOFF2 for web, OTF and TTF for desktop.

stitch-fonts-suisse-intl.zip

Written spec

The same rules as markdown, structured for repos and agent context.

BRAND-SYSTEM.md

Slide template

Grid, layouts and data-vis rules for the deck template, as markdown.

PRESENTATIONS.md

Token sheet

Drop into any stylesheet
:root {
  --white: #ffffff;   --paper: #fcfbfb;     --platinum: #f5f4f4;
  --silver: #dddcdd;  --titanium: #b5b3b6;  --steel: #56545a;
  --graphite: #2e2b33; --carbon: #16131c;    --ink: #0f0c15;

  --brand: #6e2cff;   --amber: #ff5b00;     --signal: #daff35;
  --electric: #59fcf9;    --danger: var(--amber);

  /* accent ramps */
  --indigo: #221244;  --lavender: #c4c0ff; --mist: #e3deff;
  --bronze: #3f1711;  --peach: #fddcc9;
  --slate: #0e242c;   --spring: #f8ffda;  --ice: #d1fffe;

  /* type on an accent */
  --brand-ink: var(--indigo); --amber-ink: var(--bronze); --signal-ink: var(--slate);

  --bg: var(--paper);      /* the stage */
  --raised: var(--white);  /* cards and the sidebar */

  --ink-reversed: #f5f4f4;  /* mirrors --ink; swaps in dark */
  --ink2: color-mix(in srgb, var(--ink) 62%, transparent);
  --ink3: var(--steel);
  --muted: color-mix(in srgb, var(--ink) 45%, transparent);
  --grid: color-mix(in srgb, var(--ink) 8%, transparent);
  --inset-edge: color-mix(in srgb, var(--ink) 20%, transparent);

  --r: 1.5rem;  --r-m: 16px;  --r-s: 12px;
  --sans: 'Suisse Intl', -apple-system, BlinkMacSystemFont, sans-serif;
}