West Kite

West Kite Design System

How West Kite looks, in one place: the mark, color, type, layout and components, for the website, the product and everything in between.

01

The brand

West Kite makes complex commercial real estate information easier to understand and act on. The identity has one job: to get out of the way of the information.

What we are

Commercial real estate runs on documents that were never designed to be read together: leases, rent rolls, appraisals, zoning records, transaction data. West Kite reconciles them into one clear view of every asset, so that the people making decisions can see what is true without doing the reconciliation themselves.

The visual identity should feel the way a good analyst's desk feels. Clean paper, sharp figures, nothing decorative, and one marker pen kept for the thing that actually matters.

Positioning line

Property intelligence, without the noise.

The line does two things: it names the category and it states the promise. It is set in the display face, in sentence case, with a full stop. It is not shortened, rearranged, or given an exclamation mark.

Three principles

Clarity over cleverness

Every page answers a question the reader arrived with. Headlines say what the thing is. Numbers are shown with their units and their source. If a design choice makes something harder to read, it is wrong, however good it looks.

Restraint

Two colors do almost all the work. One weight per typeface. No italics, no capitals, no bold. The accent color appears rarely enough that it still means something when it does.

Precision

The product is numbers, so the numbers must look trustworthy. Tabular figures that line up, consistent decimal places, real units, dates in full. Precision in the type is a signal of precision in the data.

02

Mark and wordmark

The mark is two circles built from stripes. The wordmark is the name, typeset. They work alone or together, and neither is ever redrawn: the mark's proportions are fixed numbers and the wordmark is the display face set the way this guide describes.

The mark

Two circles, each made of fifteen horizontal stripes that thicken from one end to the other, running in opposite directions: the left circle from thick at the top to thin at the bottom, the right circle the other way. In every row the two stripes add up to the row height, so where the circles overlap they tile the row completely and the lens fills solid, shading from Cobalt at the top into Teal at the bottom. The lens is not drawn. It is what happens when the circles meet.

Color

On Paper and on Linen the mark is Cobalt on the left and Teal on the right, always in that order. On Carbon, or on any dark photograph, it is Linen in one color, because Cobalt does not carry on dark. A gray version, Slate in one color, exists for documents, partner logo strips and anywhere the color mark would fight its surroundings. It is never any other pair.

Proportions

Fifteen rows. Centers half a radius apart. The thinnest stripe is a quarter of a row and the thickest three quarters, with the growth eased rather than linear. The stripe at each tip runs to the circle's edge so the silhouette always closes. These numbers do not change per use; the mark is generated from them, not drawn.

Construction

r½ r2½ r15 rowscenter line

Two circles of radius r, centers half a radius apart, so the whole mark is two and a half radii wide. Fifteen equal rows; in each row the two stripes add up to the row height. Dashed lines are construction only and never ship.

Downloads

Copy puts the logo straight on your clipboard, ready to paste into a slide, a document or a design tool: PNG is a 2048px image with a transparent background, SVG is the vector. The wordmark files carry the letterforms as outlines, so they do not depend on the font. The Carbon files are opaque, and every Carbon card also offers the same Linen drawing with no background, for a dark surface of your own. Download saves the same files; all of them as one zip, including an Ink-only mark.

Mark, colorCobalt and Teal. The default on Paper and Linen.CopyDownloadPNGSVG
Mark, graySlate, one color. Documents, partner strips, anywhere the color mark would fight.CopyDownloadPNGSVG
Mark on CarbonLinen, one color, for dark surfaces. The no background files are the same Linen mark on nothing.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Wordmark, InkThe name as outlines: Newsreader at weight 375, kerned and tracked.CopyDownloadPNGSVG
Wordmark, graySlate, for quiet placements.CopyDownloadPNGSVG
Wordmark on CarbonLinen on Carbon. The no background files are the same Linen wordmark on nothing.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Lockup, colorMark at cap height on the baseline, half a cap height of gap.CopyDownloadPNGSVG
Lockup, grayThe whole lockup in Slate.CopyDownloadPNGSVG
Lockup on CarbonLinen on Carbon, for footers and dark headers. The no background files are the same Linen lockup on nothing, for a dark surface of your own.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Stacked lockup, colorThe mark above the name, both centered. The capitals are 42% of the mark, the gap 36% of it.CopyDownloadPNGSVG
Stacked lockup, grayThe whole stacked lockup in Slate.CopyDownloadPNGSVG
Stacked lockup on CarbonLinen on Carbon. The no background files are the same Linen stack on nothing, for a dark surface of your own.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG

Pattern

The stripes, for business cards, banners, backdrops and anywhere the brand needs a surface without the mark. Fifteen equal stripes to the height, each one solid color, stepping from Cobalt at the top to Teal at the bottom. The rows run flat, drop five eighths of the height through one turn, and run flat again; the turn spans 1.26 heights and sits at 72% of the width. The rule is set by the height, so every format is the same curve cropped differently. Text and the mark never sit on the stripes: give them a Paper panel, as on the card. The stripes never change; only the surface behind them does, and it may be Paper, Linen, Carbon or Cobalt. On Cobalt the top rows melt into the surface and the Teal rows emerge toward the bottom. Copy and download work as for the logos; the plain files have transparent gaps, so the pattern takes the surface it lands on, and the Carbon and Cobalt files are opaque.

Pattern, cardSeven by four, the business card. This is the drawing the rule was set on.CopyDownloadPNGSVG
Pattern, bannerFour by one, for headers, email banners and stands.CopyDownloadPNGSVG
Pattern, squareOne by one, for social tiles and backdrops.CopyDownloadPNGSVG
Pattern on Carbon, cardThe same stripes on Carbon. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Pattern on Carbon, bannerFour by one on Carbon. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Pattern on Carbon, squareOne by one on Carbon. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Pattern on Cobalt, cardThe top rows melt into the surface and the Teal rows emerge. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Pattern on Cobalt, bannerFour by one on Cobalt. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG
Pattern on Cobalt, squareOne by one on Cobalt. Opaque.CopyDownloadPNGSVGCopy, no backgroundDownload, no backgroundPNGSVG

Favicon

West Kite
West Kite

The favicon is the full mark centered in a square, same fifteen rows, in color on every tab. It ships as favicon.svg for current browsers, with a 32px PNG for older ones and a 180px PNG on a Paper background for iOS home screens.

The wordmark

Two words, sentence case, in the display face. There is no other symbol; the mark above is the only one.

West Kite
West Kite

Clear space

capcapcapcap
West Kite

Clear space is one cap height on every side, measured from the capitals and the mark, not from the line box. The same rule applies to the wordmark alone and to the mark alone.

Nothing else, including other text, rules or the edge of the page, comes inside that margin.

Minimum size

West Kite64px · display
West Kite32px · page header
West Kite20px · minimum on screen

Never smaller than 20px on screen or 6mm in print. Below that the serifs fill in and the name reads as a smudge. In a footer or a caption, set the name in the text face instead.

Color

The wordmark is Ink on Paper or Linen, and Linen on Carbon. It is never set in Cobalt or any semantic color, and never placed over a photograph unless the photograph is dark enough for Linen to pass contrast, as on the current landing page.

Do not

  • Set the name in capitals, italics or a heavier weight.
  • Add a kite, a compass, a map pin, or any other symbol.
  • Stretch, condense, outline or shadow it.
  • Close up the space between the words, or hyphenate it.
  • Break it across two lines. The name is always one line; where it cannot fit, use the mark alone.
  • Spell it "Westkite" or "WestKite" anywhere, including in URLs shown to people. The domain is lowercase because domains are; the name is not.

Together

The mark sits to the left of the name, a third taller than the capitals and centered on them, separated by half the mark height. Or it sits above the name, both centered, with the capitals at 42% of the mark and a gap of 36% of the mark between them. Clear space around the lockup is the height of the W on every side. The mark is not placed between or after the words.

mark top · 100%mark bottomcap line · 75% of the markx-heightbaseline
West Kite

With the mark at 100%, the capitals are 75% of it and the gap is 50%. The mark is centered on the capitals, so it rises above the cap line and drops below the baseline by the same amount. Nothing is centered on the line box; everything else hangs off the capitals.

West Kite
West Kite
03

Color

Near-black ink on white paper, warmed slightly at the edges. The system is essentially two colors plus one accent. Everything else is a neutral in between, or a semantic color that only appears on data.

How much of each

Paper, most of any page Linen, panels and cards Ink, text and primary buttons Carbon, an occasional dark band Cobalt, a few pixels

These are proportions of a typical page, not a rule to measure. If a page has more Cobalt than this, something is being over-emphasized.

Core

Accent

Semantic

Semantic colors describe the state of data, not the brand. They appear as a small dot, a figure, a badge or a thin stripe, never as a surface or a heading. Each is a step on a scale below: 700 for a dot or fill, 900 for text, 200 for a badge background.

On Carbon

Dark sections do not invert the palette. They use Carbon as the ground and warm, translucent whites for text. The 250 in the blue channel is deliberate: pure white on Carbon looks cold. There is no accent on Carbon, because Cobalt fails contrast against it; a hovered link simply brightens from Warm white 60 to Linen.

Scales

The named colors above are what a page is made of. Components need more: a badge needs a tint behind dark text, a hover needs one step darker than rest, a focus ring needs one step lighter than the accent. So each hue is a scale of ten steps, and every step has a fixed job. Gray is warm and passes through the named neutrals; blue is built on Cobalt; the other six are shared with well-established systems and are a starting point, to be tuned as we use them.

StepUse
100Subtle background: a hovered row, a code block, a quiet panel
200Element background: a badge, a selected item, an input on Linen
300Hover on an element background
400Border at rest
500Border on hover, a strong divider
600Focus ring, strong border, a disabled solid fill
700Solid fill: a dot, a solid badge, the accent itself
800Solid fill on hover or pressed
900Text on Paper, Linen, or on a 100 to 300 fill of the same hue
1000High-contrast text

Backgrounds sit outside the scales: background 100 is Paper and background 200 is Linen. Click any step to copy it.

Gray
Blue
Red
Amber
Green
Teal
Purple
Pink

Amber is the one scale to watch. Its 800 and 900 steps drift toward orange, which we do not use. Amber is for warning badges only, at 200 behind 900 text, and never as a fill or a heading color.

Scales on Carbon

On a dark band the scales run the other way: 100 is nearest Carbon and 1000 is nearest Linen, so the step numbers keep the same jobs. Background 100 on Carbon is Carbon itself; background 200 is a panel at #262626.

Gray
Blue
Red
Amber
Green
Teal
Purple
Pink

Text and background pairings

Every combination we use, with its contrast ratio. Anything at 4.5 or above is fine for body text. Between 3 and 4.5 is for text above 24px only. Nothing below 3 carries words.

Ink on Paper
#292929#ffffff
Ink on Linen
#292929#f7f6f5
Slate on Paper
#737373#ffffff
Paper on Ink
#ffffff#292929
Linen on Carbon
#f7f6f5#1c1c1c
Warm white 60 on Carbon
.60#1c1c1c
Cobalt on Paper
#093acd#ffffff

Rules

Ink is the text colorNever pure black #000000 for words, and never a tint of Ink. Muted text is Slate, not Ink at reduced opacity.
Cobalt is rareIt marks the one thing on a page that must be seen. If two things are in Cobalt, neither is. It is never a background or a heading, and never appears on Carbon.
No gradientsNot on surfaces, not behind a headline, not on text. Every surface is one flat color from the palette.
No shadowsSurfaces are separated by color, Paper against Linen, or by a one-pixel Ash rule. Elevation is not part of the system.
Semantic colors stay on dataGreen, yellow and red belong to figures, dots and stripes. They are not used for buttons, headings or emphasis.
No orange, no warm accentsNothing coral, amber or terracotta, anywhere. Warmth comes only from the slightly warm neutrals.
Scales are for componentsA page is Paper, Linen, Ink and a hairline of Ash. The scales are what badges, pills, inputs, rows and states are built from. A section background is never a scale step.
Photography is darkWhen a photograph carries text, it is dark enough for Linen to pass contrast on it. The hero image on the landing page is the reference.
04

Typography

A serif for the voice and a grotesque for the facts. Each is used in one weight, tracked tight, in sentence case. That combination of a light serif with negative tracking is the most recognizable habit in the system.

Every asset, one clear view.

Role
Display: headlines, section titles, the wordmark, lead paragraphs. Never numbers.
Face
Martina Plantijn Light, weight 300
Foundry
Klim Type Foundry. Commercial license, per style and per site.
Stand-in
Newsreader at weight 375, optical size fixed at 28. Free, from Google Fonts. This page renders in the stand-in.
Tracking
−0.03em at every size
Line height
1.0 at display sizes, 1.4 as a lead paragraph
Italic
Never

Class A, 18,240 sq ft, $74.50 psf

Role
Text: body copy, interface, labels, buttons, tables, captions, and every number at every size
Face
STK Bureau Sans Book, weight 400. STK Bureau at 430 for labels.
Foundry
STK. Commercial license.
Stand-in
Inter, weights 400 and 500. Open license, already self-hosted on the site. This page renders in the stand-in.
Tracking
−0.04em at every size
Line height
1.4 for body, 1.1 for leads and buttons
Figures
Tabular, always

The display face in full

Built for the decisions that move capital.

CapitalsABCDEFGHIJKLMNOPQRSTUVWXYZ
Lowercaseabcdefghijklmnopqrstuvwxyz
Figures0123456789 $€£ %
Punctuation.,;:!?‘’“”–—-()[]/&@#*
Accentsàáâäãåæ çèéêë ìíîï ñòóôöõø ùúûü ß

Martina Plantijn is sold in Light, Regular, Medium, Bold and Black, each with an italic. We use Light only, and no italics. Its lineage is Plantin, the sixteenth-century Antwerp face that Times was later drawn from, which is why it feels sturdy even at its lightest weight. Newsreader, the stand-in, is a variable font: weight 375 and optical size 28 are the settings that match Martina's stroke.

The text face in full

Rent roll, reconciled to the lease.

CapitalsABCDEFGHIJKLMNOPQRSTUVWXYZ
Lowercaseabcdefghijklmnopqrstuvwxyz
Figures0123456789 $€£ %
Punctuation.,;:!?‘’“”–—-()[]/&@#*
Accentsàáâäãåæ çèéêë ìíîï ñòóôöõø ùúûü ß
Weight 500Used for labels, buttons and the name of a thing in a list.

Tabular figures

Every digit takes the same width, so columns of numbers align without a table. On the web this is font-variant-numeric: tabular-nums, applied to anything that holds a figure.

200 Park Avenue$48,200,000
Cap rate5.80%
In-place rent$2,795,600
WALT4.2 yrs

Large figures

A number pulled out of a paragraph, a valuation or a cap rate, is the text face at display size: 40px or larger, weight 400, tracked −0.04em, tabular. It reads as a fact, not a flourish.

Portfolio value$412.6M
Assets38
Blended cap rate5.4%

Type scale

Eleven sizes. Size and line height are in pixels; the serif sizes above 24px have a line height equal to their size.

Display XLSerif · 54 / 54 · −0.03em
Hero headlines only

Every asset, one clear view.

Display LSerif · 48 / 48 · −0.03em
Page titles

Built for the decisions that move capital.

Display MSerif · 40 / 40 · −0.03em
Section titles

Comparables, rent rolls, and cap rates, reconciled.

HeadingSerif · 32 / 32 · −0.03em
Card and panel titles

Portfolio overview

SubheadingSerif · 24 / 24 · −0.03em
Groups within a panel

Lease events this year

LeadSerif · 22 / 1.4 · −0.03em
The paragraph under a headline

We make complex commercial real estate information easier to understand and act on.

Body LSans · 18 / 1.4 · −0.04em
Long-form reading

Rent escalations, renewal options, and expirations across the portfolio, pulled into one schedule and kept current.

BodySans · 16 / 1.4 · −0.04em
Default for everything

The rent roll is read directly from the lease documents, so the figures on the page match the figures in the contract.

Body SSans · 14 / 1.4 · −0.04em
Tables, dense interface

Sources: county records, CoStar, the landlord's own rent roll.

LabelSans 500 · 13 / 1.4 · −0.04em
Buttons, field labels, chips

Cap rate 5.8%

CaptionSans · 12 / 1.4 · −0.04em
Timestamps, footnotes, sources

Updated September 5, 2026, 5:40 pm

A headline, a lead and a paragraph

Comparables, rent rolls, and cap rates, reconciled.

One view of every asset, built from the documents that actually govern it.

Leases, amendments, estoppels, and appraisals are read once, checked against each other, and turned into a schedule you can act on. When the documents disagree, we show you where, and which one wins.

Rules

No italicsIn any face, at any size, including for titles of documents or emphasis. Where a word must stand out, change its size or color, or give it its own line.
No capitalsHeadings, labels, buttons, table headers and navigation are all sentence case. Acronyms keep their capitals: NOI, WALT, CAM.
No boldThe serif is Light only. The grotesque is Book, and 500 for labels. Weight is not used for emphasis in running text.
Tracking is always negative−0.03em on the serif, −0.04em on the grotesque, at every size. It is not loosened for small text and not tightened further for large.
MeasureBody text runs between 55 and 70 characters a line. Above that, add a column or a margin, not a larger font.
Full stops on headlinesA headline that is a sentence ends with a full stop. A headline that is a label does not. "Portfolio overview" has none; "Every asset, one clear view." has one.
Numbers are never serifEvery figure, from a table cell to a headline-sized valuation, is set in the text face with tabular figures. The serif is for words. Write headlines so they do not need a number in them.
Numbers are figuresWrite 38 assets, not thirty-eight. Keep decimal places consistent within a table. Use the dollar sign and thousands separators: $48,200,000 or $48.2M, never $48.2m or 48.2 million.
Dates in fullSeptember 5, 2026, never 9/5/26. Times are 12-hour, with am or pm.
Serif for the website, sans for the softwareThe display face belongs to the website: headlines, leads, the positioning line, the wordmark. Inside the product every heading, title and label is the text face at weight 500. A serif never appears in a component.
Interface sizes14px for body and controls, 13px for dense tables and labels, 16px for card titles, 18px for dialog and page titles. The display sizes of the scale stay on the website.
No hyphenationWords are not broken across lines. Headlines wrap at word boundaries with balanced wrapping; body text is left ragged.
05

Layout and spacing

Left-aligned, single column where possible, generous margins, and a one-pixel hairline where anything needs separating. Space does the work that boxes and shadows do elsewhere.

Spacing scale

4
8
12
16
24
32
48
64
96

Nine steps. Inside a component use 4 to 16. Between components use 24 or 32. Between sections use 64 or 96. A value that is not on the scale is a mistake.

Corners

One radius, 4px, on everything that has a corner: buttons, inputs, cards, badges, tags, dialogs, image frames, the page frame. Sharp enough to read as precise, soft enough not to catch the eye. There is no second value and no pill; a badge is a small rectangle with the same corner as the button beside it.

Page

Content sits in a column no wider than 1080px, with 32px gutters that drop to 20px on a phone. Text blocks inside that column are further limited to their measure. Sections are separated by a hairline in Ash and 72px of space, not by a change of background color, except for the occasional Carbon band.

Alignment is to the left edge. Nothing is centered except the wordmark on a cover, and a single button under a single line of text.

The frame

The landing page as it is today: a rounded panel inset from the viewport edge.

The landing page shows its hero inside a panel inset 22px from the edge of the browser. The inset is the signature, not the roundness: the panel's corners take the same 4px as everything else. It is not used for inner pages.

06

Components

The interface pieces the website and the product share, each specified once: when to use it, variants, sizes, states, the numbers behind it, the rules, and the markup. Every example on this page is rendered by westkite.css with the same wk- classes the snippets show, so what you copy is what you see. Components are interface, so they use the text face only: the serif belongs to the website and never appears inside the product.

Button

One primary per view, as many secondaries as the view needs, and a ghost for actions that should not draw the eye. Labels are verbs in sentence case.

Use it for

An action that changes something: saving, sending, exporting, opening a flow.

Not for

Navigation to another page, which is a link. Toggling a setting that applies immediately, which is a switch.

Accessibility

Use a real button element. Disabled buttons keep their label and expose aria-disabled when they must stay focusable. A loading button sets aria-busy and keeps its label.

Variants

Book a walkthroughSee the dataCancelDelete asset

Sizes

SmallDefaultLarge
SmallDefaultLarge

States

RestHoverPressedFocusDisabledSaving
RestHoverFocusDisabled

Full width

Send inquiry

On Carbon

Book a walkthroughSee the dataCancelHoverDisabled

Specification

PropertyValue
Height40px default, 32px small, 48px large
Padding0 18px; 0 12px small; 0 22px large
TypeText face, 14px, weight 500, tracked −0.04em; 13px small; 16px large
Corner4px, the system radius
PrimaryInk fill, Paper text. Hover gray 900. Pressed Carbon. On Carbon: Linen fill, Ink text
SecondaryStone outline, Ink text. Hover gray 100 fill
GhostNo outline. Hover gray 100 fill
DangerRed 700 fill, Paper text. Hover red 800. Confirmations only
Focus2px Cobalt outline, 2px offset, on every variant; Linen on Carbon
DisabledGray 300 fill, gray 700 text, no outline, not-allowed cursor
Loading12px spinner before the label; the label stays so the width does not jump

Rules

  • One primary button per region: a page header, a dialog, a form and a card each get one. If two things in one region are primary, neither is.
  • Labels are verbs: Book, Send, Export, Delete. Never OK, Submit, or Click here.
  • Danger is for destructive, irreversible actions, and always sits behind a confirmation.
  • Never shrink a button below 32px tall or set its label in capitals.
  • A button next to a link: the button is the action, the link is the escape.
Show the markupHide the markup6 lines
<button class="wk-btn wk-btn-primary" type="button">Book a walkthrough</button>
<button class="wk-btn wk-btn-secondary" type="button">See the data</button>
<button class="wk-btn wk-btn-ghost" type="button">Cancel</button>
<button class="wk-btn wk-btn-danger" type="button">Delete asset</button>
<button class="wk-btn wk-btn-primary" type="button" disabled>Book a walkthrough</button>
<button class="wk-btn wk-btn-primary is-loading" type="button" aria-busy="true">Saving</button>

Input

Text fields, with their label above, help below, and the state carried by the border. Numbers get a prefix or a suffix so the unit is never in doubt.

Use it for

Free text, numbers, email, a short note, a choice from a list.

Not for

Yes or no questions, which are a checkbox or a switch. Long documents, which need an editor.

Accessibility

Wrap the field in a label so the label is the click target and announced with the input. Errors are tied to the input with aria-describedby and aria-invalid.

Text

Emailname@company.comWe reply within one working day.
Emailname@company
Emailname@companyEnter a full email address, including the domain.
Emailname@company.comSet by your administrator.

With units

Passing rent$1,240,000
Floor area18,240sq ft
Cap rate5.8%

Textarea and select

NotesBreak option exercisable at month 60 with six months' notice.
Use classOffice

On Carbon

Emailname@company.com
Passing rent$1,240,000
Emailname@companyEnter a full email address.

Specification

PropertyValue
Height40px; textarea minimum 96px
Padding9px 12px
TypeText face, 14px, tracked −0.04em; label 13px above; help 12px Slate below
Border1px Stone at rest; Ink on focus with a 3px Ink ring at 8%; red 700 on error
Corner4px
Prefix and suffixLinen fill, Slate text, Ash rule against the value
DisabledGray 100 fill, gray 700 text, Ash border
Width320px default; 480px wide; full width inside a dialog

Rules

  • Every field has a visible label above it. Placeholder text is not a label.
  • Help text says what to enter; error text says what went wrong and how to fix it, in one sentence.
  • Units live in the affix, not in the label and not in the value.
  • Required is the default; mark the optional fields, not the required ones.
Show the markupHide the markup15 lines
<label class="wk-field">
  <span>Email</span>
  <input class="wk-input" type="email" name="email" autocomplete="email" required aria-describedby="email-help">
  <span class="wk-help" id="email-help">We reply within one working day.</span>
</label>

<label class="wk-field">
  <span>Passing rent</span>
  <span class="wk-input wk-affix"><span class="wk-affix-l">$</span><input class="wk-affix-v" type="text" inputmode="numeric"></span>
</label>

<label class="wk-field">
  <span>Use class</span>
  <select class="wk-input wk-select"><option>Office</option><option>Industrial</option></select>
</label>

Checkbox, radio and switch

Three ways to choose. A checkbox for any number of options, a radio for exactly one, a switch for something that takes effect immediately.

Use it for

Checkbox: zero or more of a set. Radio: one of a set, two to six options. Switch: a setting that applies the moment it flips.

Not for

A radio group with one option. A switch that needs a Save button, which is a checkbox.

Accessibility

Real inputs styled with appearance none, wrapped in a label. A switch carries role switch. Groups of radios sit in a fieldset with a legend.

Checkbox

Include vacant floorsInclude vacant floorsInclude vacant floorsInclude vacant floors

Radio

NetGrossEffective

Switch

Email me when a lease event is 90 days outEmail me when a lease event is 90 days out

On Carbon

Include vacant floorsNetNotify me

Specification

PropertyValue
Checkbox16px, 4px corner, 1px Stone border; checked is Ink fill with a Paper tick
Radio16px circle, 1px Stone border; selected is an Ink ring with a 10px Ink dot. The circle is the one shape that is not the system corner
Switch36px by 20px track, fully round; Stone off, Ink on; 16px Paper knob
LabelText face 14px, 10px from the control, the whole label is the click target
Focus2px Cobalt outline, 2px offset
DisabledControl at 50% opacity, label gray 700

Rules

  • A switch acts immediately. If there is a Save button, use a checkbox.
  • Radios need at least two options and exactly one selected by default.
  • Labels are statements of what happens when on: Email me when…, not Enable emails.
Show the markupHide the markup8 lines
<label class="wk-control"><input class="wk-checkbox" type="checkbox" checked> Include vacant floors</label>

<fieldset class="wk-fieldset"><legend>Basis</legend>
  <label class="wk-control"><input class="wk-radio" type="radio" name="basis" checked> Net</label>
  <label class="wk-control"><input class="wk-radio" type="radio" name="basis"> Gross</label>
</fieldset>

<label class="wk-control"><input class="wk-switch" type="checkbox" role="switch"> Email me when a lease event is 90 days out</label>

Badge

A badge reports a state and cannot be clicked. Subtle is the default; solid is for the one state that must be seen from across the room.

Use it for

Status in a table cell, a card header, a list row.

Not for

Anything clickable, which is a pill. Counts, which are plain figures.

Accessibility

A badge is text; it needs no role. If the color carries meaning, the word carries it too, so it never depends on the hue.

Subtle

DraftIn reviewOverdueExpiringLeasedVerifiedForecastFlagged

Solid

DraftIn reviewOverdueExpiringLeasedVerifiedForecastFlagged

Sizes and without the dot

LeasedLeasedLeasedLeased

On Carbon

DraftIn reviewOverdueExpiringLeasedVerifiedForecastFlagged
DraftIn reviewOverdueExpiringLeasedVerifiedForecastFlagged

Specification

PropertyValue
Height22px default, 28px large
Padding0 9px; 0 12px large
TypeText face 12px weight 500; 13px large; tracked −0.02em
Corner4px, the system radius, never a pill
Subtle200 fill, 900 text, 700 dot of the same hue. On Carbon: 300 fill, 900 text
Solid700 fill, Paper text; Ink text on amber
Dot6px, 6px before the label; omit when the badge sits in a list of the same kind

Rules

  • Semantic hues mean something: green is done or positive, amber needs attention, red is failed or overdue. Do not use them for categories.
  • Gray, blue, teal, purple and pink are for categories and have no fixed meaning; decide once per product and keep it.
  • One badge per cell. If a row needs two states, it needs a different table.
  • Sentence case, two words at most. Expires in 25 days is the limit.
Show the markupHide the markup4 lines
<span class="wk-badge wk-badge-green"><i></i>Leased</span>
<span class="wk-badge wk-badge-amber"><i></i>Expiring</span>
<span class="wk-badge wk-badge-solid-red">Overdue</span>
<span class="wk-badge wk-badge-lg wk-badge-blue"><i></i>In review</span>

Pill

A pill is interactive: a filter you can select, a tag you can remove. It is always gray, and it shares the system corner; color and the outline are what tell it from a badge.

Use it for

Filters above a table, tags on a record, a small set of mutually exclusive views.

Not for

Status, which is a badge. Primary actions, which are buttons.

Accessibility

A pill is a button with aria-pressed for its selected state. A removable pill's × carries an aria-label naming what it removes.

States

OfficeOffice, hoveredOffice, selectedOffice, focusedOffice, disabled

Removable and sizes

Midtown ×Class A ×OfficeClass A

As a filter bar

AllOfficeIndustrialRetailMultifamilyLand

On Carbon

OfficeOffice, hoveredOffice, selectedMidtown ×

Specification

PropertyValue
Height32px default, 28px small
Padding0 14px; 0 11px small
TypeText face 13px weight 500, tracked −0.04em; 12px small
Corner4px
RestPaper fill, Stone outline, Ink text
HoverGray 100 fill, gray 600 outline
SelectedInk fill and outline, Paper text
Remove× at 15px after an 8px gap, 70% opacity, the whole pill is the target
Focus2px Cobalt outline, 2px offset

Rules

  • Pills are never colored. If it has a hue, it is a badge.
  • In a filter bar, exactly one pill is selected unless the filter is explicitly multi-select.
  • A removable pill shows the × always, not on hover, so it is discoverable on touch.
Show the markupHide the markup3 lines
<button class="wk-pill" type="button" aria-pressed="false">Office</button>
<button class="wk-pill" type="button" aria-pressed="true">Class A</button>
<button class="wk-pill" type="button">Midtown <span class="wk-pill-x" aria-label="Remove Midtown">×</span></button>

Card

A card is a Linen surface with no border and no shadow. It groups one thing: an asset, a lease, a figure with its source. Cards do not nest.

Use it for

One record in a grid, a summary with one action, a figure with its source.

Not for

Wrapping a whole page section. Nesting a card in a card. Lists longer than three items, which are a table.

Accessibility

An interactive card is a link or a button covering the whole card, with the title as its accessible name. Never a click handler on a plain div.

Default

200 Park Avenue

Class A office, 18,240 sq ft, leased to three tenants. Next lease expiration March 2027.

Open asset

With header and footer

AssetLeased

200 Park Avenue

Class A office, 18,240 sq ft, leased to three tenants.

Updated September 5, 2026 from the rent roll

Outlined and interactive

300 Main Street

Outlined: Paper fill with a Stone border, for a card on a Linen surface.

Riverside Commerce Center

Interactive, hovered: the whole card is the target and takes an Ink outline.

On Carbon

200 Park Avenue

Class A office, 18,240 sq ft, leased to three tenants.

Open asset

Specification

PropertyValue
FillLinen on Paper; background 200 on Carbon. Outlined variant: Paper fill, 1px Stone border, for use on Linen
Padding24px
Corner4px
TitleText face 16px weight 500, line height 1.3. Never the serif
BodyText face 14px, Slate
Header and footer13px Slate; the footer sits above a gray 400 rule
InteractiveHover takes a 1px Ink outline inside the edge; no lift, no shadow
WidthFills its column; 340px minimum

Rules

  • One thing per card, and one action at most, at the bottom.
  • No card inside a card. If a card needs sections, use a rule between them.
  • A grid of cards has equal heights in a row; let the tallest set the row.
  • Cards do not carry shadows; separation is Linen against Paper.
Show the markupHide the markup5 lines
<article class="wk-card">
  <h3 class="wk-title">200 Park Avenue</h3>
  <p>Class A office, 18,240 sq ft, leased to three tenants.</p>
  <a class="wk-btn wk-btn-secondary wk-btn-sm" href="/assets/200-park-avenue">Open asset</a>
</article>

Table

A table sits in a box and carries the numbers. Headers are Ink, rows have no rules, figures are tabular and right-aligned, and nothing wraps.

Use it for

More than three records with the same fields. Anything that will be sorted or compared.

Not for

Layout. Two-column key and value pairs, which are a description list.

Accessibility

A real table with th cells and scope. Interactive rows are focusable and act on Enter. Sort state is announced with aria-sort on the header.

Default, interactive

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
300 Main StreetVacant, 4th floor$0Vacant
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days

Sorted header and a selected row

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days
300 Main StreetVacant, 4th floor$0Vacant

Dense

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
300 Main StreetVacant, 4th floor$0Vacant
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days

Static

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
300 Main StreetVacant, 4th floor$0Vacant
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days

On Carbon

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
300 Main StreetVacant, 4th floor$0Vacant
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days

Specification

PropertyValue
Box1px Stone border, 4px corner, Paper fill, 8px inner padding; scrolls sideways on its own when the table is wider than the page
Header40px tall, text face 14px weight 500 Ink, one Ash rule beneath, then 12px of air
Sorted headerA 6px chevron after the label; the column sorts on click
Rows14px, tabular figures, 10px 12px cell padding, no rules; 32px and 6px 12px in dense
AlignmentNumbers and dates right; text left; the last column follows its content
InteractivePointer cursor; hover and keyboard focus fill the row in gray 100 with the corner; selected fills blue 200
Empty valueAn em dash, never a blank
On CarbonCarbon fill, 22% white border, 11% white rule, gray 300 hover, blue 300 selected

Rules

  • Every column has a header, even the status column.
  • Sort by one column at a time, and show which one.
  • Wide tables scroll sideways inside the box with the first column kept in view; move columns that are rarely read to the detail view.
  • Totals go in a final row set in weight 500, above a single Ash rule.
  • A table with no rows shows the empty state, not an empty box.
Show the markupHide the markup9 lines
<div class="wk-table-root">
  <table class="wk-table">
    <thead><tr><th scope="col">Asset</th><th scope="col" class="wk-num wk-fit" aria-sort="ascending">Cap rate</th><th scope="col" class="wk-fit">Status</th></tr></thead>
    <tbody class="wk-table-gap" aria-hidden="true"></tbody>
    <tbody class="wk-table-body" data-interactive="true">
      <tr tabindex="0"><td>200 Park Avenue</td><td class="wk-num wk-fit">5.8%</td><td class="wk-fit"><span class="wk-badge wk-badge-green"><i></i>Leased</span></td></tr>
    </tbody>
  </table>
</div>

Note

A note is a short message set apart from the flow: something the reader should know before they continue. It is not a badge, not an alert, and not a place for paragraphs.

Use it for

Context the reader needs before acting: where a figure came from, what changed, what failed.

Not for

Confirmation of an action just taken, which is a toast. Marketing copy.

Accessibility

Error and warning notes carry role status so they are announced when inserted. The dot is decorative; the words carry the meaning.

Kinds

Read from the rent roll. Figures on this page come from the landlord's rent roll dated August 14, 2026.
New source added. County records are now checked against every lease automatically.
Reconciled. All 38 assets match their source documents.
Two leases expire within 90 days. Riverside Commerce Center and 300 Main Street.
We could not read page 4 of the lease. Upload a clearer scan, or enter the rent by hand.

On Carbon

Read from the rent roll. Figures on this page come from the landlord's rent roll.
Two leases expire within 90 days. Riverside Commerce Center and 300 Main Street.

Specification

PropertyValue
Padding12px 14px
Corner4px
TypeText face 14px; the first sentence at weight 500, the rest at 400
Dot8px, 700 step of the hue, 10px before the text
NeutralLinen fill, Ash border, Ink text, gray 700 dot
InfoBlue 200 fill, blue 400 border, blue 900 text
Success, warning, errorSame pattern in green, amber and red
WidthFills its column, 520px at most

Rules

  • Two sentences at most. If it needs a third, it is a paragraph, not a note.
  • Error notes say what went wrong and what to do, in that order.
  • A note does not dismiss; if it must go away, it is a toast.
Show the markupHide the markup4 lines
<div class="wk-note wk-note-warning" role="status">
  <i></i>
  <span><b>Two leases expire within 90 days.</b> Riverside Commerce Center and 300 Main Street.</span>
</div>

Dialog

A dialog takes over for one decision. The page dims, the dialog sits in the middle, and it closes when the decision is made or abandoned.

Use it for

One decision that must be made before continuing: confirm, collect two or three fields, choose.

Not for

Multi-step flows, settings pages, anything with tabs. Those are pages.

Accessibility

Use the dialog element and showModal so focus is trapped and Escape closes. Label it with aria-labelledby pointing at the title. Return focus to the trigger on close.

Default

Start a conversation.

Share your details and we will be in touch within one working day.

NameAmelia Hart
Emailamelia@marlowcap.com
CancelSend inquiry

Confirmation

Delete 300 Main Street?

The asset, its 4 leases and 12 documents will be removed. This cannot be undone.

CancelDelete asset

Specification

PropertyValue
BackdropInk at 32% over the page
PanelPaper, 1px Stone border, 4px corner, 28px padding; no shadow
Width440px; full width less 24px on a phone
TitleText face 18px weight 500, a sentence with a full stop or a question
BodyText face 14px Slate, two sentences at most
ActionsRight-aligned, 10px apart; the primary or danger button last, a ghost Cancel before it
CloseEscape, the Cancel button, or a click on the backdrop; no × in the corner unless there is no Cancel

Rules

  • One decision per dialog. A dialog with tabs is a page.
  • The title says what the dialog does, as a sentence: Start a conversation. Delete 300 Main Street?
  • Destructive confirmations name the thing and its consequences, and use the danger button.
  • Focus moves into the dialog on open and back to the trigger on close.
Show the markupHide the markup9 lines
<dialog class="wk-dialog" aria-labelledby="dlg-title">
  <h2 class="wk-title" id="dlg-title">Delete 300 Main Street?</h2>
  <p>The asset, its 4 leases and 12 documents will be removed. This cannot be undone.</p>
  <form method="dialog" class="wk-dialog-actions">
    <button class="wk-btn wk-btn-ghost" value="cancel">Cancel</button>
    <button class="wk-btn wk-btn-danger" value="delete">Delete asset</button>
  </form>
</dialog>
<script>document.querySelector(".wk-dialog").showModal();</script>

Toast

A toast confirms that something happened and goes away on its own. It never asks a question and never holds an error the reader must act on.

Use it for

Confirming a completed action: saved, exported, archived. Offering Undo for a reversible one.

Not for

Errors that need attention, which are a note. Anything that needs a decision, which is a dialog.

Accessibility

role status with aria-live polite, so it is announced without interrupting. The Undo action is a real button and stays long enough to reach by keyboard.

Default

SavedReport exported OpenAsset archived Undo

On Carbon

SavedAsset archived Undo

Specification

PropertyValue
FillInk with Paper text; Linen with Ink text on Carbon
Padding10px 14px
Corner4px
TypeText face 13px, tracked −0.03em
ActionOne at most, underlined, 16px after the message
PositionBottom center, 28px from the edge
Duration1.4 seconds; 5 seconds when it carries an action
MotionFades and rises 12px on entry, 160ms; respects reduced motion

Rules

  • Past tense, one to three words: Saved. Report exported. Asset archived.
  • An Undo action is the only way to make a destructive action feel safe; prefer it to a confirmation dialog for reversible things.
  • Never stack toasts. A new one replaces the old one.
  • Errors that need attention go in a note on the page, not in a toast.
Show the markupHide the markup3 lines
<div class="wk-toast wk-toast-fixed" role="status" aria-live="polite">
  Asset archived <button class="wk-toast-action" type="button">Undo</button>
</div>

Empty state

What a list, table or page shows when there is nothing in it yet. It says what would be here and offers the one action that fills it.

Use it for

A first run, a filtered list with no matches, a search with no results.

Not for

Loading, which is a skeleton or a spinner. Errors, which are a note.

Accessibility

The title is a heading at the right level for the page. The action is the same control the reader would find in the navigation.

Default

No assets yet.

Add your first asset and West Kite will read its leases, rent roll and records into one view.

Add an asset

Search with no results

Nothing matches "Bishopsgate".

Check the spelling, or clear the filters to see all 38 assets.

Clear filters

Specification

PropertyValue
Frame1px dashed Stone, 4px corner, 48px 24px padding, centered
MarkThe mono mark in Stone, 28px tall, only on a first-run empty state
TitleText face 18px weight 500, a sentence with a full stop
BodyText face 14px Slate, 36 characters wide at most
ActionOne button; primary for first-run, secondary for a search with no results

Rules

  • Say what would be here, not that nothing is here.
  • The action is the same one the reader would take from the main navigation; never a new path.
  • No decorative illustration. A chart, a document thumbnail or a map is fine when it is the content.
Show the markupHide the markup5 lines
<div class="wk-empty">
  <h2 class="wk-title">No assets yet.</h2>
  <p>Add your first asset and West Kite will read its leases, rent roll and records into one view.</p>
  <a class="wk-btn wk-btn-primary" href="/assets/new">Add an asset</a>
</div>

App shell and navigation

The frame every product page sits in: a Linen sidebar with the mark and the navigation, and a Paper main column. The wordmark does not appear in the product; the mark alone stands for West Kite.

Use it for

Every page of app.westkite.com.

Not for

The website, which has no sidebar. Marketing pages inside the product; they belong on the website.

Accessibility

The sidebar is a nav element with an aria-label. The current page carries aria-current="page". The main column is a main element with the page title as its first heading.

Default

Portfolio

38 assets, updated September 5, 2026

ExportAdd an asset
Portfolio value$412.6M+6.1%vs last quarter
Blended cap rate5.4%−0.2 ptsvs last quarter

Specification

PropertyValue
Sidebar240px, Linen fill, Ash rule on the right, 16px 12px padding; collapses above the content below 1100px
BrandThe mark at 20px beside the name in the text face at 14px weight 500. Never the serif wordmark
Nav item36px tall, 14px weight 500, Slate at rest, Ink on hover with gray 100 fill; current page is Paper fill with an Ash inset ring
Group label12px weight 500 Slate, 14px above
MainPaper, 24px 32px padding, 64px below; content column no wider than 1080px

Rules

  • One level of navigation by default, grouped with labels; a second level indents 12px under its parent.
  • The current page is marked once, in the sidebar; the page title repeats it.
  • Icons are optional in the nav: 20px Lucide at stroke 1.5, before the label, the same for every item or for none.
  • The sidebar never scrolls independently unless the nav is taller than the viewport.
Show the markupHide the markup12 lines
<div class="wk-shell">
  <nav class="wk-sidebar" aria-label="Main">
    <a class="wk-sidebar-brand" href="/"><img src="/mark.svg" alt="" height="20"> West Kite</a>
    <a class="wk-nav-item" href="/portfolio" aria-current="page">Portfolio</a>
    <a class="wk-nav-item" href="/assets">Assets</a>
    <div class="wk-nav-group">Analysis</div>
    <a class="wk-nav-item" href="/reports">Reports</a>
  </nav>
  <main class="wk-main">
    ...
  </main>
</div>

Page header and toolbar

Every product page opens the same way: breadcrumbs if there is a parent, the title in the text face, one line of context, and the page's actions on the right. Filters and search sit in a toolbar beneath.

Use it for

The top of every product page and detail view.

Not for

Marketing headlines. Anything in the serif.

Accessibility

The title is the page's h1. Breadcrumbs are a nav with an ordered list. Toolbar controls have labels, visible or aria-label for a lone search field.

Header with breadcrumbs

  1. Assets
  2. 200 Park Avenue

200 Park Avenue

Class A office, 18,240 sq ft, Midtown

EditAdd a lease

Leases

Toolbar

Search assets
AllOfficeIndustrialRetailExport

On Carbon

Portfolio

38 assets, updated September 5, 2026

ExportAdd an asset

Specification

PropertyValue
TitleText face 24px weight 500, tracked −0.03em, line height 1.2. Never the serif
Subtitle14px Slate, 4px below the title
ActionsRight-aligned, 10px apart; one primary at most
RuleAsh hairline 20px below the header, 24px before content
Breadcrumbs13px Slate, separated by a Stone slash, 8px above the title
Section title18px weight 500, 32px above, 12px below
ToolbarSearch field 260px, then pills, then a spacer, then secondary actions; 16px below

Rules

  • One h1 per page, and it is the page title.
  • Breadcrumbs show the path, not the site: two or three levels, never the home.
  • The subtitle is facts, not a slogan: counts, dates, the address.
  • A toolbar holds filters and search only; the page's actions live in the header.
Show the markupHide the markup18 lines
<nav aria-label="Breadcrumb"><ol class="wk-crumbs"><li><a href="/assets">Assets</a></li><li>200 Park Avenue</li></ol></nav>
<header class="wk-page-head">
  <div>
    <h1 class="wk-page-title">200 Park Avenue</h1>
    <p class="wk-page-sub">Class A office, 18,240 sq ft, Midtown</p>
  </div>
  <div class="wk-page-actions">
    <a class="wk-btn wk-btn-secondary" href="/assets/200-park-avenue/edit">Edit</a>
    <button class="wk-btn wk-btn-primary" type="button">Add a lease</button>
  </div>
</header>
<div class="wk-toolbar">
  <label class="wk-field"><span class="wk-visually-hidden">Search</span><input class="wk-input" type="search" placeholder="Search assets"></label>
  <button class="wk-pill" type="button" aria-pressed="true">All</button>
  <button class="wk-pill" type="button">Office</button>
  <span class="wk-toolbar-spacer"></span>
  <button class="wk-btn wk-btn-secondary wk-btn-sm" type="button">Export</button>
</div>

Stat

A figure with its label, its change and its source. The product is numbers with provenance, and this is the component that carries them.

Use it for

The top of a portfolio or asset page: value, cap rate, vacancy, WALT. Anywhere one number needs to be seen before the detail.

Not for

Numbers that belong in a table row. Text metrics. More than four in a row.

Accessibility

The label comes before the value in the markup so a screen reader announces what the number is. Deltas spell the direction in the text, never only in color.

A row of stats

Portfolio value$412.6M+6.1%vs last quarterAppraisals, Aug 2026
Blended cap rate5.4%−0.2 ptsvs last quarter
Vacancy8.3%No change
WALT4.2 yrsRent roll, Sep 5, 2026

On Carbon

Portfolio value$412.6M+6.1%vs last quarter
Blended cap rate5.4%−0.2 ptsvs last quarter

Specification

PropertyValue
TileLinen fill, 4px corner, 20px 24px padding, 200px minimum, equal widths in a row
Label13px Slate, above the value
ValueText face 28px weight 500, tracked −0.03em, tabular figures. Never the serif
Delta13px weight 500; green 900 up, red 900 down, Slate flat; the sign is part of the text
Source12px Slate with a Stone dot: the document and its date
RowAuto-fit grid, 12px gaps, four at most

Rules

  • Every stat says where the number came from, in the tile or in the page footer.
  • Up is not always good: vacancy up is red. Choose the color by meaning, not by sign.
  • One decimal for percentages, $48.2M for money in a stat, the full figure in the table beneath.
  • Four stats fit a 1080px column; more wrap to a second row.
Show the markupHide the markup7 lines
<div class="wk-stats">
  <div class="wk-stat">
    <span class="wk-stat-label">Portfolio value</span>
    <span class="wk-stat-value">$412.6M</span>
    <span class="wk-stat-meta"><span class="wk-delta wk-delta-up">+6.1%</span> vs last quarter <span class="wk-source">Appraisals, Aug 2026</span></span>
  </div>
</div>

Description list

Facts about one thing, as label and value pairs. It is the detail view's workhorse and the right answer whenever a table would have one row.

Use it for

An asset's details, a lease's terms, a document's metadata.

Not for

Anything with more than one record, which is a table. Prose.

Accessibility

A real dl with dt and dd pairs. Values that are links or badges keep their own semantics inside the dd.

Default

Address
200 Park Avenue, New York, NY 10166
Use class
Class A office
Floor area
18,240 sq ft
In-place rent
$1,240,000
Cap rate
5.8%
Next event
Mar 12, 2027
Status
Leased

On Carbon

Address
200 Park Avenue, New York, NY 10166
Cap rate
5.8%
Status
Leased

Specification

PropertyValue
GridTwo columns: labels at their content width, values fill the rest; 24px between
Rows10px above and below, one Ash rule between pairs, none after the last
Label14px Slate
Value14px Ink, tabular figures
WidthFills its column; pairs never wrap onto one line

Rules

  • Labels are nouns in sentence case: Floor area, Cap rate, Next event.
  • Values carry their units: 18,240 sq ft, 5.8%, $1,240,000.
  • A missing value is an em dash.
  • Six to twelve pairs; beyond that, group them under section titles.
Show the markupHide the markup5 lines
<dl class="wk-dl">
  <dt>Floor area</dt><dd>18,240 sq ft</dd>
  <dt>Cap rate</dt><dd>5.8%</dd>
  <dt>Status</dt><dd><span class="wk-badge wk-badge-green"><i></i>Leased</span></dd>
</dl>

Skeleton

What a page shows while its data loads: the shape of the content in Ash, pulsing. It is not a spinner and not an empty state.

Use it for

Any list, table, stat or card whose data arrives after the page.

Not for

Actions that take under 300ms; show nothing. Errors, which are a note. Missing data, which is an empty state.

Accessibility

The loading region carries aria-busy="true" until the data lands, and the skeleton itself is aria-hidden.

Default

On Carbon

Specification

PropertyValue
FillAsh; gray 300 on Carbon
Shape4px corner; 14px lines, 20px titles at 40% width, 40px table rows
MotionOpacity pulses to 50% and back over 1.2 seconds; stops under reduced motion
LayoutMatches the loaded layout: same number of rows, same heights, so nothing jumps

Rules

  • Show the skeleton in the exact shape of what will load, not a generic block.
  • Never a skeleton and a spinner together.
  • If loading takes more than ten seconds, replace it with a note that says so.
Show the markupHide the markup5 lines
<div class="wk-skeleton-stack" aria-busy="true">
  <span class="wk-skeleton wk-skeleton-title" aria-hidden="true"></span>
  <span class="wk-skeleton wk-skeleton-row" aria-hidden="true"></span>
  <span class="wk-skeleton wk-skeleton-row" aria-hidden="true"></span>
</div>

Density and sizes

The product needs to fit more on a screen than the website does. Every sized component has an extra-small step, and a single wk-dense class on the shell shrinks everything inside it one step at once.

Use it for

Put wk-dense on the app shell for the whole product, or on one panel, table or toolbar that needs to be tighter than the rest.

Not for

The website, and forms or dialogs where the reader is typing carefully; those stay at the default size.

Accessibility

Dense controls are 32px, and 28px at extra small. On touch screens the dense context lifts buttons, inputs, pills and nav items back to 40px automatically.

Default and dense, side by side

Default
Add an assetExportOfficeLeased
Dense
Add an assetExportOfficeLeased

All the sizes

Extra smallSmallDefaultLarge
Extra smallSmallDefaultSmallDefaultLarge
Extra smallname@company.com
Smallname@company.com
Defaultname@company.com

A dense table

AssetTenantIn-place rentCap rateNext eventStatus
200 Park AvenueMarlow Capital$1,240,0005.8%Mar 12, 2027Leased
300 Main StreetVacant, 4th floor$0Vacant
Riverside Commerce CenterKestrel Logistics$684,5006.1%Sep 30, 2026Expires in 25 days

Specification

PropertyValue
Buttons48 large, 40 default, 32 small, 28 extra small; inside wk-dense each drops one step: 40, 32, 28, 24
Inputs40 default, 32 small, 28 extra small; 32 inside wk-dense
Pills32 default, 28 small, 24 extra small
Badges28 large, 22 default, 18 small; 20 inside wk-dense
Table rows40px headers and 10px cell padding by default; 32px and 6px inside wk-dense
Type inside wk-dense13px base, 20px page titles, 16px section titles, 22px stat values
TouchUnder pointer: coarse, dense controls return to 40px

Rules

  • Choose density per screen, not per control: one wk-dense on the shell or panel, not a mix of sizes in one row.
  • Extra small is for toolbars, table cells and inline actions. A primary action is never extra small.
  • Dense tables keep 12px horizontal cell padding; only the row height shrinks.
  • Forms and dialogs stay at the default size even inside a dense shell, unless the fields are in a table.
Show the markupHide the markup7 lines
<div class="wk-shell wk-dense">
  ...
</div>

<button class="wk-btn wk-btn-secondary wk-btn-xs" type="button">Export</button>
<input class="wk-input wk-input-sm" type="text">
<button class="wk-pill wk-pill-xs" type="button">Office</button>

Tabs

Views of the same thing, side by side. A tab switches what the page shows without leaving it; it is not navigation between pages.

Use it for

Sections of one record: Overview, Leases, Documents, Activity. Two to six tabs.

Not for

Steps in a flow, which are a page each. Filters, which are pills.

Accessibility

A tablist of buttons with role tab and aria-selected; the panel has role tabpanel and is labelled by its tab. Arrow keys move between tabs.

Default

OverviewLeases4Documents12Activity

The Overview panel.

On Carbon

OverviewLeases4Documents

Specification

PropertyValue
Tab14px weight 500, 10px 12px padding, Slate at rest, Ink on hover
SelectedInk text with a 2px Ink underline sitting on the list's Ash rule
Count6px after the label, weight 400, Slate, tabular
ListAsh hairline beneath, 20px before the panel
Dense8px 10px padding, 13px

Rules

  • Tabs share one subject. If the tabs are different subjects, they are navigation.
  • The first tab is selected by default and the selected tab is in the URL.
  • Six tabs at most; beyond that, group into a select.
Show the markupHide the markup5 lines
<div class="wk-tabs" role="tablist" aria-label="Asset">
  <button class="wk-tab" role="tab" aria-selected="true" aria-controls="p-overview" id="t-overview">Overview</button>
  <button class="wk-tab" role="tab" aria-selected="false" aria-controls="p-leases" id="t-leases">Leases<span class="wk-tab-count">4</span></button>
</div>
<div id="p-overview" role="tabpanel" aria-labelledby="t-overview">...</div>

Menu and tooltip

A menu lists actions behind a button. A tooltip names a control that has no visible label, and shows its keyboard shortcut.

Use it for

Menu: three or more secondary actions on a row or a page. Tooltip: icon-only controls and shortcuts.

Not for

A menu with one item, which is a button. Tooltips holding help text, which belongs under the field.

Accessibility

The trigger has aria-haspopup and aria-expanded; the menu has role menu and items role menuitem; Escape closes and returns focus. A tooltip is tied to its control with aria-describedby and never holds anything the control does not also expose.

Menu

Open assetEditEExport as CSVArchive

Tooltip

ExportEAdd an asset

On Carbon

Open assetEditArchive
ExportE

Specification

PropertyValue
Menu200px minimum, Paper, 1px Stone border, 4px corner, 6px padding; items 32px, 14px, gray 100 on hover; a danger item in red 900; separators are Ash
Shortcutwk-kbd: 11px Slate in a Stone-bordered key, right-aligned in the item
TooltipInk fill, Paper text, 12px, 5px 8px padding, 4px corner; appears after 300ms on hover or focus, 6px from the control
LayerMenus and tooltips sit at --wk-z-dropdown

Rules

  • Menu items are verbs. Destructive items go last, after a separator, in red.
  • A tooltip is one line. If it needs two, the control needs a visible label.
  • Shortcuts are shown in the tooltip and the menu, never invented per page: keep one map for the whole product.
Show the markupHide the markup10 lines
<button class="wk-btn wk-btn-secondary wk-btn-sm" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="m1">More</button>
<div class="wk-menu" role="menu" id="m1" hidden>
  <button class="wk-menu-item" role="menuitem">Edit <span class="wk-kbd">E</span></button>
  <button class="wk-menu-item" role="menuitem">Export as CSV</button>
  <div class="wk-menu-sep" role="separator"></div>
  <button class="wk-menu-item wk-menu-danger" role="menuitem">Archive</button>
</div>

<button class="wk-btn wk-btn-secondary wk-btn-sm" type="button" aria-describedby="tt1">Export</button>
<span class="wk-tooltip" role="tooltip" id="tt1">Export <span class="wk-kbd">⌘</span><span class="wk-kbd">E</span></span>

Pagination and forms

Two layout pieces the product uses on every list and every edit screen: a pagination row under a table, and a form grid that keeps fields aligned.

Use it for

Pagination under any table longer than fifty rows. The form grid on every create and edit screen.

Not for

Infinite scroll in data tables. Single-column forms wider than 480px.

Accessibility

Pagination is a nav with an aria-label; the current page is aria-current. Form rows are visual only; the tab order follows the source.

Pagination

Showing 1 to 50 of 1,240 assetsPreviousPage 1 of 25Next

Form grid

Address200 Park Avenue
CityNew York
Floor area18,240sq ft
Cap rate5.8%
In-place rent$1,240,000
CancelSave asset

Specification

PropertyValue
Pagination13px Slate; count on the left, Previous and Next as small secondary buttons on the right with the page position between; 16px above
Page size50 rows by default; the size lives in the URL
Form16px between rows; rows are an auto-fit grid of 220px columns, so three fields fit at 700px and stack on a phone
ActionsRight-aligned, ghost Cancel then primary Save, 8px above

Rules

  • Count rows, not pages, in the summary: Showing 1 to 50 of 1,240 assets.
  • Related fields share a row: city and state, area and unit, rent and cap rate.
  • Save is the only primary in a form. Delete lives in a menu or a danger button behind a confirmation.
Show the markupHide the markup18 lines
<nav class="wk-pagination" aria-label="Pagination">
  <span>Showing 1 to 50 of 1,240 assets</span>
  <span class="wk-pagination-spacer"></span>
  <a class="wk-btn wk-btn-secondary wk-btn-sm" href="?page=1" aria-disabled="true">Previous</a>
  <span>Page 1 of 25</span>
  <a class="wk-btn wk-btn-secondary wk-btn-sm" href="?page=2">Next</a>
</nav>

<form class="wk-form">
  <div class="wk-form-row">
    <label class="wk-field"><span>Address</span><input class="wk-input" name="address"></label>
    <label class="wk-field"><span>City</span><input class="wk-input" name="city"></label>
  </div>
  <div class="wk-form-actions">
    <button class="wk-btn wk-btn-ghost" type="button">Cancel</button>
    <button class="wk-btn wk-btn-primary" type="submit">Save asset</button>
  </div>
</form>
07

Implementation

Everything above as CSS custom properties. On westkite.com they live in westkite.css, which also carries every component as wk- classes and is what this page renders with. Plus how the fonts load, and the files an agent needs. Each block is collapsed until you need it.

Tokens

Show the tokensHide the tokens158 lines
:root {
  /* Palette */
  --wk-paper:  #ffffff;
  --wk-linen:  #f7f6f5;
  --wk-ash:    #ebeae9;
  --wk-stone:  #d1cfce;
  --wk-slate:  #737373;
  --wk-ink:    #292929;
  --wk-carbon: #1c1c1c;
  --wk-cobalt: #093acd;   /* the accent */

  /* Scales, light */
  /* gray */
  --wk-gray-100:  #f4f2ef;
  --wk-gray-200:  #ebeae9;
  --wk-gray-300:  #e8e6e3;
  --wk-gray-400:  #eceae7;
  --wk-gray-500:  #d1cfce;
  --wk-gray-600:  #aaa8a5;
  --wk-gray-700:  #918f8d;
  --wk-gray-800:  #737373;
  --wk-gray-900:  #4f4d4b;
  --wk-gray-1000: #292929;
  /* blue */
  --wk-blue-100:  #f2f6ff;
  --wk-blue-200:  #edf3ff;
  --wk-blue-300:  #e5edfe;
  --wk-blue-400:  #d6e4ff;
  --wk-blue-500:  #abc6fe;
  --wk-blue-600:  #7ca5fd;
  --wk-blue-700:  #093acd;
  --wk-blue-800:  #0228b2;
  --wk-blue-900:  #042fb8;
  --wk-blue-1000: #00002f;
  /* red */
  --wk-red-100:  #ffeef0;
  --wk-red-200:  #ffe9ea;
  --wk-red-300:  #ffe4e5;
  --wk-red-400:  #ffd8d7;
  --wk-red-500:  #ffb5b6;
  --wk-red-600:  #ff6a6e;
  --wk-red-700:  #fc0035;
  --wk-red-800:  #e70022;
  --wk-red-900:  #d60020;
  --wk-red-1000: #46000c;
  /* amber */
  --wk-amber-100:  #fff6e1;
  --wk-amber-200:  #fff4d4;
  --wk-amber-300:  #fff1c8;
  --wk-amber-400:  #ffdd84;
  --wk-amber-500:  #ffc85e;
  --wk-amber-600:  #ffaa00;
  --wk-amber-700:  #ffb200;
  --wk-amber-800:  #ff9900;
  --wk-amber-900:  #a64f00;
  --wk-amber-1000: #541c00;
  /* green */
  --wk-green-100:  #ecfdec;
  --wk-green-200:  #e5fce7;
  --wk-green-300:  #d3fad1;
  --wk-green-400:  #b9f5bc;
  --wk-green-500:  #82eb8d;
  --wk-green-600:  #4ce15e;
  --wk-green-700:  #28a948;
  --wk-green-800:  #279141;
  --wk-green-900:  #107d32;
  --wk-green-1000: #00370d;
  /* teal */
  --wk-teal-100:  #dffffb;
  --wk-teal-200:  #ddfef6;
  --wk-teal-300:  #ccf9f1;
  --wk-teal-400:  #b1f7ec;
  --wk-teal-500:  #52f0db;
  --wk-teal-600:  #00e2c4;
  --wk-teal-700:  #00a694;
  --wk-teal-800:  #008d7d;
  --wk-teal-900:  #007a6e;
  --wk-teal-1000: #003d34;
  /* purple */
  --wk-purple-100:  #f9f0ff;
  --wk-purple-200:  #f9f1ff;
  --wk-purple-300:  #f5e8ff;
  --wk-purple-400:  #f1d9ff;
  --wk-purple-500:  #dda9ff;
  --wk-purple-600:  #c77dff;
  --wk-purple-700:  #9f00f4;
  --wk-purple-800:  #8400cd;
  --wk-purple-900:  #7c00c9;
  --wk-purple-1000: #2e004d;
  /* pink */
  --wk-pink-100:  #ffeaf5;
  --wk-pink-200:  #ffeaf2;
  --wk-pink-300:  #ffe0eb;
  --wk-pink-400:  #ffd5e1;
  --wk-pink-500:  #fdb3cc;
  --wk-pink-600:  #f97ea7;
  --wk-pink-700:  #f22782;
  --wk-pink-800:  #e4106e;
  --wk-pink-900:  #c41562;
  --wk-pink-1000: #460523;

  /* Semantic: surfaces and text */
  --wk-bg:            var(--wk-paper);
  --wk-bg-2:          var(--wk-linen);
  --wk-surface:       var(--wk-paper);
  --wk-surface-2:     var(--wk-linen);
  --wk-text:          var(--wk-ink);
  --wk-text-muted:    var(--wk-slate);
  --wk-text-inverse:  var(--wk-paper);
  --wk-border:        var(--wk-stone);
  --wk-hairline:      var(--wk-ash);
  --wk-hover:         var(--wk-gray-100);
  --wk-pressed:       var(--wk-gray-200);
  --wk-selected:      var(--wk-blue-200);
  --wk-focus:         var(--wk-cobalt);
  --wk-accent:        var(--wk-cobalt);
  --wk-disabled-bg:   var(--wk-gray-300);
  --wk-disabled-text: var(--wk-gray-700);
  --wk-backdrop:      rgba(41, 41, 41, 0.32);

  /* Semantic: status */
  --wk-success:      var(--wk-green-700);
  --wk-success-text: var(--wk-green-900);
  --wk-success-bg:   var(--wk-green-200);
  --wk-warning:      var(--wk-amber-700);
  --wk-warning-text: var(--wk-amber-900);
  --wk-warning-bg:   var(--wk-amber-200);
  --wk-error:        var(--wk-red-700);
  --wk-error-text:   var(--wk-red-900);
  --wk-error-bg:     var(--wk-red-200);

  /* Type */
  --wk-display:        "Martina Plantijn", "Newsreader", Georgia, "Times New Roman", serif;
  --wk-display-weight: 375;          /* 300 once Martina Plantijn is licensed */
  --wk-display-vs:     "opsz" 28;    /* Newsreader only; ignored by Martina */
  --wk-display-track:  -0.03em;
  --wk-sans:           "STK Bureau Sans", "Inter", system-ui, -apple-system, sans-serif;
  --wk-sans-track:     -0.04em;

  /* Shape and motion */
  --wk-radius: 4px;              /* the one corner */
  --wk-ease:   160ms ease;

  /* Layers */
  --wk-z-dropdown: 100;
  --wk-z-sticky:   200;
  --wk-z-dialog:   300;
  --wk-z-toast:    400;

  /* Charts: series in order, grid and axis */
  --wk-series-1: var(--wk-blue-700);
  --wk-series-2: var(--wk-teal-700);
  --wk-series-3: var(--wk-purple-700);
  --wk-series-4: var(--wk-pink-700);
  --wk-series-5: var(--wk-amber-700);
  --wk-series-6: var(--wk-green-700);
  --wk-series-7: var(--wk-gray-700);
  --wk-chart-grid: var(--wk-ash);
  --wk-chart-axis: var(--wk-slate);

  /* Provenance. Do not remove. */
  --wk-signature: "West Kite Design System 1.11 wk-78fa6fe5";
}

/* Base */
.wk, .wk-body { font-family: var(--wk-sans); letter-spacing: var(--wk-sans-track); line-height: 1.4; color: var(--wk-text); background: var(--wk-bg); font-variant-numeric: tabular-nums; -webkit-font-smoothing: antialiased; }
.wk-display, .wk h1, .wk h2 { font-family: var(--wk-display); font-weight: var(--wk-display-weight); font-variation-settings: var(--wk-display-vs); letter-spacing: var(--wk-display-track); line-height: 1; font-style: normal; text-transform: none; }
.wk-figure { font-family: var(--wk-sans); letter-spacing: var(--wk-sans-track); font-variant-numeric: tabular-nums; }
.wk-title { font-family: var(--wk-sans); font-weight: 500; letter-spacing: -0.03em; line-height: 1.3; margin: 0; }
.wk-muted { color: var(--wk-text-muted); }
.wk-focus-ring, .wk-btn:focus-visible, .wk-pill:focus-visible, .wk-input:focus-visible, .wk-control input:focus-visible + i, .wk-card-interactive:focus-visible { outline: 2px solid var(--wk-focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .wk *, .wk-btn, .wk-pill, .wk-table-body td, .wk-switch::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; } }

Tokens on Carbon

Apply on any element with a Carbon background. The scales invert so step numbers keep their jobs.

Show the Carbon tokensHide the Carbon tokens97 lines
/* Apply to any element with a Carbon background. Scales invert so step numbers keep their jobs. */
.on-carbon {
  --wk-bg:            var(--wk-carbon);
  --wk-bg-2:          #262626;
  --wk-surface:       var(--wk-carbon);
  --wk-surface-2:     #262626;
  --wk-text:          var(--wk-linen);
  --wk-text-muted:    rgba(255, 255, 250, 0.6);
  --wk-text-inverse:  var(--wk-ink);
  --wk-border:        rgba(255, 255, 255, 0.22);
  --wk-hairline:      rgba(255, 255, 255, 0.11);
  --wk-hover:         #2a2927;
  --wk-pressed:       #2f2e2c;
  --wk-selected:      #152c63;
  --wk-focus:         var(--wk-linen);
  --wk-accent:        var(--wk-linen);
  --wk-disabled-bg:   #2f2e2c;
  --wk-disabled-text: #7f7d7b;
  --wk-backdrop:      rgba(0, 0, 0, 0.5);
  /* gray */
  --wk-gray-100:  #1b1a18;
  --wk-gray-200:  #201f1d;
  --wk-gray-300:  #2a2927;
  --wk-gray-400:  #2f2e2c;
  --wk-gray-500:  #474543;
  --wk-gray-600:  #898785;
  --wk-gray-700:  #918f8d;
  --wk-gray-800:  #7f7d7b;
  --wk-gray-900:  #a2a09d;
  --wk-gray-1000: #f7f6f5;
  /* blue */
  --wk-blue-100:  #0a183a;
  --wk-blue-200:  #0e2049;
  --wk-blue-300:  #152c63;
  --wk-blue-400:  #193372;
  --wk-blue-500:  #1f3e88;
  --wk-blue-600:  #5388fe;
  --wk-blue-700:  #346cf7;
  --wk-blue-800:  #295bd9;
  --wk-blue-900:  #74a0ff;
  --wk-blue-1000: #eff4ff;
  /* red */
  --wk-red-100:  #330a11;
  --wk-red-200:  #440d13;
  --wk-red-300:  #5d0e17;
  --wk-red-400:  #6f101b;
  --wk-red-500:  #88151f;
  --wk-red-600:  #f32e40;
  --wk-red-700:  #f13242;
  --wk-red-800:  #e2162a;
  --wk-red-900:  #ff5e63;
  --wk-red-1000: #ffeaed;
  /* amber */
  --wk-amber-100:  #291800;
  --wk-amber-200:  #331b00;
  --wk-amber-300:  #4f2900;
  --wk-amber-400:  #573200;
  --wk-amber-500:  #6c4100;
  --wk-amber-600:  #e99c00;
  --wk-amber-700:  #ffb200;
  --wk-amber-800:  #ff9900;
  --wk-amber-900:  #ff9900;
  --wk-amber-1000: #fff3d9;
  /* green */
  --wk-green-100:  #00250a;
  --wk-green-200:  #003110;
  --wk-green-300:  #003814;
  --wk-green-400:  #004616;
  --wk-green-500:  #00661d;
  --wk-green-600:  #009431;
  --wk-green-700:  #00ab3e;
  --wk-green-800:  #009335;
  --wk-green-900:  #00ca52;
  --wk-green-1000: #daffe5;
  /* teal */
  --wk-teal-100:  #00211b;
  --wk-teal-200:  #002922;
  --wk-teal-300:  #003b33;
  --wk-teal-400:  #003f35;
  --wk-teal-500:  #005f53;
  --wk-teal-600:  #009885;
  --wk-teal-700:  #00a794;
  --wk-teal-800:  #008d7d;
  --wk-teal-900:  #00c9b5;
  --wk-teal-1000: #cefff5;
  /* purple */
  --wk-purple-100:  #290c33;
  --wk-purple-200:  #341142;
  --wk-purple-300:  #47185e;
  --wk-purple-400:  #541a76;
  --wk-purple-500:  #642290;
  --wk-purple-600:  #9440d5;
  --wk-purple-700:  #9440d5;
  --wk-purple-800:  #7d2bba;
  --wk-purple-900:  #c472fb;
  --wk-purple-1000: #faedff;
  /* pink */
  --wk-pink-100:  #310d1e;
  --wk-pink-200:  #420c25;
  --wk-pink-300:  #571032;
  --wk-pink-400:  #5d0c34;
  --wk-pink-500:  #76063f;
  --wk-pink-600:  #b90056;
  --wk-pink-700:  #f12b82;
  --wk-pink-800:  #e6006e;
  --wk-pink-900:  #ff518d;
  --wk-pink-1000: #ffeaf4;
  background: var(--wk-bg);
  color: var(--wk-text);
}

/* A light island inside a Carbon context: restores the light set. The page applies on-carbon to the root in dark mode and on-paper to fixed-light examples. */
.on-paper {
  --wk-bg:            var(--wk-paper);
  --wk-bg-2:          var(--wk-linen);
  --wk-surface:       var(--wk-paper);
  --wk-surface-2:     var(--wk-linen);
  --wk-text:          var(--wk-ink);
  --wk-text-muted:    var(--wk-slate);
  --wk-text-inverse:  var(--wk-paper);
  --wk-border:        var(--wk-stone);
  --wk-hairline:      var(--wk-ash);
  --wk-hover:         var(--wk-gray-100);
  --wk-pressed:       var(--wk-gray-200);
  --wk-selected:      var(--wk-blue-200);
  --wk-focus:         var(--wk-cobalt);
  --wk-accent:        var(--wk-cobalt);
  --wk-disabled-bg:   var(--wk-gray-300);
  --wk-disabled-text: var(--wk-gray-700);
  --wk-backdrop:      rgba(41, 41, 41, 0.32);
  /* gray */
  --wk-gray-100:  #f4f2ef;
  --wk-gray-200:  #ebeae9;
  --wk-gray-300:  #e8e6e3;
  --wk-gray-400:  #eceae7;
  --wk-gray-500:  #d1cfce;
  --wk-gray-600:  #aaa8a5;
  --wk-gray-700:  #918f8d;
  --wk-gray-800:  #737373;
  --wk-gray-900:  #4f4d4b;
  --wk-gray-1000: #292929;
  /* blue */
  --wk-blue-100:  #f2f6ff;
  --wk-blue-200:  #edf3ff;
  --wk-blue-300:  #e5edfe;
  --wk-blue-400:  #d6e4ff;
  --wk-blue-500:  #abc6fe;
  --wk-blue-600:  #7ca5fd;
  --wk-blue-700:  #093acd;
  --wk-blue-800:  #0228b2;
  --wk-blue-900:  #042fb8;
  --wk-blue-1000: #00002f;
  /* red */
  --wk-red-100:  #ffeef0;
  --wk-red-200:  #ffe9ea;
  --wk-red-300:  #ffe4e5;
  --wk-red-400:  #ffd8d7;
  --wk-red-500:  #ffb5b6;
  --wk-red-600:  #ff6a6e;
  --wk-red-700:  #fc0035;
  --wk-red-800:  #e70022;
  --wk-red-900:  #d60020;
  --wk-red-1000: #46000c;
  /* amber */
  --wk-amber-100:  #fff6e1;
  --wk-amber-200:  #fff4d4;
  --wk-amber-300:  #fff1c8;
  --wk-amber-400:  #ffdd84;
  --wk-amber-500:  #ffc85e;
  --wk-amber-600:  #ffaa00;
  --wk-amber-700:  #ffb200;
  --wk-amber-800:  #ff9900;
  --wk-amber-900:  #a64f00;
  --wk-amber-1000: #541c00;
  /* green */
  --wk-green-100:  #ecfdec;
  --wk-green-200:  #e5fce7;
  --wk-green-300:  #d3fad1;
  --wk-green-400:  #b9f5bc;
  --wk-green-500:  #82eb8d;
  --wk-green-600:  #4ce15e;
  --wk-green-700:  #28a948;
  --wk-green-800:  #279141;
  --wk-green-900:  #107d32;
  --wk-green-1000: #00370d;
  /* teal */
  --wk-teal-100:  #dffffb;
  --wk-teal-200:  #ddfef6;
  --wk-teal-300:  #ccf9f1;
  --wk-teal-400:  #b1f7ec;
  --wk-teal-500:  #52f0db;
  --wk-teal-600:  #00e2c4;
  --wk-teal-700:  #00a694;
  --wk-teal-800:  #008d7d;
  --wk-teal-900:  #007a6e;
  --wk-teal-1000: #003d34;
  /* purple */
  --wk-purple-100:  #f9f0ff;
  --wk-purple-200:  #f9f1ff;
  --wk-purple-300:  #f5e8ff;
  --wk-purple-400:  #f1d9ff;
  --wk-purple-500:  #dda9ff;
  --wk-purple-600:  #c77dff;
  --wk-purple-700:  #9f00f4;
  --wk-purple-800:  #8400cd;
  --wk-purple-900:  #7c00c9;
  --wk-purple-1000: #2e004d;
  /* pink */
  --wk-pink-100:  #ffeaf5;
  --wk-pink-200:  #ffeaf2;
  --wk-pink-300:  #ffe0eb;
  --wk-pink-400:  #ffd5e1;
  --wk-pink-500:  #fdb3cc;
  --wk-pink-600:  #f97ea7;
  --wk-pink-700:  #f22782;
  --wk-pink-800:  #e4106e;
  --wk-pink-900:  #c41562;
  --wk-pink-1000: #460523;
  background: var(--wk-bg);
  color: var(--wk-text);
}

For agents

The whole system as instructions: agent.md (full) and agent-compact.md (four kilobytes). Machine index at llms.txt with everything at llms-full.txt. Tokens as tokens.json, a Tailwind preset, and a Claude Code skill. Every file carries the license header, the wk- prefixes and the signature token; anything built outside West Kite must keep them and show the credit line.

Fonts

Show the font declarationsHide the font declarations24 lines
/* Both faces are self-hosted from /fonts/. No third-party requests. */
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/Newsreader-latin.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  src: url("/fonts/Newsreader-latin-ext.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter";
  src: url("/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

Both stand-ins ship from the site's own fonts folder, which its content security policy requires. When Martina Plantijn and STK Bureau Sans are licensed, add their files alongside and change the display weight token to 300; nothing else needs to change.