Every button style that exists in bristlenose/theme/atoms/modal.css, at
every size the codebook lens uses, plus three proposals marked
NEW. Rendered with the real classes and the real tokens.
| Class | sm | default | lg | Notes |
|---|---|---|---|---|
.bn-btnbare |
Today's Review. No background declared → UA grey. | |||
.bn-btn-secondary |
Page background + text + hairline. Browse Library uses this at lg. |
|||
.bn-btn-cancel |
Byte-identical to secondary. Two names, one appearance. | |||
.bn-btn-primary |
Accent fill. The page already spends this on Install. | |||
.bn-btn-danger |
Frustration orange. Not this — nothing is being destroyed. | |||
:disabled |
0.5 opacity on any variant. |
Five names, four appearances. cancel and
secondary declare the same two properties — worth collapsing, or worth one of
them becoming the quiet style below.
The size axis is provisional and scoped. .bn-btn-sm /
-lg live in codebook-v2.css under .v2-layout and
.section-heading, not in the atom — so they work on this lens and nowhere
else. Promoting them to the atom is a separate decision.
| Proposed | sm | In situ | Argument |
|---|---|---|---|
.bn-btn-quiet |
|
Accent text, no fill, no border until hover. The macOS plain button — what Finder and Mail use for a verb that is available but not urged. Lightest possible. | |
.bn-btn-tinted |
|
Accent text on the accent's own pale wash (--bn-colour-hover). Reads as a control without competing with Install. Closest to macOS 26's tinted style. |
|
.bn-btn-outline |
|
Accent border and text. More present than tinted; risks reading as a second primary next to Install. |
Updated 31 Aug 2026 — the base now carries a background.
.bn-btn declared no background, so every bare
.bn-btn fell through to the user agent's buttonface: the grey
in section 1 was the browser's, not ours. Seven call sites were shipping it, including
the Install / Uninstall toggle on every codebook card. The base is now the neutral
bordered button — which is what secondary and cancel already
described, so those two now restate the default rather than adding to it.
One trap came with the fix and is pinned by tests/test_button_atom.py:
giving the base a :hover makes .bn-btn:hover (0,2,0) outrank
.bn-btn-primary (0,1,0), so a filled variant washes out to the neutral tint
under the pointer unless it restates its fill. Visible only while hovering, which is
exactly when nobody is reading the CSS.
The competition to judge against: this page already has
Browse Library (secondary, lg) in the zone title and
Install / Uninstall in the gutter. Review is a third verb on the
same screen, and the question is what rank it should hold among them — not whether it
looks nice alone.
Two things this catalogue does not settle. Whether the size axis is
promoted out of codebook-v2.css into the atom, and whether
cancel/secondary collapse into one name. Both are cheap once
the appearance is chosen.
Scale note: these are the web type tokens (label 13px, caption
12px). The desktop app substitutes an Apple scale in
tokens-desktop-v1.css — caption is 11px there — so every specimen renders a
shade smaller in the .app. Relative weights are unchanged.
Settled 31 Aug 2026 — D. bn-btn-primary for Install,
bn-btn-secondary for Uninstall, in both codebook lenses. Ranked by
what you came to do rather than by danger: this is a catalogue, installing is the frequent
intent, and Uninstall is already gated by a sheet that measures what it costs. The
destructive reading still arrives on hover via
.picker-card-toggle-uninstall.
The question. Today they are byte-identical at rest;
.picker-card-toggle-uninstall styles only :hover and
:focus-visible (red text, red border). So on a nine-card grid the
destructive/constructive distinction arrives at the moment of commitment, not
the moment of scanning. One of these adds a codebook; the other destroys 28
tags across 4 quotes.
The counter-argument, which is real. Uninstall is already gated by a
confirmation sheet that measures what will be lost. Painting the card button red warns
about a thing that cannot happen from this click — and the house rule is that
.destructive marks what was not chosen, in the confirm, rather than
decorating every route toward it. Option A may simply be right.
What to weigh. A trusts the confirm sheet and keeps the grid calm — nine cards, no red. B is the smallest change that makes the two scannable and reads as a label, not an alarm. C puts a red rectangle on the grid for every installed codebook, which is a lot of warning for a gated action. D is the interesting one: it ranks by what you came to do rather than by danger, and makes the catalogue read as a place you install from. E is here to be rejected.
All five use existing tokens — --bn-colour-negative,
--bn-colour-accent, --bn-colour-muted. Nothing here needs a new
value.
It is not styled. The markup is
class="bn-btn bn-btn-sm"— no colour variant. The base.bn-btnsets font, padding, radius and border but declares no background, so the button falls through to the browser's ownbuttonface: measuredrgb(239,239,239)withcolor: rgb(0,0,0)— pure black, not--bn-colour-text.So "drab and grey, a bit dead" is the accurate reading of an unstyled control. It also explains "low contrast yet too noticeable": the fill is a UA grey that belongs to no palette, and it survives a palette switch unchanged — the one thing on the page that does.