The contract every layer above reads. Components never name a colour or a radius directly — they name a token, which is why a theme can rebind the whole system without touching markup, and why a palette can flip at runtime.
--mk-primary · --mk-secondary · --mk-primary-soft · --mk-on-primary · --ring
Two hues, ordered: --mk-primary is the one the interface is
built out of, --mk-secondary the one gradients and highlights
reach for. Both are held on :root as a light/dark
seed pair, so a nested dark region — an overlay header, a black
footer — resolves the dark half even on a light page.
--mk-on-primary is derived per seed rather than assumed
white, which is what stops a pale hue rendering white-on-white.
Mark ships no colour utilities. These tokens are wired to Bootstrap's own
--bs-primary-rgb and --bs-secondary-rgb, so
.text-primary, .bg-primary and
.border-primary already follow the palette and the mode —
one name per role. -light and -dark
modify a named hue; they never name one.
--mk-primary
The live half of the pair
--mk-primary-soft
A wash — .08 light, .16 dark
--mk-on-primary
Readable text ON the primary
--mk-secondary
The second hue — gradients, highlights
--ring — the focus ring every control shares:
.text-off-white · .bg-off-black · .link-off-white
Two colours that do not flip with the mode, for the places where
nothing is flipping: a control over a photograph has no ground to read,
because its ground is a picture. Bootstrap keys these
light and dark; here those two words are
already taken — they name the color mode — so
.text-light would read as “text for light mode”
and mean the opposite. They are off-white and
off-black, which is also what they are:
$gray-100 and $gray-900, a step off each.
The rename is a re-key of $theme-colors, so every family
Bootstrap generates from it follows — .btn-off-white,
.text-bg-off-white, .table-off-black,
--bs-off-white-rgb. The values did not move; only the keys.
Which leaves -light and -dark free for the only
job they should have: modifying a named hue
(primary-light). They never name one.
--bs-body-color · --bs-body-bg · --bs-border-color
The pair the base button is drawn in. Both flip with the mode, which is
the whole reason .mk-btn needs no variant to survive a dark
band — ink already knows where it is.
--bs-body-color
Ink
--bs-body-bg
Ground
--bs-border-color
The quiet line
--bs-border-radius-*
Five steps. Controls take the base, cards take lg; the
larger two are for full-bleed media and feature panels.
sm
.375rem
base
.625rem
lg
1rem
xl
1.25rem
xxl
1.75rem
$font-family-sans-serif · $display-letter-spacing · $eyebrow-letter-spacing
Inter over a system stack — no external request unless a theme adds a webfont. Tracking is two tokens pulling opposite ways, and the tension between them is a theme decision rather than a constant.
Display pulls tight
$display-letter-spacing: -.02em · weight 800 · line-height 1.15
Eyebrows push wide
$eyebrow-letter-spacing: .08em
Body sits at 1rem with a 1.65 line-height — long-form measure, not UI density.
$font-size-base: 1rem · $line-height-base: 1.65
$spacers → .p-*, .m-*, .gap-*
Bootstrap's scale with marketing-scale steps added — 5 and 6 are the band rhythm, which is why a section reads as a section without hand-tuned padding.
0
0
1
.25rem
2
.5rem
3
1rem
4
1.5rem
5
3rem
6
4.5rem