Skip to content
Browse documentation
Decided

Visual design system

Colour tokens, typography, spacing, radii, components and motion shared by both apps: the circle-first look decided on 2026-10-29 (A101).

Updated: 29 October 2026

Both apps implement one token set. The normative extract lives in mobile/design/tokens.json and is mirrored in ui/theme/Color.kt, Type.kt, Spacing.kt, Shapes.kt, Sizes.kt and Theme/IncicoColors.swift, IncicoTypography.swift, IncicoTokens.swift. A golden test on each platform pins the hex values to the JSON, and tools/tokens-check.mjs fails on drift.

The look is circle-first (A101), chosen by the product owner on 2026-10-29 over five rounds of mocks (mobile/design/redesign/). It takes Saybook’s calm shell (warm neutrals, one accent, no decoration) and lets the circles carry the colour: a circle’s name appears in its colour wherever a circle matters, and nothing else on the screen competes with it. It supersedes the v2 look of A100 (palette, system font, lit cards); the logo and the circle palette are unchanged.

Rules the look follows

Taken from Saybook and Saymail, and binding on every screen:

  1. A control carries its verb in words beside the icon. “Find”, “Add”, “Answer”, “Review and sync”, “Accept into Family”. A bare icon is allowed only for the bell, the gear, the overflow and back.
  2. Dimmed, not gone, and it says why. An action that cannot run now stays in place at 45 % opacity with a one-line reason: “Edit · offline”, “Send reminder · from 3 November”, “Invited”.
  3. Every card’s second line is its status. “8 people”, “In sync · 10 min ago”, “3 waiting · yesterday”, “Follows the system”.
  4. The next step comes first and is the primary button. A request to answer or a sync to review leads the screen, with the one action it needs.
  5. No blank screens. Every empty list has one sentence and one action; a filter that hides everything says so and offers to widen it (Saymail).
  6. Circles are named, not only coloured. Rows and details show the circle’s name in its colour; a dot alone is never the only carrier.

Colour

Warm neutrals and a single cyan accent, derived from the logo’s cyan #009fc5; the logo keeps its own navy and cyan and is never recoloured (see Brand and assets). primary fills buttons and the active state; primaryText is the accent used as text and icons on surface (links, text buttons, the active tab icon), because primary itself is too light for small text in dark mode. tint is the accent at low strength: selected rows, the active tab pill, changed rows, info boxes. accent is gold, for the favourite star only.

Light

TokenHexUse
bg#fafaf8screen background
surface#ffffffheaders, tab bar, sheets
card#fffffflist boxes and cards
fill#f0efecsegmented-control track, skeletons, icon tiles, neutral avatars
line#e5e3dehairlines and card borders
ink#1c1b19primary text; the selected filter chip
ink2#3f3d39secondary text on controls, circle labels
muted#6b6862detail lines, section labels (4.5:1 on card)
primary#007a99primary buttons, switches, radios, checkboxes
primaryText#006a85links, text buttons, active tab icon
onPrimary#fffffftext on primary
tint#e3f3f7selection, changed rows, active tab pill
accent#9a7a22favourite star
success#2f7a4bverified badge
warning#8a5a00“not verified”, “only you”, relation warnings
danger#b23b3bdelete, cancel invitation, sync errors, the bell count
inputBg#fafaf8text fields at rest (focused fields switch to surface with a 1.5 dp primary border)

Dark

TokenHex
bg#161616
surface#212121
card#212121
fill#2f2f2f
line#3b3b3b
ink#ececea
ink2#c3c1bc
muted#9a978f
primary#18a6c8
primaryText#7fd8ee
onPrimary#06161b (dark text on the cyan)
tint#16343d
accent#d8b14d
success#6cc48e
warning#e9bb62
danger#ea7a70
inputBg#161616

The scrim behind sheets is rgba(20,20,18,.42) in light and rgba(0,0,0,.6) in dark. The default appearance follows the system (A101); see Settings.

Circle colours

Circles are user-coloured from a fixed palette of ten: #cd416c pink (Family default), #009fc5 cyan (Friends default), #f4a958 orange (Work default), #a2db7a green, #7c5cff violet, #e8618c, #2a9d8f, #e76f51, #8ab17d, #6d597a. The first three come from the v1 logo. Several are too light for white text, so a circle colour is never a background for text: it is a dot, the colour of a circle’s name, or a tint.

Circle labels and chips

  • Circle label (A107): an 8 dp dot in the circle’s colour followed by the circle’s name in ink2, 11.5 sp semibold. It is the only circle marker on contact rows, service rows and detail rows, replacing the v2 dot and the trailing row of visibility dots. A detail shared with no circle shows “Only you” with an empty ring, in warning. A detail shared with part of a circle’s view says so after the label: “Friends (city only)”, “Friends (no year)”.
  • Filter chip (Contacts, View as): 32 dp tall pill, surface with a line border; selected is filled with ink and its text in bg. A count follows the name in a lighter weight (“Family 8”).
  • Selectable circle chip (the circle sheet, Put … in): 34 dp pill with dot and name; selected is the circle colour at 22 % over surface, a 1.5 dp border in the circle colour and a check, the text stays ink (white on the light circle colours fails contrast).
  • Who-sees-what cell: a 14 dp dot in the circle colour when the circle sees the detail, a half-filled dot when it sees part of it, an empty ring in line when not.

Typography

Manrope, bundled with both apps (variable font, SIL Open Font License, weights 400 to 800), replaces the system font. Sizes are sp and scale with the OS; the floor is 11 sp.

StyleSpec
screenTitle26 sp, 700, tracking −0.02 em
sheetTitle18 sp, 700
sectionLabel12 sp, 600, muted, sentence case (no uppercase)
rowName15 sp, 600
rowDetail13 sp, 500
body15 sp, 500, line height 22 sp
circleLabel11.5 sp, 600
chip12.5 sp, 600
button13.5 sp, 700
headerAction13 sp, 600
tabLabel11.5 sp, 600

Phone numbers and dates use the same face as text.

Spacing and shape

Spacing scale xs 4, sm 8, md 12, lg 16, xl 20, xxl 24; screen gutter 18 dp for titles and section labels, 14 dp for boxes.

RadiusdpUsed by
control10buttons, header actions, segmented segments
card12list boxes, cards, text fields, the segmented track
large16profile cards in the switcher, empty-state icon tile (20)
sheetTop26bottom sheets
pillcircularchips, avatars, dots, the tab pill

Depth is flat. Cards and list boxes are card with a 1 dp line border and no shadow, gradient or highlight; rows inside a box are separated by 1 dp line hairlines and run edge to edge. Only sheets (and the tablet’s side sheet) cast a shadow. The lit edge, radial gradient and shadows of A100 are gone.

Bottom tab bar 64 dp; header 46 dp plus status inset, with no title in it (the screen title sits under the header in screenTitle); rows 58 to 62 dp (they grow with the text); row and list avatars 40 dp; circle dots 8 dp in labels and filter chips, 10 dp in the circleDot token for the sheet rows. avatar and circleDot stay in tokens.json (A97).

Components

ComponentNotes
TopBarLeft: on a tab root the active profile’s avatar and first name with a chevron (opens the profile switcher); on a pushed screen a back chevron with the parent’s name (”‹ Contacts”) in primaryText. Right: header actions. No title in the bar.
HeaderActionIcon plus word in a 1 dp line outlined pill on surface (“Add”, “Connect”, “Edit”); the primary one (“Find”) is filled primary. The bell and the gear may be icon-only. A count sits on the bell as a danger badge.
ScreenTitlescreenTitle under the top bar, 18 dp gutter.
BottomTabBarFour items on surface, hairline top border, no shadow. Icon over label; the active item has a 56 × 30 dp pill in tint behind its icon, icon in primaryText, label in ink; inactive items muted.
SectionLabelsectionLabel on the left, an optional count or text action on the right (“A–Z”, “Mark all read”, “Select none”).
ListBoxcard, 12 dp radius, 1 dp line border, rows separated by hairlines.
ContactRow40 dp avatar (photo or initials on the deterministic colour), name, one detail line, trailing circle label; a favourite has a small accent star after the name. Swipe: favourite, move, remove.
ChangedRowA row in “Changed this week” and a changed line on the contact card: 3 dp primary bar on the leading edge, tint background, the detail line in primaryText saying what changed (“New mobile number · today”).
DetailRowType label (12 sp muted), value, circle labels underneath; tapping the row opens that detail’s sheet (A105).
PrimaryButtonprimary fill, onPrimary label, 10 dp radius, no shadow.
SecondaryButtonsurface fill, 1 dp line border, ink2 label.
TextButtonprimaryText, 13 sp bold, optional leading icon (“Preview”, “Move”, “Change”).
Switch, Radio, CheckboxOff: fill track / muted ring; on: primary.
SegmentedControlfill track, 12 dp radius; the selected segment surface with a faint shadow.
FilterChip / CircleChip / CircleLabelSee above.
SheetBottom sheet, 26 dp top corners, grabber, scrim, drag to dismiss.
Bannerfill box with a leading icon and one sentence (offline).
InfoBox / WarnBox / ErrorBoxtint box with a text action; warning and error boxes are warning or danger at 13 % over surface with a 40 % border, text in ink.
EmptyStateA 64 dp rounded tile in tint with a 32 dp icon, one sentence in rowName at 17 sp, one line of muted explanation, one primary action and at most one text action.
Skeletonfill bars in place of rows while the first read is in flight.
ProgressScreenTitle, determinate bar, step text, Cancel.
Toastink box with bg text and an underlined action (“Undo”), above the tab bar.

There is no FAB: “Find”, “Add” and “Connect” are header actions with words.

Motion

Sheets slide up with scrim; pushes slide horizontally; tab content cross-fades in 150 ms; durations 150 to 300 ms ease-out; the active tab pill fades in. Reduce motion drops all of it. A changed row on a contact card (A94) fades the tint in and out over 300 ms beside the static bar; with reduce motion only the bar shows, for the same three seconds.

Iconography

Material Symbols Rounded (Android) and SF Symbols (iOS) at a 1.8 dp stroke-like weight, outlined everywhere; the active tab differs by its pill, not by a filled glyph. Required glyphs: contacts, circles (three overlapping rings), services (sync arrows), profile, search, add, person-add (Find), edit, delete, favourite (star), block, verified (check badge), unverified (dashed badge), phone, message, mail, route (map pin), home, cake, camera, eye (Preview), refresh, back, more, settings, notifications, link, cloud-off, warning, lock, send.

The original v1 mark and outlined wordmark, unchanged (sources in mobile/design/brand/). The sign-in screen shows the app icon (incico-appicon.svg), the word Incico in screenTitle weight 800 and the tagline; the welcome step shows the bare mark. The splash screen is incico-splash.svg on white, or the same artwork on bg in dark mode with the wordmark in white. See Brand and assets.