Components

Every kelin token and component in both themes. Static HTML and CSS; scripts only where the platform has no native answer.

Colour

Every colour token in Paper and Ink. Alpha colours are shown over both page and surface grounds.

Paper

Grounds

Page ground and docked chrome.

--bgbg-bg
--sidebar-bgbg-sidebar-bg

Surfaces

Floating sheets, sunken wells, hover and selected fills.

--surfacebg-surface
--surface-sunkenbg-surface-sunken
--surface-hoverbg-surface-hover
--surface-activebg-surface-active

Borders

Inside panels, around panels, and around controls.

--border-subtleborder-border-subtle
--borderborder-border
--border-strongborder-border-strong

Text

Primary, secondary and tertiary text (ink tokens).

--inktext-ink
--ink-mutedtext-ink-muted
--ink-fainttext-ink-faint

Accent face

Primary key face, hover, lip and text. Fill only, not body text.

--accentbg-accent
--accent-hoverbg-accent-hover
--accent-edgeborder-accent-edge
--on-accenttext-on-accent

Accent ink

Links, current navigation and selected checks.

--accent-inktext-accent-ink

Info

Info ground, text and border. Use accent-ink for the status icon.

--accent-softbg-accent-soft
--accent-soft-inktext-accent-soft-ink
--info-borderborder-info-border

Success

Status mark, ground, text and border.

--successbg-success
--success-softbg-success-soft
--success-inktext-success-ink
--success-borderborder-success-border

Warning

Status mark, ground, text and border.

--warningbg-warning
--warning-softbg-warning-soft
--warning-inktext-warning-ink
--warning-borderborder-warning-border

Danger

Status tone and danger key face, hover, lip and text.

--dangerbg-danger
--danger-hoverbg-danger-hover
--danger-edgeborder-danger-edge
--on-dangertext-on-danger
--danger-softbg-danger-soft
--danger-inktext-danger-ink
--danger-borderborder-danger-border

Secondary key

Face, hover, lip, latched face and bevel.

--btn-facebg-btn-face
--btn-face-hoverbg-btn-face-hover
--btn-edgeborder-btn-edge
--btn-face-downbg-btn-face-down

bg

surface

--btn-highlightbg-btn-highlight

Switch

On track, border and thumb lip.

--switch-onbg-switch-on
--switch-on-edgeborder-switch-on-edge

Focus

Keyboard focus ring.

--focusoutline-focus

Tooltip

Inverted ground and text.

--tooltip-bgbg-tooltip-bg
--tooltip-inktext-tooltip-ink

Overlay

Modal scrim over each ground.

bg

surface

--overlaybg-overlay

Shadow colours

Alpha colours used by shadow utilities, shown over each ground.

bg

surface

--shade-raisedshadow-raised

bg

surface

--shade-popover-rimshadow-popover

bg

surface

--shade-popovershadow-popover

bg

surface

--shade-modalshadow-modal

bg

surface

--shade-wellinset-shadow-well

bg

surface

--shade-pressinset-shadow-press

Ink

Grounds

Page ground and docked chrome.

--bgbg-bg
--sidebar-bgbg-sidebar-bg

Surfaces

Floating sheets, sunken wells, hover and selected fills.

--surfacebg-surface
--surface-sunkenbg-surface-sunken
--surface-hoverbg-surface-hover
--surface-activebg-surface-active

Borders

Inside panels, around panels, and around controls.

--border-subtleborder-border-subtle
--borderborder-border
--border-strongborder-border-strong

Text

Primary, secondary and tertiary text (ink tokens).

--inktext-ink
--ink-mutedtext-ink-muted
--ink-fainttext-ink-faint

Accent face

Primary key face, hover, lip and text. Fill only, not body text.

--accentbg-accent
--accent-hoverbg-accent-hover
--accent-edgeborder-accent-edge
--on-accenttext-on-accent

Accent ink

Links, current navigation and selected checks.

--accent-inktext-accent-ink

Info

Info ground, text and border. Use accent-ink for the status icon.

--accent-softbg-accent-soft
--accent-soft-inktext-accent-soft-ink
--info-borderborder-info-border

Success

Status mark, ground, text and border.

--successbg-success
--success-softbg-success-soft
--success-inktext-success-ink
--success-borderborder-success-border

Warning

Status mark, ground, text and border.

--warningbg-warning
--warning-softbg-warning-soft
--warning-inktext-warning-ink
--warning-borderborder-warning-border

Danger

Status tone and danger key face, hover, lip and text.

--dangerbg-danger
--danger-hoverbg-danger-hover
--danger-edgeborder-danger-edge
--on-dangertext-on-danger
--danger-softbg-danger-soft
--danger-inktext-danger-ink
--danger-borderborder-danger-border

Secondary key

Face, hover, lip, latched face and bevel.

--btn-facebg-btn-face
--btn-face-hoverbg-btn-face-hover
--btn-edgeborder-btn-edge
--btn-face-downbg-btn-face-down

bg

surface

--btn-highlightbg-btn-highlight

Switch

On track, border and thumb lip.

--switch-onbg-switch-on
--switch-on-edgeborder-switch-on-edge

Focus

Keyboard focus ring.

--focusoutline-focus

Tooltip

Inverted ground and text.

--tooltip-bgbg-tooltip-bg
--tooltip-inktext-tooltip-ink

Overlay

Modal scrim over each ground.

bg

surface

--overlaybg-overlay

Shadow colours

Alpha colours used by shadow utilities, shown over each ground.

bg

surface

--shade-raisedshadow-raised

bg

surface

--shade-popover-rimshadow-popover

bg

surface

--shade-popovershadow-popover

bg

surface

--shade-modalshadow-modal

bg

surface

--shade-wellinset-shadow-well

bg

surface

--shade-pressinset-shadow-press

Typography

Iosevka for the interface, IBM Plex for reading content and display. Type styles set size, line height and tracking; weight is separate.

text-display-xl font-serif56/60 px · Hero, one per page.

Ship calm tools

text-display font-serif40/46 px · Page titles.

Build history

text-title font-serif28/34 px · Marketing card titles.

Branches that stay in sync

text-heading font-semibold20/28 px · In-app headings.

Deploy channels

text-subheading font-semibold16/24 px · Modal titles, group headings.

Rename branch

text-body15/24 px · Running copy.

No builds yet. Push a branch to start the first one.

text-body-sm13/20 px · Default UI.

Build 412 finished in 2m 14s on main.

text-label font-medium13/18 px · Buttons, labels, nav links.

Run task

text-caption12/16 px · Hints, timestamps, tooltips.

Updated 4 minutes ago

text-code font-mono13/20 px · Code, paths.

src/main.rs:42

text-eyebrow font-mono font-medium uppercase11/16 px · Sidebar sections, menu groups. Sparingly.

Channels

text-kbd font-mono font-medium11/16 px · Shortcuts.

⌘K

text-content-h1 font-serif font-medium32/38 px · Content title.

Release notes

text-content-h2 font-serif font-medium23/30 px · Content sections.

Faster incremental builds

text-content-h3 font-content font-semibold18/25 px · Content sub-sections.

Cache keys

text-content-lead font-content text-ink-muted18/28 px · Standfirst.

kelin now reuses build outputs across branches.

text-content-body font-content16/26 px · Reading.

Each task hashes its inputs, so unchanged steps finish at once.

text-content-small font-content14/22 px · Tables.

Cache hit rate rose from 41% to 87%.

Spacing

Tailwind's 4px grid. Each step has one job; control heights stay on the same grid.

Steps

0.52pxHairline gaps, title to text.
14pxMenu padding.
1.56pxIcon gap in buttons.
28pxDefault inner gap.
312pxButton and sheet padding.
416pxNavbar padding, control gaps.
520pxMarketing panels.
624pxSections in panels.
832pxPage sections.
1248pxWide gutters.
1664pxMarketing rhythm.

Heights

h-6.526px · Small controls.
h-728px · Menu and sidebar rows.
h-832px · Default controls.
h-1040px · Large controls.
h-1352px · Navbar.

Radius

Small, square-leaning corners. Never pills.

rounded-xs2px · Kbd, tiny badges.
rounded-sm4px · Menu items, tooltips, small controls.
rounded-md6px · Buttons, fields, sidebar items, sheets.
rounded-lg8px · Marketing only.
rounded-xl12px · Marketing only.
rounded-full50% · Dots, avatars. Never pills.

Shadows

Only floating things cast shadows; app panels use borders. Shadow colours change with the theme.

Paper

shadow-raisedActive sidebar or nav tile, marketing cards.
shadow-popoverMenus, popovers, select lists, toasts.
shadow-modalModals.
inset-shadow-wellSunken fields.
inset-shadow-pressHeld or latched key. Transparent in Ink.

Ink

shadow-raisedActive sidebar or nav tile, marketing cards.
shadow-popoverMenus, popovers, select lists, toasts.
shadow-modalModals.
inset-shadow-wellSunken fields.
inset-shadow-pressHeld or latched key. Transparent in Ink.

Depth

Keys stand on a hard lip. Pressing moves the face down by the lip height; latched keys stay down on a 1px lip. Layout never shifts.

Anatomy, md key at 3×

Face · h-8, 32px
Lip · --depth-md, 3px
Rest
Travel · --depth-md, 3px
Held
Travel · depth − 1px, 2px
Lip · 1px
Latched

Paper

secondary

RestHeld (:active)Latched--depth-sm · 2px--depth-md · 3px--depth-lg · 4px

primary

RestHeld (:active)Latched--depth-sm · 2px--depth-md · 3px--depth-lg · 4px

Ink

secondary

RestHeld (:active)Latched--depth-sm · 2px--depth-md · 3px--depth-lg · 4px

primary

RestHeld (:active)Latched--depth-sm · 2px--depth-md · 3px--depth-lg · 4px

Button

Raised keys on a visible lip. Press travels down; latched keys stay down.

secondary

primary

danger

ghost

Icon-only with tooltip

Open settingsOpen settingsOpen settingsRun task⌘RBold

Button group

Keys joined edge to edge. As a view switch, native radios keep exactly one view latched; arrow keys move the selection.

Actions

View switch

View switch, icon-only

View switch with disabled option

Formatting toggles (multi-select)

Toggle button

A key that latches and stays down. Built on a native checkbox, so screen readers announce checked or not checked; no JavaScript.

secondary

primary

danger

ghost

Icon-only

Theme toggle

Flips the page between Paper and Ink. Every toggle on the page shares one state, saved on this device.

Switch (navbar)

Switch to dark

Segmented (settings)

Close

Small ghost square with a bold x. Dismisses popovers and dialogs through native attributes.

Default

In a sheet header

Build finished

release/2.4 passed in 4m 03s.

Live: closes a popover

Build cache

2.1 GB across 38 branches.

Ink theme

Text field

Sunken well for typing. Native constraints validate on blur, then live while you fix the value.

Sizes

Lowercase letters, numbers, dashes and slashes.

Lowercase letters, numbers, dashes and slashes.

Lowercase letters, numbers, dashes and slashes.

Live validation (blur to check)

Lowercase letters, numbers, dashes and slashes.

Use lowercase letters, numbers, dashes and slashes.

Enter an email address, like you@kelin.dev.

Receives a POST for every finished build.

Use an https:// URL.

Valid HTTPS URL.

States

Lowercase letters, numbers, dashes and slashes.

Use lowercase letters, numbers, dashes and slashes.

Valid HTTPS URL.

Set when the project was created.

Only admins can change this.

Counter

First line of the squash commit.

36 / 72
20 / 206 / 16

Small states

Use a port from 1 to 65535.

Host responds.

Password field

Text field with a show/hide toggle, a Caps Lock warning and, for new passwords, a strength meter.

Sizes

New password strength

Use 12 or more characters.

Too short — use 12 or more characters.

States

Use 12 or more characters.

Copy field

Read-only value to take elsewhere. Focus selects it all; the copy key always copies the real value.

Plain

Copy webhook URL

Paste into your repository settings.

Copy clone URL

Masked secret

Copy API token

Treat it like a password.

Copy API tokenCopy runner PIN

Short secrets still show eight stars.

Copy button

Key that copies a value. Shows Copied for 1.6 seconds, or Copy failed when the clipboard is unavailable.

secondary

Copy commit SHACopy commit SHACopy commit SHA

primary

Copy commit SHACopy commit SHACopy commit SHA

danger

Copy commit SHACopy commit SHACopy commit SHA

ghost

Copy commit SHACopy commit SHACopy commit SHA

Feedback states

CopiedCopy failedCopied

Disabled

Copy commit SHA

Select

Native select in the text field well. Supporting browsers show a styled list with checks, icons and descriptions; others show the native list.

Sizes

Options

Descriptions show only in the styled list.

Long lists scroll after 264px.

Required before the first deploy.

Choose a region to deploy to.

With icons

Icons show only in the styled list and trigger.

Choose an environment to promote to.

States

Choose a region to deploy to.

Supported until 2028.

Locked by the project template.

Switch

For settings that apply at once. The thumb is a small key that slides onto the blue track.

sm

Deploy every push to main.Post results to #builds.

md

Deploy every push to main.Post results to #builds.

lg

Deploy every push to main.Post results to #builds.

Disabled

Set by your organization.Set by your organization.

Tooltip

Inverted label after 350ms of hover or keyboard focus; closes at once. Icon-only keys use an aria-label equal to the text; text keys point at the bubble with aria-describedby.

Top (default)

Copy linkRun task⌘RPin channel

Right (collapsed sidebar)

BuildsSearch⌘K

Text trigger with aria-describedby

Deploys release/2.4 to stagingFetch all remotes⇧⌘F

Static bubble

Copy link

Popover

Anchored sheets on the native Popover API. 6px from the trigger, below the lip of a raised key; flips when it would overflow.

Live placements

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

Live with actions

Share channel

Anyone in kelin-web can open #design with this link.

Build cache

214 MB across 38 entries. Entries older than 7 days are removed.

Runners

3 of 4 online. ci-03 is offline.

Static sheets

feat/sidebar

12 commits ahead of main. Last push 4 minutes ago.

Share channel

Anyone in kelin-web can open #design with this link.

Alert

In-flow status sheets. Every tone pairs an icon with a title, never hue alone.

Tones

Deploy queued

kelin-web is third in the queue. The build starts in about 2 minutes.

Build passed

main@4f2c1e built in 48s. All 212 tests passed.

Runner almost full

ci-03 has 1.2 GB left. Old caches are removed at 500 MB.

Build failed — error in src/main.rs:42

Expected `;`, found `}`. Fix the error, then push again.

Branch renamed

feature/sidebar is now feat/sidebar. Update your local remote.

Title only

Deploy queued

Build passed

Runner almost full

Build failed — error in src/main.rs:42

Branch renamed

With actions and close

Runner almost full

ci-03 has 1.2 GB left. Clear old caches to keep builds running.

New channel layout

Channels now group by project. Static specimen: dismissal is wired by the app.

Toast

Floating status sheets, bottom-right. Info, success and neutral close after 5s (hover pauses); warning and danger stay. Three at most, newest at the bottom.

Live

Draft saved

Release notes for v0.14 are saved locally.

Link copied

Anyone in kelin-web can open this channel.

Build passed

main@4f2c1e built in 48s.

Runner offline

ci-03 stopped responding. Jobs wait for another runner.

Build failed — error in src/main.rs:42

Fix the error, then push again.

Channel archived

#design moved to the archive.

Tones (static)

Draft saved

Release notes for v0.14 are saved locally.

Link copied

Anyone in kelin-web can open this channel.

Build passed

main@4f2c1e built in 48s.

Runner offline

ci-03 stopped responding. Jobs wait for another runner.

Build failed — error in src/main.rs:42

Fix the error, then push again.

With action (static)

Channel archived

#design moved to the archive.

Badge

Small status labels with square corners, never pills. Status always carries text, not hue alone.

sm · text

DraftQueuedPassedFlakyFailed

md · text

DraftQueuedPassedFlakyFailed

sm · dot

DraftQueuedPassedFlakyFailed

md · dot

DraftQueuedPassedFlakyFailed

sm · icon

DraftQueuedPassedFlakyFailed

md · icon

DraftQueuedPassedFlakyFailed

In context

release/2.4Passed

fix/socket-timeoutFailed

Ink theme

DraftQueuedPassedFlakyFailed

Dot

6px status mark. Always paired with a label; hide the label visually only where the row already explains it.

States with label

IdleRunningDonePendingFailed

Visually hidden label

  • Runningmain1m 12s
  • Donerelease/2.44m 03s
  • Failedfix/socket-timeout2m 41s
  • Pendingfeat/channel-search—

Ink theme

IdleRunningDonePendingFailed

Card

Raised sheets for marketing grids; flat hairline panels inside the app. A link card is one target from edge to edge.

Default

Pipelines

Fast builds

Every push runs on a warm cache. Most branches report back in under two minutes.

Channels

Talk about a branch where the code lives. Threads follow the build, not the calendar.

Getting started

Start a project

Connect a repository and kelin sets up builds, channels and previews.

Flat (in-app panel)

Branch

release/2.4

12 commits ahead of main. Last build passed 4 minutes ago.

Passed

Build cache

2.1 GB across 38 branches. Old entries expire after 14 days.

Deploy previews

Every pull request gets its own URL.

Ink theme

Pipelines

Fast builds

Every push runs on a warm cache.

Build cache

2.1 GB across 38 branches.

Kbd

Shortcut keys on a 1px lip. One key per element; on coloured keys and in tooltips they lose the lip and take the parent's ink.

Single keys

EscEnterTab⌘⇧↑↓/

Combos

Search
⌘K
Open command palette
⇧⌘P
New branch
⌘B
Run task
⌘R
Show shortcuts
⌘/

Inside keys

Inside tooltip

Search⌘KToggle sidebar⌘\

Ink theme

⌘K

Content

Long-form reading in IBM Plex. Running text scales with the size; headings keep their size.

Default · 16/26

Shipping a release branch

A release branch freezes what goes out while main keeps moving. This guide walks through cutting one, fixing it in place and merging the fixes back.

Cut the branch

Start from a green build on main. In kelin, open the branch menu and choose New branch, or run kelin branch create release/2.4 from the terminal. The branch gets its own channel, so release talk stays out of #general.

Before you announce it, check three things:

  • The last build on the branch passed.
  • Preview deploys point at the staging database, not production.
  • The version in package.json matches the branch name.

Freeze the scope

From now on, only fixes land on the branch. Each fix goes through a pull request with therelease label, which keeps the changelog honest.

A release branch is a promise about what will not change. Keep it small enough to keep.

When a fix is urgent

  1. Open the pull request against release/2.4, not main.
  2. Ask for one review in the release channel.
  3. Merge after the build passes; kelin posts the result in the channel.

Track the builds

Build times for the last four release branches, measured on the same runners:

BranchBuildsMedianFailed
release/2.4384m 03s2
release/2.3524m 41s5
release/2.2475m 12s3
release/2.1616m 08s9

Merge the branch back into main when the release is out. If the merge has conflicts, resolve them on main so the release history stays linear.

sm · 15/24

Freeze the scope

Only fixes land on release/2.4. Each one goes through a pull request with therelease label; see the release guide for the checklist.

A release branch is a promise about what will not change.

When a fix is urgent

  • Open the pull request against the release branch.
  • Merge after the build passes.

lg · 18/28

Freeze the scope

Only fixes land on release/2.4. Each one goes through a pull request with therelease label; see the release guide for the checklist.

A release branch is a promise about what will not change.

When a fix is urgent

  • Open the pull request against the release branch.
  • Merge after the build passes.