# DuckSuite Design Language — Interface

The companion to `DuckSuite_Design_Language.md`, which covers icons. That document defines how a
DuckSuite app looks **on a home screen**. This one defines how it looks **once opened**.

They share principles, because the icon language's rules are good interface rules:

> One focal point. Reads instantly. Readable at small sizes. No unnecessary decorations.
> Minimal clutter. If it needs explanation, it is too complicated.

This document governs **data surfaces** — lists, rows, tables, detail panes, anything rendering
records from a service. It does not govern the chrome (dock, panels, workspace shell), which is
settled and stays as it is.

## Why this exists

Every data view in Officer was built on its own, and it shows. A survey of two apps found 11 uses of
`text-[10px]`, 7 of `text-[11px]`, 52 of `text-xs` and 3 of `text-base` — a range of 10–16px with no
rule about which meant what. Six radius values. Green used both for "this is open" and for "this is
a heading icon", so green signalled nothing.

None of that was a bad decision. It was the absence of one, thirty times over. The fix is not taste,
it is removing the choice: **the ranks below are the whole vocabulary, and the primitives in
`components/Data/` are how you spend it.**

## 1. Type — four ranks, and a floor

| Rank          | Class                     | Size | Use                                                          |
| ------------- | ------------------------- | ---- | ------------------------------------------------------------ |
| **Title**     | `text-base font-semibold` | 16px | The one thing a screen is about. One per view.               |
| **Row title** | `text-sm font-medium`     | 14px | The focal point of a row — repo name, issue title.           |
| **Body**      | `text-sm`                 | 14px | Descriptions, prose, form values, anything read as language. |
| **Meta**      | `text-xs`                 | 12px | Secondary facts: timestamps, counts, authors, hints.         |

**12px is the floor.** No `text-[10px]`, no `text-[11px]`, no arbitrary sizes. If something feels
like it needs to be smaller than meta, it is not important enough to be on the screen — cut it
instead. This single rule is most of the readability fix.

`font-medium` marks the focal point. `font-semibold` is for titles only. Never both in one row.

## 2. One focal point per row

Straight from the icon language. A row has exactly **one** element at row-title rank; everything
else is meta. A list is scanned, not read, and scanning works by finding the one bold thing per
line. Two competing emphases per row and the eye has nowhere to land — which is why the current
lists feel busy at a glance despite containing very little.

Meta facts sit on **one line**, separated by a `·` dot, in source order of importance. They do not
each get their own badge.

## 3. Colour means state, never decoration

Colour on a data surface is **information**. If it isn't telling you something you'd act on, it is
noise, and it dilutes the colour that is.

Five tones, and no others:

| Tone      | Token         | Means                                                                 |
| --------- | ------------- | --------------------------------------------------------------------- |
| `neutral` | `muted`       | No state, or a state with no valence. The default.                    |
| `success` | `success`     | Open, healthy, passing, connected, done.                              |
| `warning` | `warning`     | Needs attention, degraded, archived, stale.                           |
| `danger`  | `destructive` | Failed, rejected, closed-unresolved, error.                           |
| `info`    | `info`        | In progress, merged, informational. Distinct from success on purpose. |

Always the semantic token, never a raw Tailwind palette number. `emerald-500` and `success` may
render the same pixel today, but only one of them follows the theme, survives dark mode, and can be
re-tuned in one place. **`text-black/50` and `ring-black/5` are bugs** — they are invisible in dark
mode.

Fills are `bg-<tone>/15` with `text-<tone>`. Never a solid saturated fill on a small element.

Brand colour (`brand`, the DuckSuite forest green) is for identity and primary actions. It is never
a state. Keeping it out of the state vocabulary is what lets it read as "Officer" rather than as
"good".

## 4. One radius rule

| Shape                            | Radius         |
| -------------------------------- | -------------- |
| Containers, cards, panels        | `rounded-lg`   |
| Controls, inputs, buttons, tiles | `rounded-md`   |
| Pills, badges, avatars           | `rounded-full` |

Three values. Nothing else. A radius is not an expressive choice.

## 5. Spacing rhythm

On a 4px grid, and each step means something:

- `gap-1.5` — an icon and its own label. They are one object.
- `gap-2` — items inside one cluster.
- `gap-3` — between clusters in a row; the row's own `px-4 py-3`.
- `gap-4` / `gap-6` — between sections.

Dense list rows are `px-4 py-3`. Detail panes are `p-4` inside `gap-6` sections. Rows separate with
`divide-y`, not with margins and not with a border per row.

## 6. The three non-content states are not an afterthought

Every data surface can be loading, failed, or empty, and those are the states a new user sees first.
They get the same care as the populated view, and they come from `StateBlock` so they are identical
everywhere.

An empty state says **what would be here** and **why it isn't** — never just "No results". An error
state shows the real message, because the person reading it is usually the person who can fix it.

## 7. Numbers

Anything columnar or comparable is `tabular-nums`. Counts, sizes, durations, dates. Digits that
change width make a list twitch.

## Applying it

Use `components/Data/` primitives rather than re-implementing a row. They encode everything above,
which means adopting them is how a view complies — not a separate step afterwards.

Adoption is per-view and additive; nothing breaks by not having adopted yet. Gitea went first
because it is the first surface shown to people outside this machine.
