Button

Components

Every component on one page, one section each: what it is for, its variations and states, its sizes.

Every component the screens of Valere CRM are drawn with: the standard set, the product’s own — the record, the AI layer and the decisions on it — and a reserve. Each says what it is for, its variants, states, sizes and properties, and where it comes from: a component of the pilot (web/shared/components), a slide of the design concept, or proposed.

Button

Variants and sizes; hover, focus, disabled and loading; with an icon.

Source: web/shared/components/Button

A button starts an action: create, save, move a Site on, remove. The rule that matters: one primary button to a view, and everything else is secondary or ghost; an action that removes something is red and says what it removes.

Variants

Primary
Secondary
Ghost
Destructive
Remove
Remove, quiet
VariantLook in tokensUsed for
Primary
btn--primary
--color-primary fill, --color-primary-foreground label; hover --color-primary-hoverThe one main action of a view: Create opportunity, Save changes.
Secondary
btn--secondary
--color-surface fill, --color-emphasis border, --color-foreground label; hover --color-surface-mutedThe other actions beside it: Cancel, Back, Export.
Ghost
btn--ghost
No fill and no border, --color-foreground label; hover --color-surface-mutedQuiet actions in a toolbar or a row: Log activity, Filters.
Destructive
btn--danger
--color-danger fill, --color-danger-foreground label; hover --color-danger-hoverThe last step of a removal, in its confirm dialog: Delete Lead.
Remove
c-button--remove
--color-surface fill, --color-danger-text border and label; hover --color-danger-softA page header’s Remove: outlined, so it never reads as the page’s main action.
Remove, quiet
c-button--remove-quiet
No fill, --color-danger-text label; hover --color-danger-softA table row’s Remove, quiet beside the row’s other actions.

States

Default
Hover
Focus
Disabled
Loading
Default
Hover
Focus
Disabled
Loading
Click it: it loads for two seconds
Complete the Site Review checklist first.Disabled, with its reason under it

proposed The loading state is proposed: the pilot’s button has none. The label stays in place, the spinner sits over it and the button reads aria-busy.

Sizes

Default, 36 px
Small, 32 px
SizeHeightUsed for
Default
btn
--size-controlForms, dialogs and page headers; the height of a field, so a field and its button line up.
Small
btn--sm
--size-control-smToolbars, table rows and the top bar, where a 36 px control would crowd.

proposed The small size is proposed: the pilot has one height, 36 px.

With an icon

The icon sits before the label and never replaces it, except where the icon is the whole button: then it is square and carries an aria-label.

Full width

Properties

NameTypeDefaultWhat it does
variantprimary | secondary | ghost | destructive | remove | ghostRemoveprimaryWhich button it is, from the Variants table.
fullWidthbooleanfalseStretches to the width of its container; a sign-in form’s button.
disabledbooleanfalseNot available. A grey fill and a not-allowed cursor; it is also off the tab order.
disabledReasonstring—Shown under the button while it is disabled and tied to it with aria-describedby, so a screen reader hears why.
reasonAlignstart | endstartThe edge the button and its reason line up on; end for a button at the right edge of a header.
typebutton | submitbuttonA button inside a form that submits it says so; every other button is button.
childrenReactNode—The label, in sentence case; an icon goes before it.

Text field

Label, hint and error; disabled and read-only; password; the text area.

Source: web/shared/components/TextField

A text field takes one line of free text, or several in the text area. The rule that matters: the label is visible text above the field, never only a placeholder, and the hint and the error sit under the field and are tied to it, so a screen reader reads them with it.

Variants

Shown in lists and on the Monday meeting board.

Church Farm’s fee, excluding VAT.

Up to 5,000 characters.

VariantLook in tokensUsed for
Text
type="text | email | numeric"
--color-surface fill, --color-emphasis border, --radius-control, 36 px high; label above in --color-mutedA name, an email, a figure: one line of free text.
Password
type="password"
As text; a Show / Hide button in --color-primary sits inside the field’s right edgeSign-in and a new password; Show reveals the text and says what it did to a screen reader.
Text area
c-input--area
As text, 88 px at least, resizable up and downA note, a reason, a meeting summary.
With a prefix
c-input--prefixed
As text; the symbol in --color-muted inside the left edgeMoney in £: a fee, a land value.

States

Default
Hover
Focus
Filled

Enter a Site Code like CE-014.

Error
Disabled
Read only

An error is the red border and a line under the field with an icon and the words: what is wrong and what to do. It is never the colour alone. A disabled field leaves the tab order; a read-only one stays in it, so its value can be selected and copied. A field another function owns is a read-only field with its owner named: see Locked field.

Sizes

Default, 36 px
Compact, 32 px

proposed The compact size is proposed, for a field in a toolbar next to a small button. The pilot’s field is 36 px, the height of a button.

Properties

NameTypeDefaultWhat it does
labelstring—The visible label, in sentence case, tied to the field with for.
hintstring—A line of help under the field, such as “Today when left empty”.
errorstring | boolean—A string shows under the field with a warning icon; true only marks the field, for a form-level alert that already says what is wrong.
readOnlyHintstring—On a read-only field: names the function that owns it, with a lock (see Locked field).
typestringtexttext, email or password; a password gets the Show / Hide button.
readOnlybooleanfalseGrey fill; the value can be selected and copied but not changed.
disabledbooleanfalseNot available; out of the tab order.
value, onChange, placeholder, idinput props—Passed on to the input. A field without an id gets one, so label, hint and error always join it.

Select

The own combobox (ADR-0040): a labelled field and a compact filter, the list that opens, the keyboard.

Source: web/shared/components/Dropdown, Select and the crm app’s LabelledSelect (ADR-0040)

A select chooses one value from a list. The product draws it itself (ADR-0040): a field-looking button and a panel on the tokens, never the browser’s or the system’s list, which ignores the theme. The rule that matters: it works from the keyboard as a native select does, and the list opens above the field when there is no room below.

Variants

Land moves a Site on.

Compact filter: no label, each option carries it
VariantLook in tokensUsed for
Labelled field
LabelledSelect
A field’s look: --color-emphasis border, --radius-control, 36 px; the label above; hint or error under. The list is --color-surface with --shadow-overlay and --radius-card.Forms: Stage, Region, Site, Landowner.
Compact filter
Select
The same control without a label: each option carries it (“Region: All”), so the chosen filter reads on its own. At least 160 px wide.Filter bars over a table or a board; the label is also the accessible name.

The list that opens

Both are live: open them with the pointer or the keyboard. The first holds nine stages; the second holds fourteen registered providers, so it scrolls and type-ahead earns its place: try typing “ma”.

Tab to it, then use ↑ ↓ or type a letter.

A long list scrolls inside the panel.

Open, drawn

The same list held open. Groups have a quiet title and a divider; the highlighted option is in the brand’s soft tint; the chosen one is semibold with a check; an option that cannot be chosen is grey, says why, and is skipped by the arrows.

Fee Discussion
Site Review
Instruction Approval
Acceptable Offer
Under Offer
Legals
ExchangedAfter Legals
Completed
Dead
Open below the field
Site Review
Instruction Approval
Acceptable Offer
Open above, when there is no room below

Keyboard

KeyWhat it does
↑ ↓Closed: opens the list. Open: moves the highlight one option; it stops at the ends and skips an option that cannot be chosen.
Home End PgUp PgDnThe first or last option; ten options up or down in a long list.
LettersType-ahead: the letters typed in a row find the option that starts with them; the same letter again steps to the next. Open, it moves the highlight; closed, it chooses.
Enter SpaceClosed: opens the list. Open: chooses the highlighted option and closes.
EscCloses the list and keeps the focus on the field. The value stays as it was.
TabCloses the list and moves on; nothing is chosen.
PointerA click opens and closes; the option under the pointer is highlighted; a click outside closes. The focus never leaves the field.

ARIA: the field is role="combobox" with aria-haspopup="listbox", aria-expanded and aria-controls; the highlighted option is named by aria-activedescendant, so the focus stays on the field; the panel is role="listbox", each option role="option" with aria-selected.

States

Default
Empty
Hover
Focus

Choose a stage to move the Site on.

Error
Disabled

Open is its own state, drawn above. A disabled select leaves the tab order; one that is read-only because another function owns the value is the locked select of Locked field.

Sizes

SizeHeightUsed for
Default--size-controlA field in a form; the options are 36 px as well.
Small
c-select--sm
--size-control-smA filter beside small buttons in a toolbar.

proposed The small size is proposed; the pilot’s select is 36 px.

Properties

NameTypeDefaultWhat it does
labelstring—The control’s name, on the field and on its list. The visible label above the field reaches the button through for.
valuestring—The chosen option’s value. The empty string is the placeholder or “All”.
options{ value, label, typeahead? }[]—What the list holds; typeahead is what typing matches when it is not the label (the filter’s “Region: Central” is found by “c”).
onChange(value) => void—Called when another option is chosen.
disabledbooleanfalseNot available; out of the tab order.
invalidbooleanfalseThe red border and aria-invalid; the message sits under the field.
requiredbooleanfalseSets aria-required; the label carries the asterisk.
classNamestring—The width of the field: a form’s full width, a filter’s minimum.

Checkbox, radio, switch

One or many of a list, one of a few, on or off.

Source: web/packages/crm/src/pages/NewLead.tsx, RecommendedBuyers.tsx and LandTermsCard.tsx (the pilot draws them with the browser’s own checkbox and radio; the look here is proposed)

Checkboxes choose any of a list, radios choose one of a few, a switch turns one thing on or off. The rule that matters: the whole label is the target, the group has a legend, and a switch always carries the word for its state, so it never depends on colour or position alone.

Variants

Notify when a Site changes stage
Deal type
Land terms agreedOff

Set by Land. Heads of Terms needs it.

VariantLook in tokensUsed for
Checkbox
type="checkbox"
16 px box, --radius-sm, --color-emphasis border; ticked: --color-primary fill with a white tick; mixed: a dash instead of the tickZero, one or many of a list; a single yes: “Hide buyers already in the journey”.
Radio
type="radio"
Round, 16 px; chosen: --color-primary fill with a dotOne of a few, all shown at once: Deal type, Landowner. More than about five go in a Select.
Switch
role="switch"
44 × 24 px pill; off: --color-subtle, knob left; on: --color-primary, knob right; the word On or Off beside itA setting that takes effect at once: Land terms agreed. A choice that needs a Save is a checkbox.

States

Checkbox

Unchecked
Hover
Focus
Checked
Mixed
Disabled
Disabled, checked

Tick to confirm before moving on.

Error

Radio

Unchecked
Hover
Focus
Checked
Disabled
Disabled, checked

Switch

Land terms agreedOff
Off
Land terms agreedOn
On
Land terms agreedOff
Hover
Land terms agreedOff
Focus
Land terms agreedOff
Disabled
Land terms agreedOn
Disabled, on

A disabled choice is grey and at half strength, and leaves the tab order. When it is disabled because another function owns it, name the owner beside it: see Locked field.

Sizes

ControlSizeTarget
Checkbox and radio16 × 16 pxThe whole label line, at least 20 px high.
Switch44 × 24 pxThe switch itself; the label names it with aria-labelledby.

Properties

NameTypeDefaultWhat it does
labelstring—The text beside the control; clicking it toggles the control.
hintstring—A line under the label that says more, in --color-subtle.
checkedbooleanfalseThe state of a checkbox or a radio. A radio group shares one name, and the arrow keys move within it.
indeterminatebooleanfalseA parent checkbox whose children are some on and some off; it is set from script and turns every child on or off.
disabledbooleanfalseNot available; out of the tab order.
aria-checkedbooleanfalseOn a switch (role="switch"): the state; the switch is a button, so Space and Enter toggle it.
aria-invalidbooleanfalseThe red border; the message sits under the control and names what to do.

Date picker

A date and a range; the calendar and its keyboard; UK dates.

proposed The pilot uses the browser’s date input, type="date" in TextField; this is the product’s own calendar, on the model of its lists (ADR-0040).

A date picker takes a day, or a start and an end. Dates are UK ones: dd/mm/yyyy, weeks from Monday. The rule that matters: the field can always be typed in, the calendar is only the other way in, and the month and year are lists drawn by the product, with the keyboard of a Select.

Variants

Today when left empty.

Exchange window

Church Farm, CE-014: from the exchange date to the longstop.

VariantLook in tokensUsed for
Date
mode="single"
A text field with a calendar button inside its right edge; the calendar is a --radius-card panel on --color-surface with --shadow-overlay; the day chosen is --color-primary, today has a ringOne day: Instructed on, a meeting date, a longstop.
Date range
mode="range"
Two date boxes in one field frame; the days between are in --color-primary-soft, the ends in --color-primaryFrom and to: an exchange window, a reporting period.

The calendar, drawn

The calendar held open, as it looks. Left: a date, with today ringed and the days after today out of reach. Middle: a range being chosen, with its two ends and the days between. Right: the month list open, the same panel as a Select’s.

A date, not in the future
A range, both ends chosen
The month list open

Keyboard

KeyWhat it does
← →The day before or after; past the month’s edge, the calendar turns to the next month.
↑ ↓The same weekday a week earlier or later.
Home EndThe Monday or the Sunday of the week.
PgUp PgDnThe previous or next month, on the same day; with Shift, the previous or next year.
Enter SpaceChooses the day. A date closes the calendar; a range keeps it open for the second date.
EscCloses the calendar, or the month or year list if one is open, and puts the focus back.
TabMoves to Today and Clear; leaving the calendar closes it.

In the field: type 09/11/2026 (a full stop or a hyphen works too). The calendar follows when the field is left or Enter is pressed; text that is not a real date marks the field and says what to type. The month and year lists open from their buttons and keep a Select’s keys.

ARIA: the calendar button is aria-haspopup="dialog" with aria-expanded; the calendar is a role="dialog" holding a role="grid" of days, each a button named by its full date and aria-selected when chosen; today is aria-current="date"; month and year are the comboboxes of Select.

States

Today when left empty.

Default

Today when left empty.

Focus

Today when left empty.

Enter a date as dd/mm/yyyy, for example 09/11/2026.

Error

Today when left empty.

Disabled

Days in the calendar

12A day
12Hover
12Focus
9Today: a ring
5Chosen: filled
20Between the ends
30Another month
14Out of reach: struck through

Sizes

PartSizeUsed for
Field--size-control36 px high, 280 px wide for a date and 320 px for a range.
Calendar276 pxSeven 36 px days across and six weeks down, so the panel never changes height from one month to the next.

Properties

NameTypeDefaultWhat it does
labelstring—The visible label; for a range it names the group of two boxes.
modesingle | rangesingleA day, or a start and an end. A range takes its first click as the start and its second as the end, swapping them if they come the wrong way round.
valueyyyy-mm-dd | [start, end]—The day, as the browser’s date input would hold it; it is shown as dd/mm/yyyy.
min, maxyyyy-mm-dd—The days out of reach: struck through and not choosable; typing one gives the range message. Instructed on stops at today.
onChange(value) => void—Called with the new value, or empty when cleared.
hint, errorstring—As the Text field: help under the field, and the message that names what to type.
disabledbooleanfalseNot available; both the boxes and the button leave the tab order.

Locked field

A field another function owns: read only, with the owner named.

Source: web/shared/components/TextField (readOnlyHint, LockedHint), role rules FR-25

A locked field shows a value that another function owns. The rule that matters: the field stays on the screen and says who owns it and who may not change it, in the same words everywhere (FR-25); it is never hidden, because a missing field only raises the question where it went.

Variants

Seen by Land

The Site this opportunity is on.

Seen by Construction

Land and Partnerships create opportunities. Read only for Construction.

Land moves this deal on. Read only for Construction.

Land terms agreedNot agreed yet

Set by Land. Read only for Construction.

Set by Back Office. Read only for Land.

VariantLook in tokensUsed for
Locked text field
readOnly + readOnlyHint
--color-surface-muted fill, --color-default border, --color-muted text; focusable and selectable; the hint under it has a lock icon in --color-mutedA value another function owns: Site Code, Fee basis, Land value.
Locked select
aria-readonly
The field look above, with no chevron; it does not openThe Stage, for a role that cannot move it.
Locked switch
disabled
The switch at half strength with its state in words; the hint beside itLand terms agreed, for Construction.
The hint alone
LockedHint
A lock icon and a line in --text-xs and --color-muted, under or beside the controlNext to a button, a tab or a switch that the role cannot use.

The words

Every locked thing names its owner the same way: who sets it, then who may only read it.

PatternExampleUsed for
Set by {owner}. Read only for {role}.Set by Partnerships. Read only for Construction.A field the user does not own.
{Owner} moves this deal on. Read only for {role}.Land moves this deal on. Read only for Construction.The stage move, and the buttons that make it.
{Owner} and {owner} create {things}. Read only for {role}.Land and Partnerships create opportunities. Read only for Construction.A form the user may open but not submit.
Read only.Read only.When no role is known; for example before the session has loaded.

In a form

Locked fields sit among editable ones at full width, so the form keeps its shape; only the fill, the border and the hint set them apart.

Seen by Land

Edit Site

Set by Back Office. Read only for Land.

States

Set by Partnerships. Read only for Construction.

Locked

Set by Partnerships. Read only for Construction.

Focus

Set by Partnerships. Read only for Construction.

Locked, no value yet

You set this one.

For its owner: editable

A locked field has no hover change: it does not invite a click. It keeps the focus ring, so a keyboard user can reach it and a screen reader reads its value and its hint. An empty one says so in the field.

Sizes

Default, 36 px
Compact, 32 px

The sizes are the Text field’s: a locked field is the same control with another state.

Properties

NameTypeDefaultWhat it does
readOnlybooleanfalseMakes the field read only: grey fill, value selectable, aria-readonly.
readOnlyHintstring—The owner’s sentence, shown under the read-only field with a lock and joined to it by aria-describedby.
LockedHint → childrenstring—The same line on its own, for a control that is not a field: a stage move, a switch, a tab’s buttons.
LockedHint → idstring—What the control points at with aria-describedby.
roleRole—The viewer’s role: the line ends “Read only for” and the role’s name, or is just “Read only.” when none is known.

Badge and status

Neutral badges and the status tones with their icons.

Source: web/shared/components/Chip, StatusPill, StatusIcon, HealthChip

A badge is a small pill that names a state or a category. A status badge pairs its tone with an icon and a word, so a status is never read from colour alone: an icon and a label always stand beside the colour.

Variants

Land & BuildNeutral tag
S106Neutral tag
12Count
Fee DiscussionStage chip
Under OfferStage chip
Acceptable OfferStage chip
LegalsStage chip
CompletedCompleted
DeadDead
On trackStatus pill, good
At riskStatus pill, warning
Stalled: no contact 21 daysStatus pill, critical
AwaitingInfo pill
ActiveActive status
72Deal HealthHealth chip, good (70 and above)
55Deal HealthHealth chip, fair (40–69)
34Deal HealthHealth chip, poor (below 40)
VariantLook in tokensUsed for
Neutral tag--color-surface, --color-default border, --color-muted textA plain label: a deal type, a count, a category.
Stage chip--color-stage-tint on --color-stage-text, a dot before the wordThe stage a Site is in. In a table cell the border and the dot go.
Stage, completed and dead--color-stage-done-* and --color-stage-dead-*A Site that closed: Completed in green, Dead in grey.
Status pill--color-success-soft, --color-warning-soft, --color-danger-soft with their text colours, and an iconOn track, At risk, Stalled; the stalled pill names the rule that fired.
Info pill--color-info-soft on --color-info-textA state that is neither good nor bad: Awaiting, Not started.
Health chip--color-health-good-*, --color-health-fair-*, --color-health-poor-*, the band’s icon and the scoreThe Deal Health score at a glance; as a button it opens the breakdown.

States

Default
Hover
Focus
No score: the deal is closedNo score

A badge on its own is not interactive and has no hover. Only the health chip is a button; it takes the hover tint and the focus ring.

Sizes

LegalsDefault, 24 px
12Small, 20 px: counts in tabs

Properties

NameTypeDefaultWhat it does
tone'neutral' | 'good' | 'warn' | 'crit' | 'info''neutral'The status tone; every tone but neutral shows its icon.
kind'tag' | 'stage' | 'dealType' | 'function''tag'A record’s state is a pill with a dot; a function is a tinted label.
iconstringthe tone’s iconReplaces the icon of a status pill.
childrenReactNode—The word. A stalled pill carries the rule: “Stalled: no contact 21 days”.
scorenumber (0–100)—Health chip: the number, whose band picks the tone and the icon.
onClick() => void—Makes the chip a button, with expanded and controls for the popover it opens.

Avatar

Initials in the function’s tint, sizes, a stack of them.

Source: web/shared/components/Sidebar, Chip (function colours); the stack: design concept, app shell top bar

An avatar is a person’s initials in a circle, in the tint of the function they work in, so one function has one colour everywhere. The rule: initials and function colour only, and the name sits beside it or in a tooltip, because the colour alone does not say who.

Variants

PNPriya NairBack Office
JMJamie MorganLand
DWDana WhitfieldPartnerships
CHConor HigginsConstruction
RCRhys CallowayLegal
Unassigned: dashed, no initials
TANo function: a person outside the five
+3More than the stack shows
VariantLook in tokensUsed for
Function tint--color-fn-<function>-tint fill, --color-fn-<function> initialsEvery person in the product: the owner of a Site, the author of a note.
Neutral--color-surface-muted fill, --color-muted initialsA person with no function, and the “+3” of a stack.
Unassigned--color-emphasis dashed border, a user iconA Site or an action nobody owns yet.
Stackoverlapped by a sixth, a --color-surface ringThe people on a record: the avatar stack in the top bar.

Stack

JMDWCHRCFour people, overlapping, with a white ring
JMDWCHRC+3Four and the rest
JMDWCHSmall, in the top bar
JMDWCHRC

The ring is the colour of the surface under the stack (--c-avatar-ring), so the overlap reads as a cut-out on a card as on the page.

States

Default
Hover
Focus

Sizes

DWSmall, 24 px: rows and the stack
DWMedium, 32 px: the default
DWLarge, 40 px: a profile head
DWExtra large, 48 px: an account page

Properties

NameTypeDefaultWhat it does
namestring—The person; the initials come from it and it is the accessible name.
fn'backoffice' | 'land' | 'partnerships' | 'construction' | 'legal'noneThe function whose tint the avatar takes; none gives the neutral one.
size'sm' | 'md' | 'lg' | 'xl''md'Diameter: 24, 32, 40 or 48 px.
maxnumber4Stack: how many avatars show before the “+n” count.
onClick() => void—Makes it a button, as the avatar of the account menu.

Card

Title only, with actions, with an edge-to-edge table.

Source: web/shared/components/Card

A card is a white panel with a hairline border, a 12 px radius and a shadow that holds one subject. The rule: a title bar of 56 px with the title at the left and the actions at the right, a body of 20 px padding, and a table runs edge to edge with no padding of its own.

Variants

Church Farm, Rugby

Site Code
CE-014
Units
28
Deal type
Land & Build
Title only

Actions

  • Chase the draft contractHadley & Finch LLP · due Tue 10 Nov
  • Confirm the exchange date with Calder Valley HomesDue Fri 13 Nov
With actions in the bar

My deals

My deals
DealStageHealth
Heron Fields, ColchesterUnder Offer72Deal Health
Hatch Farm, SindleshamFee Discussion68Deal Health
Church Farm, RugbyLegals55Deal Health
With an edge-to-edge table
VariantLook in tokensUsed for
Title only--color-surface, --color-default border, --radius-card, --shadow-cardA fact sheet: the Site’s details, a position, a fee stream.
With actionsthe same, buttons btn--sm in the barA list the user adds to: actions, activity, key contacts.
Edge-to-edge tablebody padding: 0, the table’s head on --color-surface-mutedA short table that belongs to a card: My deals on the dashboard.

States

Actions

Loading…
Loading: skeleton lines, aria-busy

Actions

No open actions for your function.

Empty: one quiet sentence

Actions

Error: what failed and what to do

A card is not interactive; a card that opens a record is a link inside it, not the card itself. The default state is the variants above.

Sizes

Church Farm, Rugby

Site Code
CE-014
Units
28
Deal type
Land & Build
Standard: 56 px bar, 20 px body

Key dates

Site Code
SW-031
Units
33
Deal type
S106
Compact: 48 px bar, 16 px body

Properties

NameTypeDefaultWhat it does
titlestring—The card’s title, in the bar; it names the card for a screen reader.
actionsReactNode—Buttons at the right of the bar.
childrenReactNode—The body.
flushbooleanfalseTakes the body’s padding away, for a table.
size'md' | 'sm''md'Standard or compact.
isLoadingbooleanfalseShows the skeleton lines and sets aria-busy.

Stat tile

A figure, its delta against the previous period, a sub-caption; loading.

Source: web/packages/crm/src/pages/overview-parts (Tile); the delta pill: design concept, KPI tiles

A stat tile gives one figure its name, its movement and its comparison. The rule: the arrow shows the direction and the colour shows whether that is good, so a fall in days to exchange is green and a fall in Deal Health is red; the figure and the pill always carry their numbers.

Variants

Pipeline fee
£1,663,000
8.4%Upvs last month
Average Deal Health
50
3 ptsDownvs last week
Days from HoTs to exchange
74
6 daysDownvs last quarter
Active Sites
4
0No changevs HoTs
VariantLook in tokensUsed for
Figure with a sub-captionlabel --color-subtle 13 px, figure 26 px medium tabular, caption 12 px --color-mutedA count or an amount with a line that says what it covers: “Not exchanged, completed or lost”.
Delta pill, good--color-success-soft on --color-success-text, an arrowA move in the right direction: up for fees, down for days to exchange.
Delta pill, bad--color-danger-soft on --color-danger-text, an arrowA move in the wrong direction: Deal Health down.
Delta pill, flat--color-surface-muted on --color-mutedNo change against the comparison.

States

Active Sites
4
Not exchanged, completed or lost
Default
Pipeline fee
Loading…
Loading: skeleton, aria-busy
Average Deal Health
No value yet
Scores appear once a Site is instructed
No value yet
Days from HoTs to exchange
Couldn’t load
Error: says so, offers a retry

Sizes

Pipeline fee
£1,663,000
8.4%Upvs last month
Standard: 26 px figure, 20 px padding
Units
303
12Upvs last month
Compact: 18 px figure, 16 px padding

Properties

NameTypeDefaultWhat it does
labelstring—What the figure counts, in sentence case.
valueReactNode | undefinedundefinedThe figure; undefined shows a grey dash.
delta{ direction, tone, text }—The pill: direction is the arrow, tone says whether it is good.
notestring—The sub-caption: what the delta is against, or what the figure covers.
isLoadingbooleanfalseShows the skeleton and sets aria-busy.
size'md' | 'sm''md'Standard or compact.

Data table

The list table and its four states: loading, error, empty, filled.

Source: web/packages/crm/src/pages/OpportunitiesOverview, overview-parts

The list table is how the product shows many records of one kind: the Sites, the Leads, the Accounts. The rule: it always says which of its four states it is in (loading, error, empty or filled), the record’s name is the link, and every status in a row is a chip with its icon or word.

Variants

VariantLook in tokensUsed for
List tablehead --color-surface-muted, 13 px --color-subtle; rows split by --color-defaultA page of records with a pager under it, in a bordered, scrolling frame.
Sortable headthe head’s word is a button with an arrow, aria-sortA column the user may order: Site Code, Fee.
Row on hover--color-surface-muted behind the cellsShows which record the pointer is on.
In a cardno frame of its own, edge to edgeA short table inside a card (see Card).

States

Sites
Site Stage Deal Health Owner Fee
Loading Sites…
Loading: skeleton rows, aria-busy on the table
Sites
Site Stage Deal Health Owner Fee
Sites couldn’t be loaded. Refresh the page to try again.
Error: the message in the table, with a retry
Sites
Site Stage Deal Health Owner Fee
No Sites match these filters.
Empty: why, and the one way out
Sites
Site Stage Deal Health Owner Fee
CE-014 Church Farm, Rugby28 units · Land & Build Legals 55Deal Health DWDana Whitfield £262,000
LE-022 Heron Fields, Colchester46 units · Land Under Offer 72Deal Health BOBen Okafor £310,000
LE-019 Lea Bridge Works, Walthamstow64 units · Whole Block / Turnkey Acceptable Offer 41Deal Health CHConor Higgins £380,000
SW-031 Hatch Farm, Sindlesham33 units · S106 Fee Discussion 68Deal Health JMJamie Morgan £145,000
Showing 1–4 of 4 Sites
Filled: Sites from the sample; row 2 on hover, the pager line under the rows

Sizes

Sites
SiteStageFee
Heron FieldsUnder Offer£310,000
Hatch FarmFee Discussion£145,000
Default: 8 × 16 px cells
Sites
SiteStageFee
Heron FieldsUnder Offer£310,000
Hatch FarmFee Discussion£145,000
Compact: 6 × 12 px cells, for a card

Properties

NameTypeDefaultWhat it does
captionstring—The table’s name for a screen reader, drawn hidden.
columnsColumn[]—Heading, alignment (numbers right) and the cell for each column.
rowsT[][]The records of the page.
state'loading' | 'error' | 'empty' | 'ready''ready'Which of the four the body shows.
sort{ column, direction }—The ordered column; its head carries aria-sort.
emptyMessagestring—Says whether a filter or nothing at all caused the empty state.
onRetry() => void—The error state’s button.
footerReactNode—The pager under the rows.

Pagination

First, middle and last page.

Source: web/shared/components/Pager; the numbered pages: proposed

Pagination sits at the foot of a paged table and says where the user is in it. The rule: it always shows the range and the total in words, past page 1 the buttons stay so a page number that went stale can still be left, and when everything fits on one page there are no buttons at all.

Variants

First page: Previous is off
Middle page
Last page: Next is off
With numbered pages: first, middle, last
VariantLook in tokensUsed for
Previous and Nextbtn--secondary, range in --color-subtleThe pager of every Overview: the range, then two buttons.
Numbered pagescurrent page --color-primary-soft on --color-primary-strong, aria-currentA long list where the user jumps: Accounts, the activity log.

States

Loading: the range is blank and both buttons are off
Everything on one page: the range, no buttons
Nothing to show

Sizes

Standard: 56 px high, for a page-wide table
Compact: 48 px, inside a card

Properties

NameTypeDefaultWhat it does
pagenumber1The page on show, counted from 1.
pageSizenumber25Rows to a page.
totalnumber—Every row there is, for the range and for the last page.
nounstring—What is counted, in the plural: “Sites”.
isLoadingbooleanfalseBlanks the range and turns the buttons off.
onPage(page: number) => void—Asks the caller for another page; the page number lives in the URL.

Progress and meter

How far along; how full.

Source: design concept (the fee stream and the checklist); the bands of web/shared/components/HealthChip proposed

A progress bar shows how far a task has got and only grows; a meter shows a level against a range and may change band. The rule: both say their number in words beside the bar, and a band or a failure is named with an icon or a word, never left to the colour.

Variants

Instruction Approval checklist3 of 5
Progress bar, with its label and number
78%
Site Review, 78% complete
Progress ring
Deal Health72 Good
Deal Health55 Fair
Deal Health34 Poor
Meter: Deal Health against its three bands
Fee stream, Church Farm£262,000
  • Collected£65,500
  • Invoiced, not yet collected£65,500
  • Not yet invoiced£131,000
Stacked meter: a fee stream, collected within invoiced within the fee
VariantLook in tokensUsed for
Progress barfill --color-chart-actual on --color-chart-trackA checklist, an upload, a step count: how far along.
Progress ringthe same colours, drawn as a ring with the figure insideA completion share where there is no room for a bar.
Meter with bands--color-health-poor-tint, --color-health-fair-tint, --color-health-good-tint behind a markerA level with meaning in its range: Deal Health 40 and 70.
Stacked meter--color-chart-actual, --color-chart-forecast, the track, each named in a legendParts of one whole: a fee collected, invoiced, still to invoice.

States

Uploading CSA draft62%
In progress
Uploading CSA draftDone
Complete: the check beside the number
Uploading CSA draftFailed at 62%
Error: danger tone and the word
Reading the email…
Not known yet: indeterminate, aria-busy

Sizes

Key dates40%
Small, 4 px: inside a card or a row
Key dates40%
Default, 8 px
Key dates40%
Large, 12 px: a hero figure
40%
40%
40%
Rings: 32, 56 and 80 px

Properties

NameTypeDefaultWhat it does
valuenumber | null—Progress: how far along, 0 to max; null is indeterminate.
maxnumber100The end of the range.
labelstring—The name, drawn above the bar and used as its accessible name.
valueTextstringn %The number in words: “3 of 5”, “Failed at 62%”.
tone'default' | 'good' | 'bad''default'Complete or failed; the word beside it says so too.
size'sm' | 'md' | 'lg''md'Bar height 4, 8 or 12 px; a ring is 32, 56 or 80 px.
bands{ poor, good }{ 40, 70 }Meter: where the bands change; Deal Health uses 40 and 70.

Tooltip and popover

A hint on hover; a small panel on click.

Source: popover: web/shared/components/ScorePopover; locked-button reason: web/shared/components/Button; tooltip: proposed

A tooltip is a hint that appears on hover and on keyboard focus; a popover is a small panel the user opens with a click to read more. The rule: a tooltip never holds the only way to learn something or anything to click, and a popover keeps focus, closes on Escape, hands focus back to its trigger and may hold more than a line.

Variants

Download the signed CSATooltip on an icon button: hover or focus it
Land and Partnerships create opportunities. Read only for Construction.Tooltip on a locked button: the reason it is locked
Popover from the health chip: the breakdown
Popover from an info button
VariantLook in tokensUsed for
Tooltip--color-surface-inverted with --color-inverted text, 12 px, --radius-controlA name for an icon button; the reason a control is locked.
Popover--color-surface, --color-default border, --radius-card, --shadow-overlayMore than a line: the Deal Health breakdown, a short definition.

Open, drawn statically

Download the signed CSATooltip, open
Popover, open: the Deal Health breakdown of Heron Fields

States

Tooltip: hidden by default; shown on hover and on focus after a short pause; Escape hides it while the pointer or focus stays. It stays while the pointer moves onto it. Popover: closed by default; open on a click or Enter or Space on its trigger, which carries aria-expanded; Escape, a click outside or focus leaving closes it.

Trigger, default
Trigger, hover
Trigger, focus
Locked control: aria-disabled, still focusable, so the reason can be read

Sizes

PartWidthUsed for
Tooltip, one linefits its textA name: “Download the signed CSA”.
Tooltip, wrapped240 px at mostA reason, in a sentence or two.
Popover, small240 pxA definition.
Popover, large384 pxThe Deal Health breakdown and other tables of lines.

Properties

NameTypeDefaultWhat it does
contentstring—Tooltip: the hint, in a sentence at most.
placement'top' | 'bottom' | 'left' | 'right''top'Where it opens; a popover opens below, or above when there is no room.
triggerComponentType—Popover: the button, which gets onClick, expanded and controls.
labelstring—Popover: the panel’s accessible name.
childrenReactNode—Popover: the panel’s content.
widthnumber320Popover: the panel’s width in px.

Tabs

Segmented: the white pill on a grey bar, with counts.

Source: web/shared/components/TabBar

Tabs switch between the views of one record or one list. They sit on a grey bar, the open one raised as a white pill, and every tab keeps the same medium weight, so choosing one never moves the others.

Variants

Fill, with counts: the Site page. Click a tab to open it; Risk & Actions carries its count in the alert tone.
Fit: filters of one list, as buttons
Fit, with counts
VariantLook in tokensUsed for
FillThe bar spans its row and spreads its tabs when they fit; narrower, they pack from the start and wrapThe sections of a record: the Site page
FitThe bar is as wide as its tabs: --color-surface-muted track, --radius-card, 4 px padding; the open tab --color-surface with --shadow-cardFilters of one list: the Activity filter, the actions by status
With countA pill after the label: --color-surface on the bar, --color-surface-muted on the open tab; the alert tone --color-danger-soft and --color-danger-textHow many there are: Buyers 14, Contractors 5; what needs a look: Risk & Actions 3
Links or buttonsThe same look. Links go to another page and carry aria-current="page"; buttons filter the page and are groupedLinks for the Site page; buttons for filters

States

Idle
Hover
Focus
Open
Disabled
With count

Sizes

One size: a 32 px tab (--size-control-sm) with the label in 14 px medium, 12 px side padding, 4 px between tabs. When the row is too narrow the tabs wrap onto a second row.

Properties

NameTypeDefaultWhat it does
labelstring—What a screen reader calls the bar
as'nav' | 'group''nav'nav when the tabs go to other pages, group when they are filter buttons on one page
fillbooleanfalseSpans the row and spreads the tabs, instead of sizing to them
tab.labelstring—The tab’s text, in sentence case
tab.countnumbernoneA pill after the label; leave it out for a tab with nothing to count
tab.alertbooleanfalseDraws the count in the alert tone, for what needs a look
tab.currentbooleanfalseThe open tab: the white pill, with aria-current
tab.disabledbooleanfalseGreyed out and out of the tab order

Stepper

Steps done, current and to come.

proposed

A stepper shows where the user is in a sequence that has a fixed order: what is done, the step in hand and what is still to come. Each step says so in words and in its marker, a tick, a number in a ring or a plain number, so the state is never told by colour alone.

Variants

  1. Site detailsDoneCurrentTo come
  2. 2
    Fee streamDoneCurrentTo come
  3. 3
    TeamDoneCurrentTo come
  4. 4
    ReviewDoneCurrentTo come
Horizontal: add a Site. Step through it with the buttons.
  1. Site detailsName, Site Code, region and unitsDoneCurrentTo come
  2. 2
    Fee streamBasis and trigger of the feeDoneCurrentTo come
  3. 3
    TeamWho owns the Site in each functionDoneCurrentTo come
  4. 4
    ReviewCheck it and create the SiteDoneCurrentTo come
Vertical, with a line under each title: for a side panel

Step 2 of 4Fee stream

Compact: for a narrow place, the count and the step in hand
VariantLook in tokensUsed for
HorizontalA marker and a line per step, the title and its status under it; the line is --color-emphasis, --color-primary once the step is doneA form in several steps, across the page
VerticalThe same, down the side, with a description under each title in --color-mutedA side panel, a drawer
Compact“Step 2 of 4” in 14 px semibold, the step’s title in --color-muted and a track of four segments in --color-primary and --color-chart-trackA narrow window

States

  1. Fee streamDoneCurrentTo come
Done: a tick in a --color-primary disc
  1. 2
    Fee streamDoneCurrentTo come
Current: the number in a ring, the title in medium weight
  1. 3
    Fee streamDoneCurrentTo come
To come: a plain number on --color-emphasis

Sizes

One size: a 28 px marker with a 2 px line. The titles are 14 px, the status 12 px. Four to six steps fit a page; more should be a list.

Properties

NameTypeDefaultWhat it does
steps{ title, description? }[]—The steps in order; the description shows in the vertical stepper
currentnumber0The index of the step in hand: those before it are done, those after it are to come
orientation'horizontal' | 'vertical' | 'compact''horizontal'How the steps are laid out
labelstring—What a screen reader calls the list; the current step carries aria-current="step"

App shell

The grouped sidebar, the top bar and the page, as components.

Source: web/shared/components/Sidebar, web/shared/components/AppLayout; design concept, Opportunity Overview

The shell is what every signed-in screen sits in: the sidebar that groups the product’s pages, the top bar that says where you are and what you can do about it, and the page. The page on show is the one item in the grey --color-nav-active; the sidebar folds to a rail of icons and remembers the choice.

Sidebar

Expanded, 240 px: the logo tile and name with the fold button; search with its shortcut; the signed-in user; the groups; Settings at the foot
Folded, 64 px: icons only, a rule in place of each group name, the name of an item in a tooltip on hover and on focus. The fold buttons work here.

Top bar

Updated 12 m ago
JMDWCH+2

The path to the page on the left, 56 px high. On the right: when the record was last updated, who is working on it, Share and the primary action.

In place

Live: fold the sidebar with its button, open the tabs. The page keeps its width within 1200 px.

Variants

VariantLook in tokensUsed for
Sidebar, expanded240 px (--size-sidebar) on --color-sidebar, 1 px --color-default on its right; group names 12 px uppercase in --color-subtle; items 36 px, --color-mutedThe default, on desktop
Sidebar, folded64 px rail; each item a 36 px square; a tooltip in --color-surface-inverted with --shadow-overlayMore room for the page; remembered per user
Item on show--color-nav-active with --color-nav-active-text, medium weight, aria-current="page"Sites, while a Site is open
User card--color-surface with --shadow-card, the avatar in the function’s tint, the name and the role under itWho is signed in; a link to the profile
Top bar56 px (--size-header) on --color-surface with a 1 px --color-default line below; the avatars overlap by 4 px, each ringed in --color-surfacePath, freshness, people and the page’s actions

States

Sizes

Sidebar 240 px expanded, 64 px folded; top bar 56 px; the page has 24 px of padding and stops at 1200 px. Desktop first and usable on a tablet; there is no phone layout in V1.

Properties

NameTypeDefaultWhat it does
productNamestring—Set beside the logo tile; the tile alone when the sidebar is folded
groups{ id, label, items }[]—The groups of pages; a group with no item the user may open is left out
item{ label, href, icon, active }—One page; the active one carries aria-current="page"
user{ name, role, initials, fn } | nullnullThe user card; none until the session has loaded
collapsedbooleanfalseFolds the sidebar to the rail; kept between visits
crumbsBreadcrumbs—The path in the top bar
updatedstringnoneHow long ago the record changed, in the viewer’s time
peopleAvatar[][]Who is working on the record, as a stack with the count of the rest
actionsButton[]—Share, then the one primary action
childrenReactNode—The page

Alert and toast

Inline alerts in four tones; toasts that confirm or report.

Source: web/packages/crm/src/components/form.tsx (FormError and FormSuccess: tinted box, icon, one sentence). The four tones, the info tone and the toast stack are proposed

An alert is a message that stays on the page, in one of four tones, beside the thing it speaks about. A toast is the same message after an action: it confirms or reports and then leaves by itself. The one rule: a tone is never colour alone, the icon and the words carry it too, and an error that needs a decision is an alert, never only a toast.

Variants

ToneLook in tokensUsed for
InfoBackground --color-info-soft, title and icon --color-info-text, the info iconA fact the user should know, nothing to fix: documents live in SharePoint, a read-only field.
SuccessBackground --color-success-soft, title and icon --color-success-text, the check-circle iconAn action went through: an offer recorded, a quote read, a Site moved to Under Offer.
WarningBackground --color-warning-soft, title and icon --color-warning-text, the warn iconSomething needs attention soon, nothing is broken: unsigned Heads of Terms, a longstop coming up.
ErrorBackground --color-danger-soft, title and icon --color-danger-text, the x-circle iconAn action failed or a value is rejected. Read by a screen reader at once (role alert).
Documents stay in SharePoint Valere CRM links the files of a Site and reads its quotes and planning documents. It keeps no copy.
Offer recorded Calder Valley Homes’ offer on Heron Fields (LE-022) is on the Site.
Heads of Terms are not signed Church Farm (CE-014) is due to exchange on 20 Nov 2026.

States

Heron Fields (LE-022) moved to Under Offer on 9 Nov 2026.
Text only: a one-line fact
The longstop on Church Farm (CE-014) is in 12 days.
With one action
Offer recorded Click the cross: the alert goes and can be shown again.
Dismissible: the cross is a 32 px target
Close button: hover and focus together

Sizes

Documents stay in SharePoint Valere CRM links the files of a Site and reads its quotes and planning documents. It keeps no copy.
Default: 14 px, icon 18 px, fills its column
Documents stay in SharePoint; the CRM keeps the link.
Compact: one line, 13 px, inside a card or a table head

Toast

Offer recorded Calder Valley Homes on Heron Fields.
Confirms what was just done
Offer not recorded The connection dropped. Nothing was saved.
Reports an error
Reminder sent to Hadley & Finch LLP
Title only
Quote read 41 lines are ready to review.
With one action
Live: toasts appear at the bottom right, stay five seconds and wait while the pointer is on them.
RuleWhat it does
PlacementToasts stack at the bottom right above everything else (--z-toast), 384 px wide at most; alerts sit inside the page, above the thing they speak about.
TimeA toast stays 5 seconds and waits while the pointer is on it. An alert stays until the cause is gone or the user dismisses it.
AnnouncementThe toast stack is a polite live region (role="status"); an error alert is role="alert".
Which oneA result the user caused and can forget: toast. A state of the page or an error that needs a decision: alert. Never only a toast for an error.

Properties

Alert

NameTypeDefaultWhat it does
tone'info' | 'success' | 'warning' | 'danger''info'Sets the tint, the text colour and the icon.
titlestring—A short headline in medium weight; optional.
childrenReactNode—The sentence: what happened and what to do about it.
actionReactNode—At most one small secondary button.
onDismiss() => void—Shows the close button. Without it the alert is permanent.
compactbooleanfalseOne line, 13 px; for tight places.

Toast

NameTypeDefaultWhat it does
tone'info' | 'success' | 'warning' | 'danger''info'The icon and the stripe on the left.
titlestring—What happened, in a few words: “Offer recorded”.
textstring—The record it happened to; optional.
durationnumber5000Milliseconds before it leaves by itself; the pointer on it holds it.
actionReactNode—At most one ghost button, such as Review or Undo.

Drawer

A panel from the side for details and forms.

Source: proposed The pilot opens details on their own pages; the drawer keeps the list in view, as the Monday meeting needs.

A drawer is a panel from the right edge for details and forms about something in the list behind it. The one rule: the user keeps their place; the list is still there, dimmed, and closing the drawer returns focus to the row or the button that opened it.

Variants

VariantLook in tokensUsed for
DetailsPanel on --color-surface, border --color-default, --shadow-overlay; a list of labelled values, actions in the footReading one thing without leaving the list: a Next Action, a buyer’s position, an activity.
FormThe same panel; fields stacked in the body, Cancel and the primary button in the footCreating or editing something with more than three fields: a Next Action, a contact.
Details of a Next Action
The form for a new Next Action

The owner and the due date are drawn as plain fields here; in the product they are the Select and the Date picker components.

Try it

Live: slides in from the right; Escape, the dark area or the cross closes it.

States

StateLook
ClosedNot in the page; the list behind it stays as the user left it.
OpenSlides in from the right over --duration-base; the page behind is dimmed with --color-overlay and does not scroll. Under reduced motion it simply appears.
Unsaved changesClosing a form with edits asks first, with the Modal’s leaving variant.

Sizes

SizeWidthUsed for
sm360 pxA short list of values.
md440 pxDetails and short forms. The default.
lg640 pxA longer form or a table inside the panel.

Properties

NameTypeDefaultWhat it does
openbooleanfalseShows the panel and the dark area.
size'sm' | 'md' | 'lg''md'The width of the panel; it is always full height.
titlestring—The heading, with the record it belongs to under it.
childrenReactNode—The body; it scrolls when it is long.
footerReactNode—The actions, right-aligned; the primary one last.
onClose() => void—Called by the cross, Escape and the dark area.

Empty state

A picture, a sentence and the one thing to do next.

Source: proposed The Data table has its own empty state; this component is the one it and every other list share.

An empty state fills the space where a list would be: a picture, one sentence and the one thing to do next. The one rule: say why it is empty and give one action, never two; a list that is empty because the work is done gets the sentence and no button.

Variants

VariantLook in tokensUsed for
First useCircle --color-primary-soft, icon --color-primary-strong; one primary buttonA list nobody has filled yet. The button does the thing that fills it.
No matchCircle --color-surface-muted, icon --color-muted; one secondary buttonA search or a filter that finds nothing. The button undoes the filter.
All clearCircle --color-success-soft, icon --color-success-text; no buttonA list that is empty because the work is done: no overdue actions, no unmatched emails.

No offers on Heron Fields yet

When a buyer makes an offer, record it here and it joins the comparison.

First use: Offers of a Site

No Sites match these filters

Try another region or stage, or clear the filters to see every Site.

No match: the Sites list

Nothing overdue

Every Next Action on your Sites is on time this week.

All clear: the Monday meeting

The circle with an icon stands in for a picture until the Illustrations exist; the box it fills does not change when they arrive.

States

No buyers on the register

Add the first buyer to start matching.

Default

No buyers on the register

Add the first buyer to start matching.

Action: hover

No buyers on the register

Add the first buyer to start matching.

Action: focus

No offers on Lea Bridge Works yet

Only Land and Partnerships can record an offer. Read only for Construction.

Without the right: the action is off and the sentence says why

Sizes

No documents linked yet

Link the planning documents and quotes from SharePoint.

Full: a page or a panel, 64 px circle

No actions match

Clear the function filter to see them all.

Compact: inside a card or a table, 40 px circle

Properties

NameTypeDefaultWhat it does
iconIconName—The picture in the circle. Until Illustrations exist it is a 24 px icon.
tone'neutral' | 'brand' | 'success''neutral'The circle’s tint.
titlestring—What is empty, in a few words.
childrenstring—One sentence: why it is empty or what fills it.
actionReactNode—At most one button. None when the list is empty because the work is done.
compactbooleanfalseLess padding and a smaller circle, for inside a card.

File upload

Drop zone, progress, the file list, an error.

Source: proposed The pilot has upload in its base storage and no document screen (S-05 OPP-16); in the product the file is handed to SharePoint.

A file upload hands a document over to be filed and read: a drop zone, a progress bar while it travels, a list of what was sent and an error when it could not be. The one rule: a document never lives in the CRM. The file goes to SharePoint and the product keeps the link, so every row says where it was filed.

Variants

VariantLook in tokensUsed for
Drop zone with listDashed border --color-emphasis, --color-primary-soft when a file is over it; rows in a bordered list belowFiling a returned quote or planning document against a Site: the main way.
Button onlyA btn--secondary with the upload icon; the rows are the sameA row of a table or a card where a zone does not fit.
  • Westbrook-quote-CE-014-v2.pdf 1.8 MB
    Uploading 62%
  • Westbrook-quote-CE-014-v1.xlsx 640 KB Sent to SharePoint. Reading the quote…
  • Westbrook-quote-CE-014-v0.pdf 2.4 MB Read 41 lines ready to review
  • Westbrook-scan.pdf 38 MB Not uploaded: larger than 25 MB, the limit set by an admin.
  • Westbrook-quote-CE-014-v3.docx 310 KB Could not reach SharePoint. Nothing was filed.
A quote from Westbrook Construction for Church Farm (CE-014): uploading, reading, read, and two errors.
Button only: opens the same file picker

Documents live in SharePoint. The drop zone hands the file over, the row says where it was filed, and only the link is kept in the CRM. A quote in Excel, PDF or Word is read into cost headings once it is filed.

States

Default
A file is over it (or the pointer, on hover)
Keyboard focus: the hidden input shows the ring on the zone
Disabled: the hint says what is missing
Error: the file type is refused; the message names the file

Try it

Live: choose or drop a PDF, Excel or Word file. Nothing leaves your computer: the progress is simulated. Any other type, or a file over 25 MB, shows its error.

Sizes

PartSize
Drop zoneFull width of its column, 560 px at most; 32 px padding above and below the 40 px circle.
File row36 px icon tile, name in medium weight, size and status under it; actions at the right, 32 px targets.
Progress bar6 px high on the chart track token; it carries role progressbar and its percentage as text beside it.

Properties

NameTypeDefaultWhat it does
acceptstring[]['.pdf', '.xls', '.xlsx', '.doc', '.docx']The types a quote can arrive in; anything else shows the type error.
maxSizenumber26214400Bytes: 25 MB, an admin setting; a larger file shows the size error.
multiplebooleantrueSeveral files at once; each gets its own row.
disabledbooleanfalseTurns the zone off; the hint says what is missing.
files{ name, size, status, progress, error }[][]The rows. Status is uploading, reading, read or error.
onFiles(files: File[]) => void—Called with the files chosen or dropped; the parent hands them to SharePoint.
onRemove(name: string) => void—The cross on a row.
onRetry(name: string) => void—The Try again button on an error row.

Function chip

The owning function in its colour, on every record and action.

Source: web/shared/components/Chip (kind function) · avatar tiles: the app shell, web/shared/components/AppLayout

Shows who owns a record, an action or a stage, in the function’s own colour and always with its name. The one rule: the name is always written — the colour only helps the eye, so a chip is never a bare dot.

Variants

One colour per function, the same everywhere: the chip, the avatar of a person, the owner of a stage, the clock.

VariantLook in tokensUsed for
Back Office--color-fn-backoffice · --color-fn-backoffice-tintFinding and researching Sites, the first approach, the appraisal. Owns Lead and Appraisal.
Land--color-fn-land · --color-fn-land-tintThe regional lead’s Sites: Site Review, instruction, land terms, the fee agreement. Owns the stages up to Instructed.
Partnerships--color-fn-partnerships · --color-fn-partnerships-tintThe PM: buyers, offers and Heads of Terms. Owns Buyer agreed and Heads of Terms.
Construction--color-fn-construction · --color-fn-construction-tintContractors, tenders, the build price and the CSA. Owns the Contractor stage. Shown only where Construction applies.
Legal--color-fn-legal · --color-fn-legal-tintThe Partners / MD and Admin roles. The neutral slate of the set.

Sizes

Small in a table cell or a stage bar, medium on a record, large in a page header.

LandSmall · 20 px
LandMedium · 24 px, the default
LandLarge · 28 px

With an avatar

The person’s initials in a round tile that takes the function’s colours reversed, with or without the person’s name.

LandInitials and function
PartnershipsInitials and function
ConstructionSmall
Priya Nair ·Back OfficeLarge, with the person’s name

States

A chip is a label, not a control: it has no hover, focus or disabled state. To filter by function use the Select. Shown as the owner of a record, alone or jointly.

LandOne owner
LandPartnershipsConstructionJoint owners: Legals and Exchange
LandPartnershipsConstruction not shown where it does not apply
SiteStageOwner
Church Farm, RugbyLegalsLandPartnershipsConstruction
Heron Fields, ColchesterUnder OfferPartnerships
Hatch Farm, SindleshamFee DiscussionLand

Properties

NameTypeDefaultWhat it does
fnbackoffice | land | partnerships | construction | legal—The owning function; sets the colour and the written name.
sizesm | md | lgmdHeight 20, 24 or 28 px; the text and the avatar follow.
avatartext—The initials of a person; draws the round tile before the name.
persontext—The person’s name, written before the function.

Stage bar

The Site’s eight-segment lifecycle; the buyer and contractor journeys.

Source: web/packages/crm/src/pages/lead-detail/StageBar · design concept, Opportunity Overview; owners, captions and journeys proposed

Shows where a Site, a buyer or a contractor stands on its path and who holds it now. The one rule: position is never colour alone — every segment has its name, and the current one says the day it is on.

Variants

VariantLook in tokensUsed for
Site lifecycle--color-primary · --color-emphasis · .c-fnchipThe Site header and the Opportunity Overview: Lead, Appraisal, Instructed, Buyer agreed, Contractor stage, Heads of Terms, Legals, Exchange, with the function that owns each.
Buyer journey--color-primary · --color-emphasisOne buyer on a Site, on the Buyers tab: Coverage, Teaser, Pitched, Interested, Offered, Agreed, Board, Approved. Owned by Partnerships.
Contractor journey--color-primary · --color-emphasisOne contractor on a Site, on the Contractors tab: Coverage, Longlist, Tender Sent, Bids Returned, Priced, Preferred, Appointed. Owned by Construction.

Site lifecycle

The design concept’s eight segmented bars: done solid, the current one part-filled with the day it is on, the ones ahead grey. Each stage names its owning function; the pill says whether Construction applies.

Church Farm, RugbyCE-014
Construction applies — Deal Type: Land & Build
  1. Lead (done) Done · 12 daysBack Office
  2. Appraisal (done) Done · 9 daysBack Office
  3. Instructed (done) Done · 3 daysLand
  4. Buyer agreed (done) Done · 41 daysPartnerships
  5. Contractor stage (done) Done · 28 daysConstruction
  6. Heads of Terms (done) Done · 22 daysPartnerships
  7. Legals (current) Day 35 · nowJoint
  8. Exchange Target 14 NovJoint

Joint: Land, Partnerships and Construction together, in Legals and at Exchange.

Heron Fields, ColchesterLE-022
Construction does not apply — Deal Type: Land
  1. Lead (done) Done · 12 daysBack Office
  2. Appraisal (done) Done · 9 daysBack Office
  3. Instructed (done) Done · 3 daysLand
  4. Buyer agreed (done) Done · 41 daysPartnerships
  5. Contractor stage Not applicable
  6. Heads of Terms (current) Day 12 · nowPartnerships
  7. Legals Target 3 DecJoint
  8. Exchange Target 18 DecJoint

Buyer and contractor journeys

The same bar for one buyer’s or one contractor’s path on a Site; the current stage carries the fact that matters.

Buyer journeyCalder Valley Homes
Partnerships
  1. Coverage (done)
  2. Teaser (done)
  3. Pitched (done)
  4. Interested (done)
  5. Offered (current) Offer £3,640,000
  6. Agreed
  7. Board
  8. Approved
Contractor journeyWestbrook Construction
Construction
  1. Coverage (done)
  2. Longlist (done)
  3. Tender Sent (done)
  4. Bids Returned (current) Returned 2 Nov
  5. Priced
  6. Preferred
  7. Appointed

States

A segment is one of four. When the Site is past the bar the whole bar is done.

StateLook in tokensWhat the text says
--color-primaryDone · 41 days — how long the stage took. Read aloud as “done”.
--color-primary-soft + --color-primaryDay 35 · now — the day in the stage; the fill is how far through its clock. Read aloud as “current”.
--color-emphasisTarget 14 Nov — the target date when one is set, otherwise nothing.
--color-emphasisNot applicable — a stage that Deal Type or Construction Involved removes for this Site; the outline is dashed.
Church Farm, RugbyCE-014
Completed
  1. Lead (done) Done · 12 daysBack Office
  2. Appraisal (done) Done · 9 daysBack Office
  3. Instructed (done) Done · 3 daysLand
  4. Buyer agreed (done) Done · 41 daysPartnerships
  5. Contractor stage (done) Done · 28 daysConstruction
  6. Heads of Terms (done) Done · 22 daysPartnerships
  7. Legals (done) Done · 38 daysJoint
  8. Exchange (done) Exchanged 12 NovJoint

Sizes

SizeLook in tokensUsed for
Default6 px track, 12 px names, captions and ownersA card on a record page.
Compact4 px track, names onlyA row of a table or a card of a list, where only the position matters.
  1. Lead (done)
  2. Appraisal (done)
  3. Instructed (done)
  4. Buyer agreed (done)
  5. Contractor stage (done)
  6. Heads of Terms (done)
  7. Legals (current)
  8. Exchange

Properties

NameTypeDefaultWhat it does
segments{ name, state, note?, owner? }[]—The stages in order; state is done, current, future or not applicable.
currentstage—The stage on show; past the last segment marks them all done.
fill0–10050How far through the current stage, from its clock; 50 when there is none.
ownersbooleantrueShows the owning function under each stage; joint owners as dots.
constructionAppliesbooleantrueDraws the pill and the Contractor stage; false marks that stage not applicable and drops Construction from the owners.
labeltextSite lifecycleThe accessible name of the list.
sizemd | smmdDefault with captions, or compact.

Deal clock

Days left on a function’s clock, and the overdue flag.

Source: proposed — the clocks of blueprint/modules/views-scores-dashboards.md (VIW-C21)

Shows how many days a function has left on its clock for a Site, and flags it when the clock has run out. The one rule: the count says “left” or “overdue” in words, and the length applied is one click from the clock.

Variants

One clock per function; the Back Office and Land clocks count working days, the others every day.

VariantLengthLook in tokensUsed for
Back Office5 working days--color-fn-backofficeFrom the appraisal request to the appraisal.
Land15 working days--color-fn-landFrom the appraisal to the instruction.
Partnerships28 days, first offer within 21--color-fn-partnershipsFrom instruction to an acceptable offer.
Construction21 days to a bid, 30 to the CSA--color-fn-constructionFrom the handover to Construction, only where Construction applies.
PartnershipsUnder 100 days--color-fn-partnershipsFrom the latest signed Heads of Terms to exchange.

Pill

The compact form, in a table row or a Site header.

Back Office2 working days leftBack Office · 5 working days
Land5 working days leftLand · 15 working days
Partnerships8 days leftPartnerships · 28 days
Construction3 days leftConstruction · 21 days
Partnerships65 days leftHoTs to exchange · under 100 days

Card

The full form, with the clock’s dates and the length applied with the date it last changed.

Back OfficeAppraisal clock
2working days left
Appraisal requested 4 NovDue 11 Nov

Length applied: 5 working days · changed 12 Oct 2026

LandInstruction clock
5working days left
Appraised 26 OctDue 16 Nov

Length applied: 15 working days · changed 12 Oct 2026

PartnershipsAcceptable offer clock
8days left
Instructed 20 OctDue 17 Nov

Length applied: 28 days, first offer within 21 · changed 12 Oct 2026

ConstructionAcceptable bid clock
3days left
Handed over 22 OctDue 12 Nov

Length applied: 21 days · changed 12 Oct 2026

ConstructionAcceptable CSA clock
12days left
Handed over 22 OctDue 21 Nov

Length applied: 30 days · changed 12 Oct 2026

PartnershipsHeads of Terms to exchange
65days left
HoTs signed 5 Oct · day 35Due 13 Jan 2027

Length applied: under 100 days · changed 12 Oct 2026

States

On track, due soon, overdue and paused. A closed Site shows no clock.

5 working days leftOn track
3 days leftDue soon: a quarter of the length or less is left
2 working days overdueOverdue
PausedPaused: the Site is Parked
LandInstruction clock
2working days overdue
Appraised 15 OctWas due 5 Nov

Length applied: 15 working days · changed 12 Oct 2026

Properties

NameTypeDefaultWhat it does
fnfunction—The function whose clock it is; sets the chip.
lengthnumber—The length applied, from the Admin’s setting.
unitdays | working days—Working days follow the working-day calendar; the others count every day.
startedOndate—The handoff that started the clock; a Parked Site pauses it, a closed Site stops it.
stateok | soon | over | pausedokWorked out from the days left; the words say it as well as the colour.
sizepill | cardpillA pill in a table or a header, or a card with its dates.

Deal Health

The 0–100 score in its band, its breakdown, the stalled badge that names its rule.

Source: web/shared/components/HealthChip · web/shared/components/DealHealth · web/shared/components/ScorePopover

Shows how healthy a Site is as one number from 0 to 100, in its band, and lets anyone see how it is made up. The one rule: the number is always beside its icon, and the breakdown names every input and its points — the formula is never hidden.

Variants

Three bands, each with its own icon; the bands are the pilot’s.

VariantLook in tokensUsed for
Deal health72 Good · 70 and above--color-health-good-text · --color-health-good-tint · check-circleOn track: no input costs more than a little.
Deal health55 Fair · 40 to 69--color-health-fair-text · --color-health-fair-tint · warnNeeds a look at the next Monday meeting.
Deal health32 Poor · below 40--color-health-poor-text · --color-health-poor-tint · x-circleAt risk: the owner and the MD are told.

At the edges of the bands

Deal health100100
Deal health7070 · Good
Deal health6969 · Fair
Deal health4040 · Fair
Deal health3939 · Poor
Deal health00

Sizes

Deal health55Medium · 24 px, in a table or a card
Deal health55Large · 32 px, in a Site header

States

Default: a button that opens its breakdown
Hover
Focus
Closed deal: no score

Breakdown

The popover that opens from the score: each input, its points and the fact behind it, then the stall rules that fired. Shown open, and live below it (click the score; Escape closes it).

Inputs and weights

The score starts at 100 and loses each input’s points. The weights are the pilot’s proposal; the Admin edits them in Settings.

InputWhat it countsWeight
Recent contact3 points a day after 5 days without contact, up to 30−3 a day
Overdue actions10 points for each overdue action, up to 30−10 each
Stage duration1 point a day past the stage’s limit, up to 20; Legals has no limit−1 a day
Next action20 points when no next action is named−20
Legals15 points for each critical Legals task past its deadline−15 each
Open flags8 points for each open flag, up to 24−8 each
Expectation gapLeads: 1 point for each whole % the landowner’s figure is above the appraisal, up to 20−1 a %

Stalled badge

Appears beside the score when a stall rule fires, and names the rule.

Stalled: no progress 14 daysOne rule, named
Stalled: no progress 14 days+1First rule named, “+1” for the others
Stalled: Heads of Terms open 30 daysThe rule that fired decides the words

Hover or focus shows the threshold applied and the date it last changed.

Properties

NameTypeDefaultWhat it does
score0–100—The Deal Health score; rounded and kept between 0 and 100. No score on a closed deal.
components{ input, points, fact }[]—The inputs behind the score, each with its points and the fact they come from. A missing input is named, not hidden.
stallRulesrule[][]The stall rules that fired; the badge names the first and counts the rest.
sizemd | lgmd24 or 32 px high.
onClickfunction—Makes the chip the button that opens the breakdown; Escape or a click outside closes it and Escape gives the focus back.

Key dates

Exchange, completion and longstop, each with its countdown.

Source: web/packages/crm/src/pages/opportunity-detail/KeyDatesCard · the thin bar and the tags: proposed

Shows the dates a deal turns on — exchange, completion and the longstop — each with the days left. The one rule: the count is a phrase (“39 days left”, “4 days overdue”), and the tag says whether the date is a target or a hard date.

Variants

VariantLook in tokensUsed for
Exchange Target--color-primary-soft · --color-primary-strongThe date Valere is working to. It moves, and each move is kept.
Completion Target--color-primary-soft · --color-primary-strongThe date the sale completes.
Longstop Hard date--color-surface · --color-defaultThe date in the contract that must be met; passing it is a legal blocker.

On the Site record

Church Farm on Monday 9 Nov 2026, 35 days after Heads of Terms were signed: the thin bar shows how much of the time to each date has gone.

Key dates

  • Exchange Target 14 Nov 2026 5 days left
  • Completion Target 4 Dec 2026 25 days left
  • Longstop Hard date 18 Dec 2026 39 days left

States

Key dates

  • Exchange Target 14 Nov 2026 5 days left

Key dates

  • Exchange Target 5 Nov 2026 4 days overdue

Key dates

  • Exchange Target 12 Nov 2026 Exchanged

Key dates

  • ExchangeTargetNot set

On track: 14 days or more left. Due soon: a week or less, with a clock icon. Overdue: the date has passed and the step is not recorded, with a warning icon. Met: the step is recorded.

Properties

NameTypeDefaultWhat it does
labeltext—Exchange, Completion or Longstop.
datedate—UK date; “Not set” when empty.
tagtarget | hardtargetTarget for the dates Valere works to, Hard date for the longstop.
startedOndate—Where the thin bar begins; it fills toward the date.
stateon-track | soon | overdue | meton-trackWorked out from the days left, with its words and icon.

Position card

The buyer’s or the contractor’s position on a Site at a glance.

Source: web/packages/crm/src/pages/opportunity-detail/PositionCards

Shows who is on the other side of a Site right now — the agreed buyer, the appointed contractor — with the one figure that matters. The one rule: the status is a labelled pill, and when nobody is agreed the card says so and counts who is in play.

Variants

VariantLook in tokensUsed for
Buyer position--color-stage-tint · --color-stage-text · .cardThe agreed buyer of a Site: name, status, the offer and its quality. The full list is the Buyers tab.
Contractor position--color-stage-done-tint · --color-stage-done-text · .cardThe appointed contractor, only where Construction applies: name, status, the tender price and its rank.

On the Site record

Church Farm, a Land and Build Site: both cards sit beside the Key dates.

Buyer position

Offer
£3,640,000
Quality
Strong

Contractor position

Tender price
£6,320,000
Rank
1 of 4

Status pill

Status pillLook in tokensMeans
Interested--color-stage-tint · --color-stage-textIn the journey, not yet agreed: Coverage to Offered.
Approved--color-stage-done-tint · --color-stage-done-textThe end of a journey that went through: Approved or Appointed.
Withdrawn--color-stage-dead-tint · --color-stage-dead-textOut of the running: Withdrawn or Not proceeding.

States

Default, the name as a link on hover, nobody yet, and a buyer who withdrew.

Buyer position

Offer
£3,410,000
Quality
Medium

Buyer position

No agreed buyer yet · 3 buyers in play

Contractor position

None appointed yet · 4 contractors invited

Buyer position

Offer
£3,300,000
Quality
Offer too low

Properties

NameTypeDefaultWhat it does
titleBuyer position | Contractor position—The card’s heading.
statusjourney stage—The pill: the stage of the buyer or contractor on this Site, with a dot and its name.
nameAccount—Links to the Account.
pairs{ label, value }[]—Key-value pairs: Offer £ and Quality, or Tender price £ and Rank.
emptytext—What shows when nobody is agreed or appointed, with how many are in play.

Fee stream card

A fee stream: basis, trigger, invoiced, collected; the effective fee rate.

Source: proposed — Fee streams of blueprint/modules/heads-of-terms.md (HOT-C12, HOT-C14)

Shows one stream of Valere’s income on a Site: how much, how sure, what triggers it and how far it has got to being collected. The one rule: every active stream names its trigger, and a fee that will never be paid says so, with who approved it, instead of just disappearing.

Variants

The four streams. A Site can hold several at once and shares one fee agreement.

VariantLook in tokensUsed for
Agency fee (RP)Agreed · --color-success-soft · --color-success-textThe fee from the RP at exchange. Basis agreed, contingent or speculative.
OverageContingent · --color-info-soft · --color-info-textA share of later uplift. Joint overage is owned by Partnerships and shown as one stream.
Contractor introduction feeAgreed · --color-success-soft · --color-success-textCounts at JCT signing; the split is set per Site.
Design / Reserved Matters feeSpeculative · --color-warning-soft · --color-warning-textEarned at Reserved Matters approval, if it comes.

On the Site record

Church Farm: four streams, none triggered yet. The effective fee rate is the total fees as a percentage of the package, worked out, never typed.

Agency fee (RP)

Not triggered
£130,000Agreed
Trigger
At exchange
Owner
Land
  1. Triggered
  2. Invoiced
  3. Collected

Overage

Not triggered
£20,000Contingent
Trigger
At the overage event
Owner
Partnerships
  1. Triggered
  2. Invoiced
  3. Collected

Contractor introduction fee

Not triggered
£72,000Agreed
Trigger
At JCT signing
Owner
Construction
  1. Triggered
  2. Invoiced
  3. Collected

Design / Reserved Matters fee

Not triggered
£40,000Speculative
Trigger
At Reserved Matters approval
Owner
Land
  1. Triggered
  2. Invoiced
  3. Collected
Effective fee rate2.63%

Total fees £262,000 of a £9,960,000 package

Land £3,640,000 + build £6,320,000

Basis

BasisLook in tokensMeans
Agreed--color-success-soft · --color-success-text · check-circleWritten into the fee agreement; will be due when it triggers.
Contingent--color-info-soft · --color-info-text · clockDue only if something else happens first.
Speculative--color-warning-soft · --color-warning-text · targetHoped for, not yet agreed with anyone.

States

Not triggered → triggered → invoiced → collected, or not payable. A triggered fee that is not invoiced is flagged once for its owner.

Agency fee (RP)

Not triggered
£130,000Agreed
Trigger
At exchange
Owner
Land
  1. Triggered
  2. Invoiced
  3. Collected

Agency fee (RP)

Triggered
£185,000Agreed
Trigger
At exchange
Owner
Land
  1. Triggered (reached)6 Nov
  2. Invoiced
  3. Collected

Not invoiced yet · 3 days

Design / Reserved Matters fee

Invoiced
£40,000Speculative
Trigger
At Reserved Matters approval
Owner
Land
  1. Triggered (reached)28 Oct
  2. Invoiced (reached)2 Nov
  3. Collected

Agency fee (RP)

Collected
£210,000Agreed
Trigger
At exchange
Owner
Land
  1. Triggered (reached)9 Oct
  2. Invoiced (reached)12 Oct
  3. Collected (reached)30 Oct

Overage

Not payable
£20,000Contingent
Trigger
At the overage event
Owner
Partnerships

Landowner waived it

Approved by Rhys Calloway, Partner / MD · 3 Nov

Effective fee rate

A fee marked not payable leaves the active streams and the rate; its history stays.

Effective fee rate2.63%

All four streams active: £262,000

Total fees as % of a £9,960,000 package

Effective fee rate2.23%

Design not payable: £222,000

Total fees as % of a £9,960,000 package

Properties

NameTypeDefaultWhat it does
streamagency | overage | contractor | design—Which stream of income it is; a Site can hold several.
amount£—The fee in sterling. Never typed as a percentage.
basisagreed | contingent | speculative—How sure the fee is.
triggerevent—The event that makes it due; every active stream has one.
paymentnone | triggered | invoiced | collected | notpayablenoneWhere the fee is: triggered, invoiced, collected, or never to be paid.
ownerfunction—The function that records and chases it; shown as a chip.

Source and confidence

Where an AI-read value came from and how sure the reading is.

proposed Every AI-read value carries it (N-08); the pilot has no AI layer yet.

A chip on every value the system read from a document, an email or a web page: the source with its page or address, how sure the reading is, or “Not found”. The one rule: nothing takes effect until a named person approves it.

The chip

Not found

Left to right: read with high, medium and low confidence from a document and page; read from a web address; nothing found.

Variants

VariantLook in tokensUsed for
HighThree bars filled, success text on the success tintA value the document states plainly: a figure, a date, a name
MediumTwo bars filled, warning text on the warning tintA value inferred from the wording or read from a poor scan
LowOne bar filled, danger text on the danger tintA guess the person must check before relying on it
Not foundDashed border, muted text, no bars, a search iconA field the system looked for and could not find; the field stays empty

Nothing takes effect until a named person approves

The AI only reads; a person decides. The chip stays on the value after approval, so anyone can see where it came from.

Read, awaiting approval
Planning status Outline consent granted Awaiting approval by Jamie Morgan
Approved
Planning status Outline consent granted Approved by Jamie Morgan, 9 Nov 2026
Nothing found
Affordable homes No value yet Not found Land fills this in by hand

A read value is shown apart from the record: dashed edge, the sparkles mark and the approver named. Approving makes it the record’s value and keeps the source beside it; until then no stage, score or report uses it (N-08).

States

Default
Hover
Focus
Not foundNot found

The chip is a button: it opens the document at the page, or the web address in a new tab. “Not found” opens nothing, so it is not a button.

Sizes

Default: beside a field
Compact: in a table cell

Properties

NameTypeDefaultWhat it does
sourcestringrequiredThe document and page, the email, or the web address the value was read from
confidence'high' | 'medium' | 'low'requiredHow sure the reading is; shown as bars and as a word, never colour alone
foundbooleantrueWhen false the chip says “Not found” and has no source
compactbooleanfalseThe smaller chip for a table cell: the source is shortened, the confidence stays
approvedByperson | nullnullThe named person who approved the value; empty means it still waits

Proposed update

A change read from an email or a meeting, beside its source, to accept or discard.

proposed Design concept, Buyers; the Pending proposals list on the home page is the pilot’s nearest.

A change the system read from an email or a Teams meeting, shown beside the email it came from: the field, the old value and the new one, who proposed it, and Accept or Discard. The one rule: it changes nothing on the record until a named person accepts it, and accepted and discarded are final.

Beside its source email

Proposed update

Proposed Accepted Discarded
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026
Site
Church Farm, Rugby · CE-014
Proposed by
The system, from an email logged by Dana Whitfield
Source

Approver: the owner of this field, Dana Whitfield (Partnerships). Nothing changes on the record before then.

Written to the record with the email linked, by Dana Whitfield, 9 Nov 2026, 10:02. Final.

Discarded by Dana Whitfield, 9 Nov 2026, 10:02. The record is unchanged; only the audit log remembers. Final.

Live: Accept and Discard both settle the proposal; a settled proposal is final, so the sample offers only a reset.

States

Proposed update

Proposed
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026

Waiting for the approver. It also appears in their Pending proposals on the home page.

Proposed update

Accepted
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026

Written to the record by Dana Whitfield on 9 Nov 2026; the source email stays linked to the change.

Proposed update

Discarded
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026

Discarded by Dana Whitfield on 9 Nov 2026. The record is unchanged.

Proposed update

Superseded
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026

A newer proposal for the same field from the same thread replaced it: Fri 18 Dec 2026.

Proposed update

Lapsed
Exchange date Fri 27 Nov 2026Fri 11 Dec 2026

Nobody decided within 14 days, or the Site was closed. It can no longer be accepted.

Variants

VariantLook in tokensUsed for
Beside the emailTwo columns on the surface: the source email, then the update; a border and the card shadowThe approver’s screen: decide with the email in view
CompactThe update column alone, one line of outcome under the changeA list of proposals, the Lead page and the Site stage bar’s display line
From a meetingThe same, with the passage of the transcript in place of the emailA change read from a Teams meeting; the chair approves them in one go

The five states

StateLook in tokensWhat it means
ProposedInfo tone, clock icon; Accept and Discard shownRead from the email; it changes nothing yet
AcceptedSuccess tone, check icon; no actionsA named person accepted it: final
DiscardedNeutral, x icon; no actionsA named person discarded it: final, no trace on the record but the audit entry
SupersededNeutral, arrow icon; no actionsA newer proposal for the same field from the same thread replaced it
LapsedWarning tone, clock icon; no actionsIt waited past the admin-set age, or its Site was closed

Properties

NameTypeDefaultWhat it does
fieldstringrequiredThe record’s field the proposal would change
from / tostringrequiredThe current value, struck through, and the proposed one
state'proposed' | 'accepted' | 'discarded' | 'superseded' | 'lapsed''proposed'Decides the badge and whether the actions show
sourceemail | transcriptrequiredShown beside the update, with its source and confidence chip; “redacted” if the thread was later made private
proposedByperson | systemsystemWho proposed it: the system, or the person whose MCP connector made the change
onAccept / onDiscard() => voidrequiredAccepting always happens on the web surface; the email is linked to the change

Activity timeline

Calls, emails, meetings, notes and system entries, by day, by function.

Source: web/packages/crm/src/pages/lead-detail/ActivityTimeline

The log of what happened on a Site, Account or Contact: calls, emails, meetings, notes and stage changes that people logged, and the entries the system wrote when a rule fired. The one rule: entries are only ever added, newest first, and a system entry looks different from a person’s.

The timeline

Today · Mon 9 Nov 2026

  1. Dana WhitfieldPartnershipsCall

    Spoke to Calder Valley Homes about Heron Fields. They will confirm their interest by Friday.

  2. SystemSystemRule

    Stalled: no progress logged on Lea Bridge Works for 14 days. Rule: stalled after 14 days.

Fri 6 Nov 2026

  1. Jamie MorganLandMeeting

    Fee discussion with the landowner at Hatch Farm. Fee basis to be confirmed with Rhys Calloway.

  2. Conor HigginsConstructionEmail

    Sent the tender pack for Church Farm to Westbrook Construction.

  3. Dana WhitfieldPartnershipsStage change

    Heron Fields moved from Acceptable Offer to Under Offer.

Thu 5 Nov 2026

  1. Jamie MorganLandNote

    Landowner would like to complete before Christmas. Ask Hadley & Finch LLP about the longstop date.

Filled dot: a person logged it, in their function’s colour Hollow dot: a system rule wrote it

Variants

VariantLook in tokensUsed for
Person entryFilled dot in the function’s colour, name in semibold, function chip in its tint, type tag in neutralA call, email, meeting, note or stage change someone logged
System entryHollow dot with a muted ring, the name “System”, the System chip in neutral, type Rule or Stage changeA stall rule or another rule firing; the entry names the rule
Day headerSemibold caption in muted text over a hairline, one per dayEntries grouped by day, newest first
ComposerA text area on the surface with a segmented type choice and a primary Log buttonLogging by hand at the top of the Activity & Risk tab, an Account or a Contact

States

StateLookWhat the user sees
FilledDay headers, entries newest firstAll is the default; a pill filters by function, and a day with nothing left disappears
Filter onThe pressed pill takes the primary tint and keeps its labelOnly the entries of that function; the pill is aria-pressed
EmptyOne sentence under the composerNo entries yet, or none for the function chosen; the log action stays offered
FocusRing on a pill, the type choice, the text area or LogKeyboard users move through the pills, then the composer

Properties

NameTypeDefaultWhat it does
entriesActivity[][]The log, newest first; nothing is ever overwritten
entry.type'call' | 'email' | 'meeting' | 'note' | 'stage' | 'rule'requiredShown as the type tag; a meeting also lists its attendees from Contacts
entry.authorperson | systemrequiredA person gives a filled dot, their name and function; the system a hollow dot
filter'all' | 'land' | 'partnerships' | 'construction' | 'system''all'Filters by the function of the author
canLogbooleantrueShows the composer; every internal role may log on what it may see

Score card

A recommended buyer: the score out of 100 and the match on each criterion.

Source: web/shared/components/BuyerMatch · design concept, Buyers tab

A recommended buyer for a Site: the score out of 100 with its label, the reason in words, the four criteria as chips and, behind Why, the arithmetic. The one rule: a gap is never hidden in the score; a criterion with no data is shown as not recorded, with a dashed edge.

Recommended buyers for Church Farm

ScoreBuyer and reasonCriteriaAction
85Strong

Calder Valley Homes RP, grant funded

Active in Central, 28 homes fits their 20 to 80 range and they take S106, but they prefer land-only deals.

Region: full pointsUnits: full pointsS106: full pointsTurnkey: no points
CriterionResultWhyPoints
RegionMatchCentral is one of their active regions40 / 40
UnitsMatch28 homes is inside their 20 to 80 range30 / 30
S106MatchThey take S106; the site has 8 affordable homes15 / 15
TurnkeyNo matchThey prefer land-only deals; this site is Land & Build0 / 15
Criteria from the Calder Valley Homes account, updated 12 Aug 2026. Open account85 / 100
70Fair

Northfield Partnership RP, grant funded

Active in Central and takes S106 and turnkey, but 28 homes is below their 40-home minimum.

Region: full pointsUnits: no pointsS106: full pointsTurnkey: full points
65Fair

Ashby Rural Housing RP, grant funded

Takes S106, and 28 homes fits their 10 to 30 range; active next door in South & West. Turnkey appetite is not recorded.

Scored on 3 of 4 criteria. Completing turnkey appetite on the account can raise the score to 80.

Region: half pointsUnits: full pointsS106: full pointsTurnkey: not recorded: not recorded, no points

Scored out of 100 by rules with the admin’s weights: Region 40, Units 30, S106 15, Turnkey 15. A partial match earns half the points. Below 50 a buyer is counted under the list, not shown in it. The score is arithmetic, never the AI.

Criteria chip key

Regionfull points Regionhalf points: a neighbouring region, or units within 10% of the range Regionno points Region: not recordedno data, 0 points

Variants

VariantLook in tokensUsed for
Strong, 80 and aboveSuccess text on the success tint, in a round figureA buyer worth pitching first
Fair, 50 to 79Warning text on the warning tint, in a round figureA buyer worth a conversation; the Why shows what holds the score back
Below 50Muted text on the muted surface; not listed, counted under the listShown only on request: “2 buyers scored under 50”
Criterion: fullSuccess tint and text, check iconFull points
Criterion: halfWarning tint and text, info iconHalf points: a neighbouring region, or units within 10% of the edge
Criterion: noMuted surface and text, x iconNo points
Criterion: not recordedDashed border on the surface, minus mark, “not recorded”No data on the buyer’s account: the gap stays visible and scores 0

States

StateLookWhat the user sees
Why closedThe row: score, buyer and reason, the four chips, Why and Add to journeyThe recommendation at a glance
Why openThe Criterion, Result, Why and Points table under the row, with the total and the date the buyer’s criteria were updatedThe arithmetic behind the number; the button’s chevron turns
Incomplete criteriaA dashed chip and the line “Scored on 3 of 4 criteria”What to complete on the account to raise the score
Read onlyAdd to journey locked, a note naming PartnershipsLand, Construction and Back Office read scores; only Partnerships adds a buyer

Properties

NameTypeDefaultWhat it does
buyerAccountrequiredThe buyer: name, type and funding, linked to its account
scorenumber (0–100)requiredThe total; the band and its label follow: Strong from 80, Fair from 50
reasonstringrequiredThe score in words, one clause per criterion
criteria{ result, points, max }[]requiredRegion, Units, S106 and Turnkey; result is full, half, no or not recorded
criteriaUpdateddaterequiredWhen the buyer’s criteria were last updated; shown in the Why panel
canAddbooleantrueFalse for every role but Partnerships: Add to journey is locked

Comparison table

Offers or bids side by side, like for like, with the grade and the rank.

proposed Built on the data table; the pilot’s Buyer journey table is the nearest.

Offers from buyers, or bids from contractors, in one table so that they can be read against each other: the same Site, the same columns, every version kept. The one rule: the system suggests the grade, a person confirms it, and both are shown.

Offers, like for like

Heron Fields, Colchester · LE-022 · 46 homes · GDV £10,350,000 · every version of every offer priced against the same Site

BuyerLand value£ per unit% of GDVFeeVersionSuggested gradeConfirmed grade
Calder Valley Homes £7,820,000 £170,000 75.6% £312,800 v2 2 versions Exceptional ExceptionalMatches suggestion
Ashby Rural Housing £7,590,000 £165,000 73.3% £303,600 v1 1 version Good Not yet confirmed
Northfield Partnership £7,245,000 £157,500 70.0% £289,800 v1 1 version Weak AcceptableChanged from suggestion

The system calculates % of GDV and suggests the band: Exceptional 75% or more, Good 73% to under 75%, Acceptable 71% to under 73%, Weak under 71%. The PM confirms or changes it; a changed grade is marked. With no GDV the cell is grey and no grade is suggested.

Bids, like for like

Church Farm, Rugby · CE-014 · 28 homes · 24,640 sqft · every bid is base price plus itemised abnormals

ContractorBase priceAbnormals£ per sqftCSARank
Westbrook Construction £5,420,000 £186,000 £220 Attached 1
Marlow & Pike Build £5,590,000 £94,000 £227 Acceptable 2
Ridgeway Contracts £5,780,000 None stated £235 None 3
Harcourt Build Withdrawn £5,350,000 £210,000 £217 Attached Out of the rank

Ranked on base price, lowest first. £ per sqft is the only area unit. A withdrawn bid leaves the rank and stays on record; a bid with no itemised abnormals is flagged for Construction’s review.

Variants

VariantLook in tokensUsed for
OffersThe data table on the surface; figures right-aligned and tabular; the grade chips in their category tokensBuyers and Offers: every version of every offer, with the suggested grade beside the one the PM confirmed
BidsThe same table; the rank in a round badge; a withdrawn row mutedContractors and Tenders: base price and abnormals side by side, with the CSA status and the rank
Grade chipExceptional, Good, Acceptable, Weak in the --color-grade-* text and tintThe suggested and the confirmed grade; the word is always shown

States

StateLookWhat the user sees
ConfirmedBoth grades filled; a check and “Matches suggestion”The PM agreed with the system
ChangedBoth grades filled; a pencil and “Changed from suggestion”The PM chose another band; the earlier suggestion stays visible
Not yet confirmedDashed muted text in the Confirmed columnThe system has suggested; nobody has chosen
Missing dataA grey dash in % of GDV and no suggestion; “None stated” in AbnormalsThe figure the comparison needs is not on record
WithdrawnThe row muted, a Withdrawn badge, “Out of the rank”The contractor withdrew or was moved to the Removed list

Properties

NameTypeDefaultWhat it does
kind'offers' | 'bids'requiredChooses the columns
rowsOffer[] | Bid[][]One row per version; earlier versions stay on record
suggestedGradegradenullThe band the system calculated from % of GDV; a suggestion only
confirmedGradegrade | nullnullThe PM’s choice; empty shows “Not yet confirmed”
rankBy'basePrice''basePrice'How bids are ranked; withdrawn bids leave the rank
canEditbooleanfalseOnly Partnerships grades an offer and only Construction a bid; everyone else reads

Approval checklist

The Instruction Approval gate: each item’s evidence, reviewer and date, or a partner’s exception.

proposed The gate is in the brief; the pilot has no checklist component.

The final gate before a Site is instructed: every item with its evidence, its reviewer and the date, or a partner’s named exception. The one rule: Record instruction stays locked until every item is complete or excepted.

Locked: items still open

Instruction Approval

Hatch Farm, Sindlesham · SW-031

3 of 5 closed
  1. Complete

    Technical pack reviewed (title and covenants, drainage, rights of way)

    Technical pack v3.pdf
    Jamie Morgan
  2. Complete

    Planning portal comments checked (highways, drainage, heritage)

    Portal notes, 5 Nov.docx
    Jamie Morgan
  3. Open

    Unit mix checked against actual site constraints

    Evidence needed: a link to a SharePoint document, or a note
    No reviewer yet
  4. Excepted

    Construction route and cost validated by Construction

    No evidence: a partner’s exception stands in for it
    No reviewer yet

    Named exception · Rhys Calloway, Partner / MD · 6 Nov 2026

    The pre-quote estimate is still being prepared. Construction will validate it before exchange.

  5. Open

    Fee agreement ready to sign

    Evidence needed: a link to a SharePoint document, or a note
    No reviewer yet

The Instruction cannot be recorded while an item is open and not excepted. Open now: unit mix, fee agreement.

Ready: every item complete or excepted

Instruction Approval

Hatch Farm, Sindlesham · SW-031

5 of 5 closed
  1. Complete

    Technical pack reviewed (title and covenants, drainage, rights of way)

    Technical pack v3.pdf
    Jamie Morgan
  2. Complete

    Planning portal comments checked (highways, drainage, heritage)

    Portal notes, 5 Nov.docx
    Jamie Morgan
  3. Complete

    Unit mix checked against actual site constraints

    Unit mix check.xlsx
    Jamie Morgan
  4. Excepted

    Construction route and cost validated by Construction

    No evidence: a partner’s exception stands in for it
    No reviewer yet

    Named exception · Rhys Calloway, Partner / MD · 6 Nov 2026

    The pre-quote estimate is still being prepared. Construction will validate it before exchange.

  5. Complete

    Fee agreement ready to sign

    Fee agreement draft.docx
    Jamie Morgan

Every item is complete or has a partner’s exception. The Instruction can be recorded.

Variants

VariantLook in tokensUsed for
CompleteSuccess tone, check icon; the evidence link, the reviewer’s avatar and name, the dateAn item closed with its evidence
OpenNeutral badge, clock icon; a line that says what evidence is needed; two actionsAn item not yet closed; it blocks the Instruction
ExceptedInfo tone, lock icon; a block under the item naming the partner, the date and the reasonAn item a partner approved a named exception for; it counts as closed and stays visible as excepted
Record instructionPrimary button; locked is disabled with a lock icon and the reason beside itThe last step: it unlocks only when every item is complete or excepted

States

StateLookWhat the user sees
LockedDisabled button, lock icon, a sentence naming the open itemsWhy the Instruction cannot be recorded, and what to close
ReadyActive primary button, a check icon in the sentenceThe gate is open; Land records the Instruction and Deal Type
Exception withdrawnThe item returns to Open; the exception stays in the logThe button locks again
FocusRing on the evidence link, the actions and the buttonThe locked button stays reachable and its reason is read with it

Properties

NameTypeDefaultWhat it does
itemsItem[]requiredThe gate’s items: technical pack, planning comments, unit mix, construction route and cost, fee agreement
item.state'open' | 'complete' | 'excepted''open'Open until closed with evidence or excepted by a partner
item.evidencelink | notenullA SharePoint document link or a note; an item cannot close without it
item.reviewer / dateperson / datenullWho closed it and when; audited
item.exception{ by, date, reason } | nullnullOnly a Partner / MD approves one; it names the item, the reason and the date
onRecord() => voidrequiredRecords the Instruction; refused by the system while any item is open and not excepted

Reserve

Components the product may need later, each with why.

proposed

These four are not in the first release. Each carries the reserve tag and one line of why it may be needed. The rule: none is built until a flow asks for it, and each is made of the tokens and components above, so it arrives looking like the rest.

Site map

reserve

May be needed when Land wants to see where the pipeline sits by region. PostGIS already holds every Site’s location, so a map is a new view, not new data.

South & West Central London & East SW-031 · 68 CE-014 · 55 LE-019 · 41 LE-022 · 72

Pin: a Site, by Site Code and Deal Health

  • 70 and above
  • 40 to 69
  • Below 40

The figure on a pin is the Site’s Deal Health. A drawing of the idea: the product would draw its own map over the PostGIS locations.

Variants pins by Site (default); clustered when zoomed out; coloured by Deal Health or by Site stage; filtered by region or function.

Signature status

reserve

May be needed once Adobe Sign is connected: the PM sees where a fee agreement or Heads of Terms is without opening the envelope.

Fee agreement · Hatch Farm (SW-031) Waiting for Valere
  1. Sent 4 Nov 2026 To Edward Marsh, the landowner
  2. Viewed 5 Nov 2026 Opened by Edward Marsh
  3. Signed by landowner 6 Nov 2026 Edward Marsh
  4. Signed by Valere Not yet signed Waiting for Rhys Calloway since 6 Nov 2026
Sent through Adobe Sign; the status comes back by itself.

Variants full card on a record; a single badge in a table row; declined or expired, with the step in the danger tone and the reason.

Comment thread on a record

reserve

May be needed if the team wants to talk on the Site itself instead of in email; today a note is an entry in the Activity timeline.

Comments 2 Hatch Farm (SW-031)
  • Jamie Morgan Land 9 Nov 2026, 09:40

    Edward Marsh confirmed 33 units on the revised layout. Drawings follow on Thursday.

  • Dana Whitfield Partnerships PM 9 Nov 2026, 10:05

    Thanks, Jamie. I will hold the Fee Discussion until the drawings are in.

The person mentioned is notified.

Variants on a Site, a Contact or a Buyer; with @mentions; a resolved thread folds to one line.

Kanban of Sites by stage

reserve

May be needed for the Monday meeting, if the partners want the pipeline as columns beside the table.

Fee Discussion2
  • SW-031 Hatch Farm Sindlesham · 33 units 68£145,000
  • CE-017 Orchard Way Daventry · 29 units 77£168,000
Acceptable Offer3
  • LE-019 Lea Bridge Works Walthamstow · 64 units 41£380,000
  • SW-027 Orchard Lane Wokingham · 52 units 74£298,000
  • LE-024 Mill Lane Braintree · 38 units 35£205,000
Under Offer1
  • LE-022 Heron Fields Colchester · 46 units 72£310,000
Legals2
  • CE-014 Church Farm Rugby · 28 units 55£262,000
  • SW-024 Kingsmead Basingstoke · 40 units 49£176,000

Deal Health beside the figure: check 70 and above, warning 40 to 69, cross below 40.

Variants columns by Site stage (default) or by owner; swimlanes by region; moving a card opens the stage-change check, so the gates still apply.