Components/Button
Valere CRM · Design foundations

Components

Components

Every component of the app on one page, one section each: what it is for, its variations and states, its properties and sizes. Scroll through them, or pick one in the menu; the menu marks the one on screen.

Source: web/shared/components, and the page parts in web/packages/crm/src where a component lives there.

Button

The app's one button. Primary is for the single main action on a screen. A disabled button always says what is missing.

Source: web/shared/components/Button/ (Button.tsx, useButton.ts for the disabled reason, Button.styles.ts, index.ts).

Variants

Four variants, one size.

primary
secondary
ghost
destructive
VariantLookUsed for
primaryFilled primary-600, white textThe one main action of a screen or a form
secondaryWhite, gray-200 border and the card shadow, gray-900 textOther actions: a card's actions, Search, the pager
ghostNo border or fill, primary-600 textCancel and other low-weight actions
destructiveFilled crit-text, white textAn action that closes or removes something, such as Mark as Lost

States

Hover darkens, keyboard focus draws a ring, disabled goes gray whatever the variant.

Primary
default
hover: primary-700
keyboard focus: 3 px focus ring, 2 px offset
disabled
Other variants on hover
secondary: gray-50
ghost: primary-100
destructive: 10% darker
Disabled with its reason
Needs Heads of Terms agreedthe reason sits under the button and is read out with it

Width

A button is as wide as its label. fullWidth stretches it to its container, as on the sign-in card.

Properties

PropertyValuesWhat it does
variantprimary, secondary, ghost, destructive; default primaryThe look, as above
fullWidthboolean; default falseStretches the button to its container
disabledbooleanGray, not clickable
disabledReasontextShown under a disabled button and tied to it with aria-describedby
typedefault buttonPass submit inside a form

Size: 36 px high, 16 px side padding, 8 px radius, 8 px between an icon and the label, text 14 / 20 Medium, after Apiko's design concept.

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • Size and weight. The board: 44 px high, 20 px side padding, Semibold, a gray-300 border on the secondary button. Since 2026-10-07 the button follows Apiko's design concept: 36 px, 16 px padding, Medium, a lighter border with the card shadow.
  • Icon button. The board has a 44 × 44 square button with a plus ("Add action"). The app has no such component: actions are added with an ordinary text button.

Text field

A labeled single-line input. The label sits above the field; a hint, an error or a lock line sits under it.

Source: web/shared/components/TextField/ (TextField.tsx with TextField and LockedHint, useTextField.ts for the ids, the descriptions and Show / Hide, TextField.styles.ts, index.ts); the text area is LabelledTextArea in web/packages/crm/src/pages/lead-detail/fields.tsx.

States

default

Hint text sits under the field.

focus: focus border, 3 px primary-300 ring

Enter the number of units as digits

error mark only: the form-level message says what is wrong

Set by Back Office. Read only for Land.

Types

The same field for every input type the app uses.

password: a Show / Hide button inside the field

date: opens the proposed date picker

search: inside the search form

Text area

For notes. Three rows, at most 5000 characters.

Lock line

The same line also stands on its own beside a control a role cannot use: a stage move, a switch, a tab's buttons. Every locked thing names its owner the same way.

Set by Back Office. Read only for Land.

Land moves this deal on. Read only for Construction.

Read only for Admin.

Properties

PropertyValuesWhat it does
labeltext, requiredThe label above the field
hinttextA gray line under the field
errortext or trueText: a red line with an icon under the field. true: only the red border, for a form whose own message already says what is wrong
readOnlybooleanGray fill, not editable
readOnlyHinttextThe lock line under a read-only field; names the function that owns it
typedefault textpassword adds Show / Hide and turns off spellcheck and autocorrect

Sizes: label 12 / 16 Semibold gray-700; 6 px between label, field and the line under it; field 36 px high (the height of a button, after Apiko's design concept), 12 px side padding, 8 px radius, text 14 px; the Show / Hide button inside a password field is 28 px; an error is a 2 px crit-text border.

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • Height. The board: 44 px. Since 2026-10-07 the field is 36 px, the height of a button, after Apiko's design concept; its border stays gray-300, so its edge doesn't fade further.
  • Search. On the board the search field has a magnifier icon inside. In the app it is a plain field with a "Search" button beside it.

Select

A list to choose one value from. Two forms: a compact filter that carries its name inside each option, and a field with a label above it. Since 2026-10-07 the app draws the list itself: a chevron that turns when the list is open, a white panel on the popover shadow, 36 px options with a check on the chosen one.

Source: web/shared/components/Dropdown/ (the control and its list, ADR-0040: Dropdown.tsx, the useDropdown hook, Dropdown.styles.ts); Select.tsx beside it (the filter); LabelledSelect in web/packages/crm/src/pages/lead-detail/fields.tsx (the labeled field, also the role on Create staff user).

Filter

Used above the overview tables. It has no label above it: each option reads "Region: All", "Region: Berkshire", so the chosen value explains itself. The name is also the control's accessible name.

default: "All" is the first option
a value chosen
disabled
a value from an old link that no option matches

The lists open as the app's dropdown: a 16 px chevron 12 px in from the edge that turns when the list is open, a white panel on the popover shadow with 36 px options and a check on the chosen one, above the control when there is more room there. Keyboard: arrows, Home, End, Page Up and Page Down move; typing finds an option by its own word ("b" steps through the regions starting with B); Enter and Space choose; Escape closes.

Labeled field

Used in forms. A placeholder option and a hint are optional.

Required before Instruction. Decides whether the Contractor stage applies.

Properties

Filter (Select)

PropertyValuesWhat it does
labeltextThe filter's name: the prefix of every option and the accessible name
valuetext; empty is "All"The chosen option
optionslist of value and labelThe choices after "All"
allLabeltext; default "All"The wording of the first option
disabledbooleanGray, not usable

Labeled field (LabelledSelect)

PropertyValuesWhat it does
labeltextThe label above the field
optionslist of value and labelThe choices
placeholdertextA first option with no value, such as "Select a deal type"
hinttextA gray line under the field

Size: 36 px high (the height of a button and a field, after Apiko's design concept), 12 px side padding, 8 px radius, text 14 px; the filter is at least 160 px wide.

Compared with the design board

The "Leads Overview" board of the Design Foundations canvas.

  • Height. The board: 44 px. Since 2026-10-07 the control is 36 px, in line with the button and the text field, after Apiko's design concept.
  • The boards draw native selects. The app has opened its own list since 2026-10-07 (App changes item 1, now in the app); the boards still show the browser's arrow and leave the list to the system.

Date picker

A field that opens a calendar to choose one day. Every word in it is English, whatever language the browser is set to.

Proposed, not in the app yet: see App changes. In the app a date is a TextField with type="date", in three places: Due date in AddActionForm, Date in LogActivityForm and Tender due in ContractorRow.

The calendar

One month at a time, weeks starting on Monday. Shown open here; in a form it opens under its field, or above it when there is more room there.

PartLook
Month title"October 2026", 14 / 20 Semibold; two arrows step a month back and forward
Weekday rowMo to Su, 12 / 16 Semibold gray-500
Day36 px square, 8 px radius; gray-100 under the pointer
TodayA 1 px primary-600 ring, primary-700 Semibold
Chosen dayFilled primary-600, white Semibold
Day of a neighboring monthgray-500; still a day that can be chosen
Day that is not allowedgray-300, not clickable
Clear, TodayText buttons in primary-600; Clear empties the field, Today chooses today

Field states

The field is the same 36 px control as a text field, with the calendar icon at its right. Click a field to open its calendar.

a day chosen: "12 Oct 2026", the app's date format

empty: "Select a date" in gray-500

no later than today: later days are gray and cannot be chosen

disabled

Keyboard

Opening the calendar puts the focus on the chosen day, or on today when the field is empty.

KeyDoes
Enter or Space on the fieldOpens the calendar
← →The day before, the day after
↑ ↓The same weekday a week earlier, a week later
Home, EndMonday and Sunday of the week
Page Up, Page DownThe same day a month earlier, a month later
Enter or Space on a dayChooses it and closes the calendar
EscapeCloses the calendar and returns to the field; the value stays
TabMoves through the month arrows, the days, Clear and Today; leaving the calendar closes it

Values and sizes

PropertyValuesWhat it does
valuea day as YYYY-MM-DD, or emptyThe chosen day; the same text the app's forms pass to a date field today
min, maxa day as YYYY-MM-DDThe earliest and latest day that can be chosen; an Activity's date has max set to today
disabledbooleanGray, does not open

Sizes: field 36 px high, 12 px side padding, 8 px radius, a 16 px calendar icon. Calendar: 12 px padding, 8 px radius, the popover shadow, seven 36 px columns 2 px apart, always six weeks so its height does not change from month to month; arrows and text buttons 32 px high.

Search form

A list's free-text search: a field and a Search button. It applies when the form is submitted, not on every keystroke, so typing does not send a query per letter.

Source: web/shared/components/SearchForm.tsx.

States

empty
a search applied

Behavior

  • The text is trimmed before it is applied, and is at most 100 characters.
  • The applied search lives in the page's URL, with the filters and the page number.
  • Clearing the filters empties the box.

Properties

PropertyValuesWhat it does
labeltextThe label above the field
valuetextThe search that is applied now
onSearchfunctionCalled with the trimmed text on submit

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • No button on the board. The board draws one search field with a magnifier inside; the app pairs a plain field with a Search button.

Switch

An on / off control. The app has one: "Land terms agreed" on the Opportunity page. It is written inside that card, not as a shared component.

Source: web/packages/crm/src/pages/opportunity-detail/LandTermsCard.tsx.

States

The switch renders only for a user who may change the value. Everyone else sees the state as text, with a line that says why.

Land terms agreedSet by Land. Heads of Terms needs it.

Not agreed yet

Land terms agreedSet by Land. Heads of Terms needs it.

Agreed

Land terms agreed

Set by Land. Read only for Construction.

Agreed

Land terms agreedFixed from Legals on, and on a closed deal.

Agreed

StateWho sees itLook
OffThe owning role, while the value can changeTrack gray-300, knob on the left, "Not agreed yet" in gray-500
OnThe owning role, while the value can changeTrack primary-600, knob on the right, "Agreed" in good-text
LockedAny other roleNo switch; the lock line names the owner
FixedEveryone, once the deal is past the stages where it can changeNo switch; a note says when it was fixed

Size: 44 × 24 px track, fully rounded; 20 px white knob with a soft shadow, 2 px in from either end. While a change is being saved the switch is disabled.

Compared with the design board

The "Opportunity: Overview" board of the Design Foundations canvas. This section shows the app's version.

  • Not on the foundations boards. The switch appears only on the Opportunity board.

Chip and tag

Small labels that say what kind of thing a record is. Stage, deal type and owning function are always separate things, so each has its own color, and a record's state (its stage, its deal type) carries a dot.

Source: web/shared/components/Chip/: Chip.tsx (the component, and the label maps for stages, deal types, account types and the other enums), Chip.styles.ts (the classes and the function colors), index.ts.

Stage

A blue pill: primary-100 with primary-700 text, a hairline border a shade darker than the fill, and a dot in the text's color. One per stage of a Site. In a table cell it is drawn plain, with no border or dot, as the concept draws it in its tables; that also keeps it inside the narrow Stage and Status columns.

Lead stages
New LeadContactedQualifiedAppraisal RequestedAppraisedFee DiscussionInstructed
Opportunity stages
BuyerContractorHeads of TermsLegalsExchange / CompletionClosed / Lost
In a table cell
StageDeal type
Heads of TermsLand & Build

Owning function

A tinted label in the function's own color, with 8 px corners and no dot. It shows which function owns the current stage or an action.

Back OfficeLandPartnershipsConstructionLegal

Deal type and neutral tag

Deal type is a neutral pill with a dot, like a stage: gray-100 with gray-700 text and a gray-200 hairline, plain in a table cell. Any other neutral label (an account's type, a critical Legal task) is a plain gray pill, with no border and no dot.

Deal type
Land OnlyLand & BuildTurnkey / Whole BlockS106
Other labels
Buyer / RPContractorLandownerOtherSolicitorLandowner contactCritical

Properties

PropertyValuesWhat it does
kindstage, dealType, tag, functionWhich of the four looks
fnBACK_OFFICE, LAND, PARTNERSHIPS, CONSTRUCTION, LEGALFor function only: picks the color and the label
childrentextThe label, for every kind but function

Size: 24 px high, 10 px side padding, text 12 / 16 Medium; a 6 px dot, 6 px before the text. Stage, deal type and tag are fully rounded; a function has 8 px corners.

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • The concept's chips. The board: Semibold pills, and the deal type as a square white tag with a gray border. Since 2026-10-07 the chips follow Apiko's design concept: Medium text; a stage and a deal type carry a dot and a hairline border (plain in a table cell); the deal type and any other tag are gray pills; a function has 8 px corners.
  • The "Buyer stage" chip. The board labels the stage "Buyer stage"; in the app the stage is called "Buyer".

Status pill

A pill with an icon that says how something is doing. Three tones, each with its own icon shape, so a status never depends on color alone.

Source: web/shared/components/StatusPill.tsx, StatusIcon.tsx; the Stalled badge is StallBadge in DealHealth.tsx.

Tones

Without text of its own, a pill shows its tone's default label.

On trackgood: a check in a circle
At riskwarn: a triangle
Stalledcrit: a cross in a square
ToneColorsDefault labelMeans
goodgood-text on good-tintOn trackDeal Health 70 and above; a deadline further than a week away
warnwarn-text on warn-tintAt riskDeal Health 40 to 69; a deadline this week; no next action
critcrit-text on crit-tintStalledDeal Health below 40; an overdue deadline; a stall rule fired

Custom text

Any text can replace the default label; the tone and icon stay.

Deadline flag on the Morning Meeting Board
OverdueDue this weekOn trackNo next action
Other uses
Expectation 8.7% above appraisalOldest outstanding

Stalled badge

A crit pill that names the first stall rule that fired; "+n" when more did. Hovering it lists them all. A deal that is not stalled has no badge.

One rule
Stalled: no contact 21 daysStalled: Heads of Terms open 34 daysStalled: tender overdueStalled: critical Legals task past deadline
More than one
Stalled: no contact 21 days+2

Icon

The status icon is also used on its own, in the color of the text around it.

Sizes
14 px: in pills and score chips
16 px: a password rule
18 px: a form message, a done action
24 px: the success mark

Properties

PropertyValuesWhat it does
statusgood, warn, critThe tone: colors and icon
childrentextReplaces the default label

Size: 24 px high, fully rounded, 10 px side padding, 6 px between icon and text, text 12 / 16 Semibold.

Health score

The Deal Health Score: a number from 0 to 100 beside its band's icon, so it reads without color. Clicking it opens the breakdown that explains the number.

Source: web/shared/components/HealthChip/ (HealthChip.tsx with the bands, HealthChip.styles.ts, index.ts), DealHealth.tsx (HealthBreakdown).

Bands

The band decides the color and the icon. The thresholds live in one place on the web side.

Deal health 100Deal health 70Deal health 69Deal health 40Deal health 39Deal health 0
BandScoreTone
Green70 to 100good, check in a circle
Amber40 to 69warn, triangle
Red0 to 39crit, cross in a square

Display and button

On its own the score is a label. With a click handler it becomes a button with an "i" icon, darker under the pointer.

Display only
Deal health 72Deal health 45Deal health 31
Button
No score
—a deal in a final stage has no score

Breakdown

Every component with its points and the fact behind them, then the stall rules that fired. Click the score to collapse or expand the sample.

Deal health 75 (100 less each component; 70 and above)

  • Recent contact
    1 day since the last contact; points off from 14 days
    0
  • Overdue actions
    1 open action past due
    −10
  • Stage duration
    12 days in this stage; no limit for this stage
    0
  • Next action
    4 open actions
    0
  • Legals
    1 critical task past deadline (counted in the Legals stage)
    −15

Stall rules fired

  • critical Legals task past deadline
  • tender overdue

Proposed weights, not yet approved by Valere.

In the app the panel is 384 px wide and fixed to the viewport. It opens below the score, or above it when there is more room there, and closes on Escape, a click outside, a page scroll or when focus leaves it.

Properties

Score (HealthChip)

PropertyValuesWhat it does
scorenumberRounded and kept within 0 to 100
onClickfunctionMakes the score a button
expanded, controlsboolean, element idTell assistive technology whether the breakdown is open and which element it is

Breakdown (HealthBreakdown)

PropertyValuesWhat it does
healthscore and its components, or nothingNothing renders the dash
stallRuleslist of fired rulesShown under the components

Size: 24 px high, the height of a chip, fully rounded, 10 px side padding, a 1 px border a shade darker than the band's tint, text 12 / 16 Medium with tabular figures; the "i" of the button form is 12 px and a little quieter.

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • Size. The board: 32 px, 14 px Semibold, no border. Since 2026-10-07 the score follows Apiko's design concept: 24 px, 12 px Medium, a hairline border in its band's tone. Its icon keeps the band's shape, and its number keeps the band's text color rather than the concept's paler one, so it still passes 4.5:1.
  • Breakdown content. The board: a 320 px panel with rows "Start 100", the deductions that fired, and "Score". The app: 384 px, always all five components with the fact under each, and a separate block of the stall rules that fired.
  • Shadow. The board gives the panel the popover shadow; the app gives it the card shadow.

Tabs

The tab bars of the Opportunity page, the Registers and the Morning Meeting Board, and the one-of-several filters (the Accounts Overview's type, a Contractor's Bid history outcome, the Activity card's type), all in one look. Each bar keeps its own behaviour: an Opportunity or Register tab is its own URL, a board view is a ?view in the URL, and an Account type is a filter button, so a tab can be linked to and the back button works.

Source: web/shared/components/TabBar/ (TabBar.tsx, the track, a nav or a role="group"; TabBar.styles.ts with tabClassName; index.ts). A route tab is web/packages/crm/src/components/TabLink.tsx, a NavLink; the bars are pages/opportunity-detail/OpportunityTabs.tsx, pages/registers/RegisterTabs.tsx, the views in pages/MorningMeeting.tsx, and the filters in pages/AccountsOverview.tsx, pages/account-detail/contractor/BidHistoryCard.tsx and pages/opportunity-detail/ActivityCard.tsx.

States

StateLook
ActiveRaised on white with the card shadow, gray-900 text, Medium; in forced colors (Windows high contrast), where fills and shadows are dropped, also a 2 px outline
Inactivegray-500 text, Medium, on the gray-100 track
Hover (Contractors in the sample)gray-700 text, a step lighter than the open tab's

Tab sets

The set depends on the deal. Contractors exists only on Land & Build, once a buyer is Agreed or the Contractor stage is reached; on every other deal its URL lands on Overview. Activity & Risk is on every deal, and a closed deal adds Closed.

Land & Build with an agreed buyer: six tabs
Any other deal: five tabs

Size: the track is gray-100 with 4 px padding, a 12 px radius and 4 px between tabs; a tab is 32 px high with 12 px side padding, an 8 px radius and text 14 / 20 Medium. The Opportunity page's bar spans its row and, from 1024 px on, spreads its tabs; the others size to their tabs. In the app the tabs are links (filter buttons on Accounts); here they are buttons, so the samples switch in place.

Every tab has the same weight, so no tab moves when the active one changes. The hidden Semibold copy of each label that TabLink held for that (the design review's fix of 2026-10-07) went with the concept's tabs.

Compared with the design board

The "Components: actions and inputs" board of the Design Foundations canvas. This section shows the app's version.

  • Inactive tab. The board colors it gray-700; the code uses gray-500, darker only on hover.
  • The concept's tabs. The board: underlined tabs, the active one blue and Semibold, on a white strip with rounded top corners. Since 2026-10-07 the tabs follow Apiko's design concept: a gray track with the active tab raised on white, one weight for every tab, and the Opportunity page's bar on its own row under the header card.

Form messages

What a form says back: one error or success line for the whole form, a live checklist under a new password, and the success mark of a finished step.

Source: web/packages/crm/src/components/form.tsx (FormError, FormSuccess, PasswordRules, SuccessMark).

Error and success

A tinted bar with the status icon. An error is announced at once (role="alert"), a success politely (role="status"). With no message, nothing renders.

Wrong email or password.

This reset link is invalid or has expired.

Profile saved.

We sent another link.

Password rules

A rule turns green with a check once it holds, and shows a dash until then.

  • 8 to 72 characters
  • Both passwords match
  • 8 to 72 characters
  • Both passwords match
  • 8 to 72 characters
  • Both passwords match

Success mark

A 48 px round check above the title of a completed step, such as "Check your email".

Sizes

Message bar: 12 px vertical and 14 px side padding, 8 px radius, 10 px between the 18 px icon and the text, text 14 / 20. Rule: 16 px icon, 8 px gap, text 14 / 20.

Card

A white panel with a title bar. Pages group their fields and tables in cards.

Source: web/shared/components/Card.tsx; a label over a value is Fact in web/packages/crm/src/pages/lead-detail/Fact.tsx.

Variations

Title only

Site details

Region
Berkshire
Units
60
Deal type
Land & Build
Owner
Liam Carter
With actions at the right of the title bar

Activity timeline

No activity yet.

With a table that runs edge to edge

Contacts

ContactJob titlePhone
Alex ReidLand Director01632 960 123

Fact

A label over a value, laid out in a grid inside a card. The value is Medium. In a record's header (Lead, Opportunity, Account, Contact) the facts sit in one row with no rule above them and a thin rule between them, as in the design concept.

Region
Berkshire
Units
60
Deal type
Land & Build
Owner
Liam Carter
In a header
Deal owner
Liam Carter
Landowner
R. & J. Whitmore
Region
Berkshire
Probability
60%
Fee
£48,000
Days in stage
48

Properties

PropertyValuesWhat it does
titletextThe card's heading
actionsany contentSits at the right of the title bar: usually one secondary button
childrenany contentThe body

Sizes: 12 px radius, 1 px gray-200 border, the card shadow; title bar 56 px high with 20 px side padding and a line under it; title 16 / 24 Medium; body padding 20 px.

Compared with the design board

The "Type, spacing and shape" board of the Design Foundations canvas. This section shows the app's version.

  • Header facts. The boards put a line above a record's facts and set each value in Regular. Since 2026-10-07 they follow Apiko's design concept: no line above, a thin rule between the facts, values in Medium.
  • Padding. The board: 16 px, 24 px for large cards. In the code the card has 20 px.

Stat tile

A total above an overview table: an upper-case label and a large figure.

Source: Tile in web/packages/crm/src/pages/overview-parts.tsx.

Variations

Total sites
6
Active
5
Not exchanged, completed or lost
Weighted pipeline fee
£412,500
Instructed
–
VariationWhen
FigureThe usual tile: a count
Figure with a noteWhen the label alone does not say what is counted
MoneyThe Weighted Pipeline Fee on the Opportunities Overview
DashWhile the figure is loading

Properties

PropertyValuesWhat it does
labeltextUpper-case label
valueany content, or nothingThe figure; nothing shows a gray dash
notetextA small gray line under the figure

Sizes: 20 px padding, 12 px radius, the card shadow; label 13 / 20 Regular in sentence case; figure 26 / 32 Medium with tabular figures. Tiles sit in a grid with 16 px gaps: two across, four on a wide screen.

List table

The table of an overview page and the smaller tables inside cards. A record's name is always the link to its one record.

Source: web/packages/crm/src/pages/LeadsOverview.tsx and the other overviews; cell classes in pages/overview-parts.tsx; the table inside a card is RecordTable in pages/account-detail/table.tsx.

Overview table

The Stalled badge sits under the name, where it has room to name the rule. The score is a button that opens its breakdown. Numbers are right-aligned with tabular figures. On a narrow screen the table scrolls sideways inside its frame.

SiteUnitsStageLast contactOwnerHealthNext action
Hatch Farm
Hatch Farm, Sindlesham RG41
60New LeadNot contactedLiam Carter—
Oak Lane Paddock
Oak Lane, Winnersh
Stalled: no contact 16 days
18Contacted16 daysLiam Carter
Call the landowner back
Due Fri 2 Oct
Mill Field
Mill Lane, Arborfield
42QualifiedTodayUnassigned
Request appraisal
Due Mon 12 Oct
Showing 1–3 of 6 sites

States

One row across the whole table says what is happening.

Loading
SiteUnitsStageLast contactOwnerHealthNext action
Loading leads…
Nothing matches the filters: with a way out
SiteUnitsStageLast contactOwnerHealthNext action

No leads match these filters.

Nothing at all
SiteUnitsStageLast contactOwnerHealthNext action

There are no leads yet.

Table inside a card

The same head and cells, without its own frame: the card is the frame. The Legal tasks table on the Legals tab.

Legal progress

Legal tasks
TaskStatusResponsibilityPassed toDays sinceDeadline
Red Book valuationCriticalOldest outstanding
In progressRPValuer · 14 Sep2130 Sep · 5 days late
Land contract draftCritical
Waiting on other partySeller’s solicitorSeller’s solicitor · 28 Sep719 Oct
SearchesNot startedSeller’s solicitor———
Board approvalsCompleteRPRP · 7 Sep2821 Sep

"Critical" is a neutral tag; "Oldest outstanding" is a warn pill; an overdue deadline is crit-text with the number of days late.

Anatomy

PartLook
FrameWhite, 12 px radius, 1 px gray-200 border, the card shadow
Headgray-50 fill; 13 / 20 Regular gray-500 labels in sentence case; 12 px vertical and 16 px side padding
Row1 px gray-200 line above it; cells with 8 px vertical and 16 px side padding, text 14 / 20
NameA link: primary-600, Semibold; a 12 px gray second line for the address
FooterThe pager: see Pager

Compared with the design board

The "Components: data display" board of the Design Foundations canvas. This section shows the app's version.

  • Owner column. The board puts a function chip in the table. The app writes the deal owner's name, or "Unassigned".
  • Row height. The board: 56 px. The code sets no row height: the content and the cell padding decide it.
  • Legal task status. The board: a colored icon and a label ("Completed"). The app: plain text ("Complete"), and the critical-path mark is a "Critical" tag instead of a "Critical path" chip.

Pager

The footer of a paged table: how many rows are shown out of how many, and Previous / Next.

Source: web/shared/components/Pager.tsx.

States

Where the page number lives is the page's business (the URL, on every overview); the pager only shows it and asks for another.

First page
Showing 1–50 of 112 sites
A middle page
Showing 51–100 of 112 sites
Last page
Showing 101–112 of 112 sites
No rows
No sites
Loading: no text, both buttons disabled

Properties

PropertyValuesWhat it does
pagenumber, from 1The page shown
pageSizenumberRows per page: 50 on the overviews
totalnumberAll rows that match
nountextWhat the rows are: "sites", "opportunities", "accounts", "contacts"
isLoadingbooleanEmpties the text and disables both buttons
onPagefunctionCalled with the page to go to

Size: 56 px high, 16 px side padding, a 1 px gray-200 line above it, text 14 px gray-500; two secondary buttons 8 px apart. A change of the text is announced politely.

Stage bar

Where a Site is along its stages: a segment per stage, its name under it, after Apiko's design concept.

Source: web/packages/crm/src/pages/lead-detail/StageBar.tsx; the stage counts are in opportunity-detail/BuyersTab.tsx and ContractorsTab.tsx.

Positions

Done stages are blue, the current one is half blue on a light blue with its name in blue, upcoming ones are gray.

At the first stage
  1. New Lead (current)
  2. Contacted
  3. Qualified
  4. Appraisal Requested
  5. Appraised
  6. Fee Discussion
  7. Instructed
Part way
  1. New Lead (done)
  2. Contacted (done)
  3. Qualified (done)
  4. Appraisal Requested (done)
  5. Appraised (done)
  6. Fee Discussion (current)
  7. Instructed
Past the bar: a Site that has moved beyond these stages shows them all done
  1. New Lead (done)
  2. Contacted (done)
  3. Qualified (done)
  4. Appraisal Requested (done)
  5. Appraised (done)
  6. Fee Discussion (done)
  7. Instructed (done)
StepSegmentLabel
Doneprimary-600gray-900
CurrentHalf primary-600 on primary-100primary-600, Medium
Upcominggray-200gray-500

Stage counts

On the Buyers and Contractors tabs many records move through the stages at once, so the bar becomes a row of columns: how many are at each stage, each column as tall as its share of the busiest stage, after Apiko's design concept. A stage that holds any is blue; an empty one keeps a gray baseline.

Buyers tab

Buyer stage

Owner:Partnerships
  1. 2Coverage
  2. 1Interested
  3. 1Offer received
  4. 0Agreed

1 withdrawn

Contractors tab

Contractor stage

Owner:Construction
  1. 3Invited
  2. 0Tender received
  3. 0Shortlisted
  4. 0Appointed

Properties

PropertyValuesWhat it does
stageslist of stages, in bar orderComes from the API's stage table, not from the component
currenta stageA stage that is not in the list marks every step done
labeltext; default "Lead stage"The bar's accessible name

Sizes: 6 px segments, fully rounded, 6 px apart; 8 px between segment and label; label 12 / 16, left-aligned; the steps share the width equally. Screen readers hear each stage with "(done)" or "(current)".

Compared with the design board

The "Components: data display" board of the Design Foundations canvas. This section shows the app's version.

  • The concept's segments. The board draws a dot per stage joined by a line, the current one a white circle with a 3 px ring. Since 2026-10-07 the bar follows Apiko's design concept: a segment per stage, the current one half filled, the labels left-aligned. The concept's "Done · 41 days" and "Signed 1 Sep" under each stage need a stage history the app doesn't keep, so they aren't shown.
  • Buyer journey. The board draws seven steps (Coverage, Teaser, Pitched, Interested, Offered, Board, Agreed) as the same bar. The app has four stages plus Withdrawn, and shows them as counts.
  • The "Contractor stage not applicable" note. It is on the board; the app simply does not show the Contractors tab on such deals.

Deal clock

The three legal dates of a deal, each with a countdown: target exchange, target completion and longstop. Read only.

Source: web/packages/crm/src/pages/opportunity-detail/LegalsTab.tsx; the countdown wording is countdown in pages/lead-detail/format.ts.

The card

Deal clock

Target exchangeTarget
42 days
16 Nov 2026
Target completionTarget
70 days
14 Dec 2026
LongstopHard date
105 days
18 Jan 2027

States of a date

The countdown is worked out in the browser from today's date, in whole calendar days.

AheadTarget
42 days
16 Nov 2026
TomorrowTarget
1 day
7 Oct 2026
TodayTarget
Today
6 Oct 2026
PassedTarget
3 days ago
3 Oct 2026
No dateHard date
Not set

Key dates, on the Overview

The same three dates on the Opportunity's Overview tab (opportunity-detail/KeyDatesCard.tsx): a row per date, its countdown as a badge after it, the targets' in blue and the longstop's, a hard date, in gray, as in Apiko's design concept. The wording is the Deal clock's.

Key dates

Target exchange
23 Nov 202647 days
Target completion
21 Dec 202675 days
Longstop
25 Jan 2027110 days

Sizes

Each date is a tile with a 1 px gray-200 border, 12 px radius and 16 px padding; the label 14 / 20 gray-500 with its tag at the right (Target in blue, Hard date in the critical tone, 20 px); the countdown 24 / 32 Medium with tabular figures; the date 14 / 20 gray-500 under it. Three tiles in a row, 16 px apart; one column on a narrow screen.

Compared with the design board

The "Components: data display" board of the Design Foundations canvas. This section shows the app's version.

  • Layout. The board: rows with a large day count (32 px) at the right. Since 2026-10-07 the app follows Apiko's design concept: three tiles, each led by its countdown with the date under it, and a Target or Hard date tag. The concept's progress bar and its notes ("5 critical items open", "subject to PC") need data the app doesn't hold per date, so they aren't shown.

Action list

A deal's Next Actions: who owns each, what it is and when it is due. One deal can hold several open actions at once, across functions.

Source: web/packages/crm/src/pages/lead-detail/FollowUpTracker.tsx.

The list

Earliest due first, as the API returns them.

Follow-up tracker

  • LandChase landowner for Heads of Terms signatureOverdue 2 days
  • LegalConfirm draft contract with seller’s solicitorToday
  • PartnershipsSend teaser to two registered providersThu 8 Oct
  • Back OfficeSend the first approach emailDone 26 Sep

States of a row

StateMarkTitleDue
OverdueBlue ringNormalcrit-text, Medium: "Overdue 2 days"
Due todayBlue ringNormalgray-500: "Today"
Due laterBlue ringNormalgray-500: the day and date, "Thu 8 Oct"
DoneGreen checkgray-500good-text, Medium: "Done 26 Sep"

An open action also has a button to complete it, at the right of its row; the admin, who is read-only, sees a lock line instead. An empty list reads "No actions yet."

Sizes

A row is a grid: 28 px for the mark, 130 px for the function chip, the title, then the due state; 12 px gaps and padding, a 1 px gray-100 line between rows, text 14 / 20. Since 2026-10-07 the open mark is a primary-600 ring and the due states are Medium, after Apiko's design concept; the concept's dashed line under the mark isn't drawn.

App shell

The frame of every signed-in page, after Apiko's design concept: a light gray sidebar with the brand, the signed-in user and the menu in groups, beside a white page. The sidebar folds to a rail of icons and back, and remembers the choice.

Source: web/shared/components/Sidebar/ and web/shared/components/AppLayout/ (each a folder: the component, its .styles.ts and index.ts, and the Sidebar's useSidebar hook for the fold; the AppLayout has no logic, so no hook); in the app, components/AppShell.tsx (the menu, the user, the router links) and UserAvatar.tsx.

Sidebar

Open: 240 px on gray-50, a line at its right. The brand and the fold button, the user card, then the menu under three captions: Workspace, Pipeline, Directory (an admin also sees Admin, with Create staff user). Folded: a 64 px rail of icons, a rule for each caption, the avatar alone; each icon shows its name on hover and on keyboard focus.

Open and folded
Menu itemLook
Defaultgray-600 text, Regular
Hover (Opportunities in the sample)gray-100 fill, gray-900 text
Current page (Leads in the sample)gray-200 fill, gray-900 text, Medium
Folded40 px square, the icon alone; a gray-900 tooltip with the name

A menu item is 40 px high with an 8 px radius, 12 px side padding, an 18 px icon and 12 px between icon and label; items are 2 px apart, groups 16 px. The fold button is labeled "Collapse sidebar" or "Expand sidebar" and tells screen readers whether the sidebar is open; the choice is kept in localStorage, and a browser that blocks storage still folds, for that page only. The user card is one link to My account; its label starts with the visible name.

Breadcrumbs

On a record page, above its header: the Overview as a link, a chevron, the record in ink, at 14 px. The Lead, Opportunity, Account and Contact pages each draw their own.

Record page

Avatar

The user's initials in the function color of their role. MD and the admin use the Legal slate. Until the account has loaded, nothing is drawn.

By role
Back Office
Land
Partnerships
Construction
MD, admin
Sizes
32 px rounded square, in the sidebar
lg: 48 px circle, on My account

Page area

The page sits on white with 24 px padding; its blocks are 16 px apart. The shell sets no maximum width.

Compared with the design board

The Design Foundations canvas's page boards. This section shows the app's version.

  • The concept's shell. Since 2026-10-07 the shell follows Apiko's design concept, not the boards: a gray sidebar with the user at its top and grouped items (the boards: a white sidebar, the user at the right of a top bar), the current page on gray (the boards: primary-100), a white page (the boards: gray-50), and a sidebar that folds.
  • Not taken from the concept. Its top bar (breadcrumbs, page actions, search, the "Updated" time, team avatars, Share and Inbox) and its item counts and Activity Feed, Regional, Capacity and Settings items: the bar would need the pages to hand it their content, a change of how the app is built, and the rest needs data or features the app doesn't have. The breadcrumbs stay above the page header.
  • Content width. The board: fluid, at most 1440 px. The shell sets no width limit.