Skip to content

Knoten Boards design system

Every colour, size and motion value below is read from the live stylesheets, so it cannot go stale. Every component of the site library follows, in each state it has.

Colour

The accent is ink. Each swatch shows its token and its value in the current theme. Switch the theme in the header and the values follow. The rules for using them and every contrast ratio are in the brand guide.

Type

The product scale from 13 to 28 px, and the two fluid display sizes the site adds above it. Nothing is set below 13 px, and reading text is 17 px.

Which style is used for which job is in the brand guide.

Space and radii

Each bar is drawn at the width of its token. How they are used is in the brand guide.

Motion

Motion moves transform and opacity only. A card glides to its new place; with reduced motion on, it fades in instead. The mark itself never moves. Its rules and a replay of the three speeds are in the brand guide.

Mark and wordmark

The mark is a cut-cornered square with one filled facet: of everything on the board, the one thing that just changed is the one that shows. The wordmark sets the name beside it in Schibsted Grotesk capitals.

mark() at sm, md, lg and xl. The sm size draws the small mark, without facet lines. Decorative unless a label is given; the last one carries a label.
wordmark() default. The link goes to the home page.
Long text: the name wraps beside the mark.

A link in running text is underlined in the ink line: the mark and wordmark. The line thickens on hover and focus.

Text link: an anchor without a class, underlined with --link-line at --link-thick.
Focus ring: --focus (ink), 2 px, clear of the control by --ring-gap.
InfoSuccessWarning
Info tone: a steel set (--i) drawn as a ring, where the state tones are filled dots.

Buttons and links

Controls come from the product: linkButton() draws a link as the product button, btn() and iconBtn() draw actions. At most one primary button sits in a view.

Default: primary, secondary and ghost.
Focus: a 2 px ring in the accent colour on every control.
Hover, on pointer devices only: the fill moves to the third surface colour. Primary moves to the second ink.
Disabled: dimmed, skipped by Tab and announced as disabled.
Long text: a 60 character label wraps inside the button.
Large, icon only and selected. Touch screens get 44 px targets.

Theme switch

themeSwitch() offers System, Light and Dark. The choice is kept in the browser and sent to every product frame on the page.

Default and selected: the pressed option shows the current choice.
Phone: the control keeps its width inside a 390 px column.

Code block

codeBlock() shows a labelled command with a Copy button. After a copy the button says Copied for two seconds and the result is announced to screen readers.

Default. This is the page's primary command, so the page menu offers to copy it.
Copy off: for output that is read, not run.
Long text: a line wraps at spaces only, never inside a word or a flag; one too long for the block scrolls inside it and never widens the page.

Product frame and view tabs

productFrame() holds a live product page. It pauses the page's scripted agent while the frame is off screen or the tab is hidden, starts it paused under reduced motion, and its bar holds the one button that pauses and plays it. viewTabs() switches the view inside the frame.

Selected and long text: the chosen tab is filled, a long label scrolls inside the row and never the page.
Long text, with the full screen link off. The label truncates in the bar.
Loading: the bar says "Loading the board" until the page inside has loaded.
Failed: after ten seconds without a load the frame says so and links to the page on its own.

Spec list, stat and steps

The page's reading blocks. A spec list replaces an icon card grid, a stat carries its source, and steps number an install sequence.

Spec list

specList() default.
Long text: a long term and a three line description.
Empty: says why and what comes next.
Code in a description: text between backticks is set in the mono face.

Stat

stat() default. A stat without a source throws, so no number can ship unsourced.
Long text: a long label and a wide value wrap.
With an id and no link: the figure is an anchor other stats link to, and its source text states the method.

Steps

step() default, in a list.
Long text: a long title and a three line body.
With a code block under the text. The block takes the same options as codeBlock().

Form fields

formField() draws a visible label, a control of an exact type and a message line that stays hidden until there is a problem and then says how to fix it.

formField() email: type email, autocomplete and a live message line.
formField() multiline: a textarea that grows with its column.

Navigation bar and footer

navBar() is sticky and shows its bottom hairline only once content scrolls under it, as this page's header does. Below 768 px the links move into a menu. footer() names the owner, links the same pages on every page with the current one marked, and, on a preview build, says the build is not public. hydrate() renders both from data attributes, as it renders every component on this page.

Default at desktop width, drawn with its hairline.
Phone: the links, the theme switch and the call to action live in the menu opened by the 44 px button. Esc closes it and focus returns to the button.
Long text: a long name is cut with an ellipsis so the bar keeps its height.
Footer default, on the home page: the same four links on every page, the current page marked and not a link. The preview line shows while the page says noindex.
Phone and long text.

Command menu, toast and version watch

commandMenu() opens on Ctrl or Cmd with K, or on the slash key. It lists this page's sections and a few actions, tolerates one typo, runs on Enter and closes on Esc. toast() speaks one quiet line; with an action it stays until dismissed. versionWatch() checks every five minutes while the page is visible and offers a reload when a newer build exists.

Default: opens with the first section selected. Try typing "colur" to see the typo tolerance, and an unmatched word for the empty result.
Default leaves by itself after four seconds. With an action it stays until dismissed.
Long text: the toast wraps and stays inside the screen.
The notice a page shows when ./version.json names a different build. A hidden page reloads at once instead.