<!-- https://getkonjo.com/design/patterns/detail · source: docs/design/patterns/detail.md -->

# The detail screen

One record, in full. The second most common shape in the app, after the list.

> Part of the [Konjo design language](../konjo-design-language.md). The laws in the spine
> apply here unless this chapter contradicts them.

A detail screen is what a list row promised. Someone tapped a name, an event, a session, a
request — they arrived with a specific question and the screen's job is to answer it before
they scroll.

There are twelve-plus of these in `src/`, and until now they had two sentences of guidance
between them.

## The formula

```
(insets.top + spacing.tight)
‹ Back                                  44pt target, no title in the bar
                                     ↕16
BROWN BELT · 3RD KYU                      eyebrow · text.secondary
Ana Torres                                display 34/800
Joined Mar 2024 · 47 classes              caption 13 · text.secondary
                                     ↕20
[ Log attendance ]                        one filled primary, hugs left
                                     ↕24
DETAILS                                   eyebrow, on the page
┌──────────────────────────────────────┐
│ Next test          Sep 14        › │   white card; 2 rows, so no hairlines —
│                                      │   12pt between them. Six or more rows
│ Waiver             Signed          │   get hairlines, inset to the text edge.
└──────────────────────────────────────┘
                                     ↕24
RECENT                                    eyebrow
┌──────────────────────────────────────┐
│ Tuesday class · 6:30pm             │
└──────────────────────────────────────┘
```

**Eyebrow → title → meta → primary action → content.** The identifier or category is an
eyebrow *above* the title, never a chip beside it — Linear's `ENG-2486` pattern. A chip beside
a title competes with it; an eyebrow above frames it.

## The title is the subject, not the screen

"Ana Torres", not "Student detail". "Tuesday 6:30pm class", not "Class". The person already
knows what kind of thing they tapped; what they need is *which one*. A detail screen whose
title names the screen type has wasted its loudest element.

The title is `display`, and like every screen title it is **chrome: it does not spend the loud
block budget**. What it does mean is that nothing else on the screen needs to be loud. **A
detail screen does not get a hero block** — the record is the content, and putting an inverted
slab above it means scrolling past the answer to reach the answer. So a detail screen normally
spends **zero** of its loud budget, which is legal and correct.

## The meta line

One line, `caption`, `text.secondary`, facts separated by `·`. It carries what someone would
say out loud after the name: how long, how many, when. Not every field the record has — that
is what the sections are for.

If the meta line needs two lines, it is not a meta line, it is a section.

## One primary action, above the fold

The single thing this person most likely came to do, as the one filled primary. Everything
else — edit, share, remove, message — goes into the header's overflow or lives at the bottom
of the relevant section as a text link.

**Never a row of equal-weight buttons under the title.** Three outlined buttons side by side
is the shape of a screen whose designer could not decide, and the person reading it now has to.

If there is genuinely no primary action, there is no button. A read-only record is a legitimate
screen.

## Content sections

- **`eyebrow` on the page, then a white card.** Group with space and a label, not a bordered
  box inside a bordered box.
- **Rows inside the card**, hairlines inset to the text edge. Five or fewer rows: no hairlines
  at all, just 12pt between them.
- **Row height is a floor, never a fixed height.** A one-line label/value row is `minHeight` 44;
  a two-line row (title over caption) is `minHeight` 64, with the caption **4pt** under its
  title. This said 2pt, which is a layout gap written with the one value
  [L2](../konjo-design-language.md) reserves for optical correction — 4 is the smallest gap the
  ramp actually offers.
  Fixed heights clip at large text sizes — see
  [accessibility](../foundations/accessibility.md).
- **Label left, value right, both in `text.primary`.** They are equals: the label says what
  you are reading and the value says it. Separate them by weight, not colour — label
  `rowTitle`, value `body`. Do not grey the label to make the value pop; a row whose metadata
  outweighs its subject is anti-pattern territory.
- **Order sections by what gets looked at**, not by how the data is modelled. The database's
  column order is never the answer.
- **A section with nothing in it does not render.** No "None", no empty card. Its absence is
  the information. The exception is a section whose emptiness is actionable — a missing waiver
  is worth a row saying so.

## Loading a detail screen

The person tapped a row they could already see, so **you already know the title, and often the
subtitle**. Render them immediately from the list's data and load only the sections. A spinner
covering a name the app already had in memory is a self-inflicted wound.

- Header and title: instant, from the row.
- Sections: skeleton at the final height, or nothing under a second.
- A section that fails loads its own inline error and retry. **One failed section never takes
  down the screen** — see [states](states.md).

**A state's action inherits the weight of what it replaced.** This is the rule that resolves
the budget: a *section* that fails or is empty sits inside a screen whose primary is still on
screen, so its retry is a **bordered secondary** — the screen has already spent its one filled
primary. A *whole screen* that fails renders nothing else, so its retry **is** the filled
primary. Same anatomy, different weight, and the budget holds either way.

The same logic settles a section-scoped empty state: if the action it would offer is **already
on screen**, it offers none — text only, in flow — because the same destination twice in one
viewport is worse than a missing button. An empty "Recent" section under a live "Log
attendance" button says what goes there and stops.

## Destructive actions

Never in the header, never adjacent to the primary action, never a red button floating above
the fold. A destructive action lives at the bottom of the screen, past the content, as a text
link in the screen's red role — and it arms on first tap, fires on second. See
[serious actions](destructive.md).

## Never

- A title that names the screen type instead of the record.
- A hero block.
- Three buttons of equal weight under the title.
- Every field the record has, in schema order.
- An empty section rendered as "None".
- A full-screen spinner over data you already had.
- Delete sitting next to the primary action.
