KC Design System — Transit / Periwinkle
Change here for everything.
Change here for everything.
One system, every component.
The transit style sets the system as a timetable in
design-system-transit.css:
Schibsted Grotesk, extra-bold and tight, a route line under the chrome, chips
with line roundels and cards hung from a coloured rule. Same components, same
markup — read off the platform board.
Reading in an unnamed pair — transit / periwinkle Change theme Browse all 8 →
01
Motion tokens & reveal
One easing and three durations — the scale that was in
00-tokens.css from the start and documented nowhere.
--ds-ease is a fast-out curve that spends most of its time
arriving; the durations name a kind of change rather than offering a range to
pick from. With them, the one opt-in that consumes them:
data-ds-reveal to fade-and-rise an element into view, or
data-ds-reveal="stagger" on a container to cascade its children.
One easing, three durations. Every
transition in the system uses --ds-ease,
a fast-out curve that spends most of its time arriving; the only things that do not
are meters and counters, which are linear because a percentage that eases is
reporting a feeling rather than a number. The three durations are not a scale to
pick from freely — each names a kind of change, and using the wrong one is
how a system stops feeling like one thing.
| Token | Value | What it is for |
|---|---|---|
--ds-ease | cubic-bezier(0.19, 1, 0.22, 1) | Everything. Composited into --ds-transition and --ds-transition-fast so a component states a duration, not a curve. |
--ds-dur-fast | 0.18s | A colour changing under the pointer: hover tints, border colours, the head's background. |
--ds-dur-base | 0.35s | A box changing size or position: the disclosure's 0fr → 1fr, a tab swap, a transform. |
--ds-dur-slow | 0.7s | Something leaving the screen entirely — the page entrance's halves parting, and little else. |
Nothing here is load-bearing without JavaScript and nothing here survives
prefers-reduced-motion.
The cancellations live together in
60-motion.css
rather than scattered through per-component media blocks, so “what stops
moving” is one file to read rather than forty to search — and an
animation added without a cancellation is visible as an absence there.
Scroll reveal. Add
data-ds-reveal to fade-and-rise
an element into view, or data-ds-reveal="stagger"
on a container to cascade its children.
Reveal one
Cascades in as the row enters the viewport.
Reveal two
A beat later than its neighbour.
Reveal three
And later still, a gentle stagger.
data-ds-reveal on a CONTAINER, not on each child
reveal.js walks the container's direct children and stamps each with
--reveal-delay: (index * 0.09)s
so a row of six staggers itself without six hand-written delays. The
transition itself is CSS; this only writes the offset.
It runs on ds-content-added as well as at boot, which is why a reveal
inside a disclosure works the first time that disclosure opens.
Under prefers-reduced-motion the whole thing is cancelled in
60-motion.css — one block, with every other cancellation, not a rule
next to this one.
02
Page entrance
The full-screen loader (.ds-loader), once per
session and never under reduced motion, in two variants — the counter, a
mono percentage over the ten-cell meter at splash scale, or the stamp, the KC
plate landing on the accent pop shadow every floating surface earns. Both leave
the same way: the halves part, as they always have. Foundation rather than a
component because it belongs to the site, not to anything on the page —
the other scales of the old waiting section are states of things, and went to
the things they are states of. Rebuilt from banter-loader.
The counter — the default.
A mono percentage over the ten-cell meter, at splash scale. There is no real
progress figure to report, so the count eases toward ninety and the last tenth
is spent by whatever actually finishes the load: the meter cannot fill and then
sit there, and it cannot claim done before done. Set with
ViewData["DsLoader"] = true.
The stamp — the alternative,
for a surface that wants the monogram rather than a number. The plate drops from
above scale, squares up and lands on the 8px accent pop shadow every floating
surface in the system earns, then breathes on the shadow while the page arrives
behind it. Add
ViewData["DsLoaderStyle"] = "stamp".
Both variants leave the same way — the halves part, as they always have.
Where it came from. The entrance is
the rebuilt banter-loader — nine bouncing boxes, once, before the
page. The boxes did not survive at page scale, but they were not thrown away
either: they are the three dots inside
.ds-btn.is-busy
over in Buttons & chips, kept alive at 7px. The full-screen loader is skipped
entirely under
prefers-reduced-motion,
which is the one scale where the reader has no way to have asked for the wait.
data-ds-loader-demo a FRAMED loader: runs in its box, never over the page
data-ds-loader-replay button that restarts the one it points at
data-ds-loader-count element that receives the counter
The live splash covers the viewport once per session and is gone. A
specimen of a thing that only happens on arrival has to be able to happen
again, which is what -replay is for and why it is markup rather than a
showcase-only script.
Under prefers-reduced-motion the count still counts and the wipe does
not: the cancellation is in 60-motion.css with the rest.
Markup
As rendered on the page behind this — captured, not transcribed.