Entrixy — design guide

A living document. It records the calibrations, the rhythm and the agreements about the interface, and is updated whenever something is settled.

Contents
  1. Icons — sizes and calibration
  2. Typography
  3. Rhythm and spacing
  4. Colours and levels
  5. Controls
  6. Animations
  7. Text rules
  8. UX patterns
  9. What not to touch

1. Icons — sizes and calibration

The problem

Material icons from androidx.compose.material.icons.outlined.* share a nominal size — a 24×24 grid — but the drawn part differs from one to the next. Give them all a plain Modifier.size(24.dp)and some will look big and heavy (Bolt, Delete) while others look tiny (Contacts, VpnKey). They need calibrating by hand.

The calibration method

  1. Take a screenshot of the screen with the icons in their 38 dp circles — our landing slot.
  2. With Python, PIL and numpy, measure the bounding box of the drawn glyph — its maximum width and height in pixels.
  3. The formula: new_size = 24 × 52 / measured_px, clipped to [18..28].
  4. Then centre it in the 38 dp slot, vertically and horizontally.

52 px is the reference painted glyph, Fingerprint; everything is measured against it as the unit.

The resulting size map

IconSizeWhy
Add, Apps, ArrowBack, Close28dpthin lines, a glyph of 36–41 px
AutoMode, Bolt, DeleteOutline, Edit, List, Photo, QrCode, Tune, DriveFileRenameOutline26dpmedium density, a glyph of about 47 px
LocationOn24dpthe map pin — do not touch, it is a reference
LocationOff26dpthe crossed-out pin — do not touch
ContentCopy, MyLocation, PeopleAlt, Wifi, ExpandMore, ExpandLess22dpwide or tall, and visually heavy
Contacts, VpnKey20dpoverloaded with internal detail
Do not touch: LocationOn (24), LocationOff (26), ExpandMore and ExpandLess (22). These are the reference icons: they were agreed pixel by pixel, and the whole calibration is measured from them.

AutoMode

Used everywhere Bolt used to be — the Automation button on a key card, the section icon in settings. Size 26 dp, the same as Bolt.

Where the table lives in the code

IconsDebugSheet.kt — the map iconSizeByName. The debug screen is opened from SettingsScreen with the "UI icons (debug)" button: a FlowRow of calibrated icons on top and a list of names, sizes and contexts below.

2. Typography

We use the standard Material 3 roles, deliberately limited to four:

RoleSize / line heightWhere it is used
titleLarge SemiBold22sp/28spThe bottom sheet title ("For the guest · <object>")
titleMedium SemiBold16sp/24spRule headings ("Geolocation", "Wi-Fi", "Interval", "Activation") and FieldSection labels ("Coordinates", "Radius", "Mode", "Saved networks")
bodyMedium14sp/20spRadio button captions, the text inside pill buttons, segment labels
bodySmall muted12sp/16spStatuses and hints ("36540 m to the point", "74 m · maximum, the guest may lower it", hint texts). Colour: onSurfaceVariant
Rule: do not mix weights within a role. If something is titleMedium SemiBold, it is SemiBold everywhere — not regular here and Semi there.

3. Rhythm and spacing

The scale

We agreed to work in multiples of 2 / 4 / 8 / 10 / 14 / 16 / 20. No feast or famine: identical elements get identical spacing.

Between cards in the main scroll

Inside a card (RuleCard)

Inside a FieldSection (label plus content)

Pill buttons and statuses

Sliders are a special case

A Material 3 slider occupies about 48 dp of layout because of the minimum touch target, of which the visible track is only about 4 dp in the middle, with roughly 22 dp of empty touch area above and below. Hence:

The result: the distance from the last visual element — pill, radio button or slider thumb — to the card's bottom edge is about the same on every card.

4. Colours and levels

Three levels of trust in a guest

Every rule — Geolocation, Wi-Fi, Time, Authorisation, Confirmation — and the Activation block takes one of three levels. The card's icon circle takes the matching colour when the switch is on.

KeyLabelColourDescription
own The guest decides #3BA55D (green) The guest picks the setting themselves
suggest Preset #E5B409 (yellow) The value is set, and the guest may change it
force Locked #CC3322 (red) The value is set and the guest cannot change it

With the switch off the circle turns grey (#9AA0A6), whatever level is selected.

The meaning of maximum and minimum: in yellow (Preset) the radius value is a maximum and the guest may only lower it. For the number of Wi-Fi networks — the match threshold — it is the other way round: a minimum the guest may only raise. The direction of freedom inverts, and the status texts say so outright: "maximum, the guest may lower it" / "minimum, the guest may raise it".

Rules for the level descriptions

5. Controls

The coloured circle with an icon (38 dp)

A single 38 dp landing slot throughout the app:

Radio buttons

Wrap them in Box(Modifier.size(38.dp), contentAlignment=Center) { RadioButton(...) } so that the centre of the radio circle lines up with the centre of the coloured circle above. Without the wrapper the radio drifts horizontally, because RadioButton renders with padding of its own.

Between a radio button and its text: Spacer(14.dp). Between radio rows in a group: Column(spacedBy(4.dp)).

The minimum touch target is disabled: CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) — otherwise each radio row swells to 48 dp and breaks the rhythm.

Switch

A standard Material 3 switch, to the right of the card title. It turns the whole rule off, and the level colour is then not shown — the circle goes grey.

Segmented row

A custom control for switching modes inside a rule — currently only in Wi-Fi: "By a specific network" / "By the networks around". Two items in a Row, each with weight(1f):

Pill buttons

An outline, an icon and text, fully rounded. Used for secondary actions — "Use the current position", "Take a snapshot of nearby networks". When disabled, the outline and text drop to alpha=0.4.

Slider

See section 3 on offset(y=-16.dp) and sliderTrim(). A slider is always preceded by a StatusLine carrying status text (bodySmall, muted) — "74 m · maximum, the guest may lower it".

The level dropdown

Opened by tapping the card's title row — the whole Row except the switch. It appears directly under the header, across the full width of the card's content. For that:

The dropdown items breathe rather than being flat rows: a colour marker on the left, the label in bold, the description beneath in small muted type. They align vertically on the first line, the label.

6. Animations

The pulsing Activation circle

Unlike the ordinary coloured circles of the rules, Activation's header circle pulses.

Do not touch the alpha formula. It took several iterations, and any simplification — linear (1−p) or ease-out — produces a visible jerk.

7. Text rules

Punctuation

A single sentence in an interface string takes no full stop. Full stops appear only when there are two sentences or more, to separate them. This covers every description, hint, status, tooltip, heading and standalone phrase.

Whose voice we write in

Choosing between a maximum and an exact value

In slider statuses in yellow (Preset) mode we state the direction of freedom plainly:

In red (Locked): "74 m · exactly" / "5 of 7 · exactly".

The level labels are settled

"The guest decides / Preset / Locked" have stuck; we do not change them.

8. UX patterns

A switch, or a "not selected" radio option

If a rule has an off state, express it with a switch rather than a radio option like "Not required / No / Off". Otherwise the user has two ways to turn the same thing off, which is confusing.

Examples:

Coordinates and the actions on the right

In the Coordinates block the copy and delete icons sit on the right level with both lines (the label and the status). Implemented as Row { Column(weight=1f) { label; status }; CopyIcon; DeleteIcon }. The icons are centred vertically against the pair as a whole.

Showing and hiding a card's body

The body expands only when:

Animation: expandVertically(tween(220)) + fadeIn(tween(220)) on opening, shrinkVertically(tween(180)) + fadeOut(tween(120)) on closing.

When to show the "Key added" dialog

On receiving a guest key — only if the app is on screen and the key is new rather than a repeat. Otherwise we save it silently.

9. What not to touch

Related documents

Last updated: 18 April 2026 · document version 2.07