Level 1, and the PAIR is the control. Two halves that state one market's two sides and cannot exist apart: there is no such thing as a YES in this product without the NO beside it. 116 pairs and 232 halves on 14 painted screens, plus 44 trader picks on 10. Measured 2026-08-08 in a browser, at 390 and 1280.
The level and the reason for it stay on the atoms page. What needs the room here is that this atom has two families of face, not two faces: a SPECTATOR's pair that states a market, and a TRADER's chooser that states a choice. They never appear together, they are drawn from opposite ends of the palette, and the shelf can only show one of them.
And like chip, a single specimen misleads here: half a pair is not a small version of the control, it is the half that carries no information.
full, 64 placements, on a card and in the hero
compact, 42 placements, in a card's option row
Same two colours, 11px instead of 12, and it is a
<span> because it stands inline in a row of text. Nothing else changes,
including the height.
the outcome list, 10 placements, and here it is a CHOICE
Same class, different ground: on a multi-outcome market the pair rests on the quiet control colour and the chosen side goes solid, so the state is the whole difference between the two. It rested on a SOFT outcome tint until 2026-08-16, which meant chosen and unchosen differed only in how much of the same colour was showing, and that is the weakest signal a state can be given. Forty-two of the 52 compact pairs are the first kind and ten are this one, and the difference is not a modifier: it is the list around them, because in a list of candidates a pair is something you answer rather than something you read.
Look at the two markups. On a card the halves are wrapped:
<a><button>YES</button></a>, because the whole card links to
the event. In an outcome list they are bare <button>s, because there is nowhere
to go.
Every colour rule used to hang off > a. So a pair whose
buttons are not inside an anchor got no color from anywhere and fell back to the
User Agent's buttontext: near black on a graphite card, 1.42:1, on four
screens. Same shape as the 992 links that rendered in the browser's blue, and the same
sentence covers both: A MISSING COLOUR IS A COLOUR. A component has to say what it is
whatever markup it stands in.
Every selector is written twice now,
> a:first-of-type button and > button:first-of-type, and the pair
of them is what the sentence in the heading means.
the same chooser with NO taken, and until 2026-08-15 no screen in this repository had ever drawn it
Five rules in yesno.css draw a chosen NO and not one of them
could fire. Read in the DOM across all three trees on 2026-08-15, .sel and
.no had never once stood on the same element: 127 chosen sides in the paint,
24 in the grey and 39 on this stand, and every one of them YES. The pair is symmetric and half of
it had never been rendered, so half of it had never been measured either, which is this
repository's whole method turned off for one control.
It was the SCREENS and not the stylesheet, and the proof is that the grey
tree agreed with the paint. A colour layer that had lost a state would show the state in grey
and not in colour; both trees showed only YES, so the tree depicted one path. The fix was one
screen in both trees, event-detail-bet-processing.html, chosen because its panel is
mid-submit and carries no payout to keep in step: the pick moves in the panel and in the sticky
dock, the dock's figure goes from $13.16 to $8.06 because $5 at 62 per cent pays that, and
the YES picture inside the how-it-works sheet stays YES because it is a picture and not this
person's bet. The route already existed: backlog 143 gave every card
?side=yes and ?side=no, 212 anchors each, so the product had been
promising an arrival the tree never drew.
And rendering it for the first time is what let it be measured. The
chosen NO is --text-on-no on --outcome-no at 4.64:1, against the
chosen YES at 6.42:1. Both clear AA for body text; the NO side is the tighter of the two
and now has a number.
It rests on the system's quiet ground and not in green and red, and
components/tokens.css wrote the reason beside the token it is not using: the tinted
fill is "the tinted YES button (spectator, not trader)". A side pressed in green would be
inventing a colour the control does not have and telling a person the side is chosen before it
is.
Two faces, and they never coexist: .yesno-pick in the panel and
the sheet, .yesno-pick-bar in the sticky dock below 760. Measured, each is absent
where the other stands: 16 picks read 0x0 at 390 and 16 bars read 0x0 at 1280. Four more
are <span>s rather than buttons, on the two loading screens, where the shape of
the control stands in for the control.
| face | worn | at 390 | at 1280 |
|---|---|---|---|
.yesno-pick, in the sheet | 8 | 166x71 | 256x71 |
.yesno-pick, in the desktop panel | 16 | 0x0 | 140x71 |
.yesno-pick-bar, in the dock | 16 | 90x51 and 174x51 | 0x0 |
The chosen side is ONE answer at both widths, and it was two until 2026-08-06. The panel and the option list filled it solid; the dock bar used a strong fill with a lit ink. Both cleared the contrast floor and no file said why the same control should differ at two widths when the bar IS the panel below 760. Two of the three placements were solid, and the third is now: a person choosing a side on a phone and a person choosing it on a desktop are choosing the same thing.
The system's neutral press settles a control onto --bg-pressed.
On a filled outcome side that would lie: hold a green YES down, the outcome colour leaves
under the finger, and it reads as "it let go", which is the opposite of what holding YES
means.
| surface | how it goes down |
|---|---|
| a quiet control | settles onto --bg-pressed, one step deeper in both themes |
| a brass gradient | reverses its own angle |
| a filled colour | goes down by DEPTH: an inset shade at the top edge, and the ink keeps the exact ground it was measured on |
The third one exists because of a number with 0.14 of room in it.
--text-on-no over --outcome-no is 4.64:1, which is the 4.5 floor with
0.14 to spare. Pressing it by stepping the ground down through its own darker role spends more
than that at every strength a person could see: 92 per cent is already 4.48:1, 82 per cent is
4.29:1, and a mix small enough to pass is a mix nobody notices. The YES side had the room
and was deliberately NOT shipped alone, because a YES that darkens beside a NO that does not
reads as a broken NO.
The unfilled sides take the ordinary press, and both are restated rather than left to the hover, because a touch screen never hovers: on a phone the pressed side would otherwise keep its rest edge.
| face | halves | at 390 | at 1280 |
|---|---|---|---|
| full | 128 | 130x44, and 147x44 in the hero | 148x44 and 207x44 |
| compact | 84 | 46x44 and 43x44 | 46x44 and 43x44 |
| outcome list | 20 | 46x44 and 43x44 | 46x44 and 43x44 |
44 tall everywhere, with a mouse and with a finger, at both widths, and
none of it comes from the touch floor in base.css: .yesno button writes
min-height:var(--control-44) unconditionally. A media block used to restate that
inside a 640px query, which read as the target is a mobile concern. It is not, and
the block was deleted rather than corrected.
The 43 is the word NO. It measures 42.7 wide, so it stands 44 tall and
under 44 across, and it is one of the 212 targets in that shape across the product. A word's box
comes from the word, and widening every one of them is a layout decision about six components
rather than a floor. docs/backlog.md 50.
No disabled state. 241 pairs across all three trees and not one of them is disabled: there is no closed-market screen that greys the pair. A rule for it would be a guess about a screen that does not exist, and it would be written in the one place nobody would think to check when the screen is drawn.
No focus rule either, and this one is a positive reading rather than a
gap. base.css draws the ring once and outline-offset puts it 2px
OUTSIDE the button, so even on the filled green and the filled red the ring stands on the row
behind the control. The ground it has to survive is the card, which it already does.
One query, at DESK, and it is the half of the wrap that says what the pair
does with the second line. options.css decides the row wraps; below the rung this
file gives .opt-row .yesno.compact a whole line of its own and lets its two halves
share it, so the pair goes from 46 and 42.7px side by side with the name to 268 apiece under it.
It stays on the WINDOW, and that was measured rather than assumed, backlog 145. Forced into the inline layout at every width from 320 to 1600: the two halves measure 46 and 42.7px at every container width from 251 to 628, because they are content-sized and the name absorbs the rest. So the container cannot answer the question this rule asks. What the container does decide is whether the NAME clips, which happens below about 260, and keying the wrap there would put the cramped pair on every phone in the product, with one of the two halves under the 44px floor this project set itself. A non-separable rule is what it looks like when the window carries information the container does not have.
The pair is the control. Not two buttons that happen to sit together: one thing with two sides, which is why every selector here is positional and why nothing on this page shows one half by itself.
Green and red are the OUTCOME and never an opinion. A spectator's pair states which side the market is on. A trader's chooser states which side YOU are on, and until you have chosen it has no outcome colour at all.
A press is paid for out of whatever the surface has left. Where the contrast is spent, the depth pays.
Never hang a colour off the markup around the control. Every
colour here once hung off > a, and the pairs that are not wrapped in a link
rendered at 1.42:1 on four screens.
Never fill a trader's side before it is chosen. It tells a person they have committed to something they have not.
Never ship half a pair's state. The YES side had the contrast for a darkening press and the NO side did not, and a YES that darkens beside a NO that does not reads as a broken NO.
Never write a target inside a width query. A finger is not a mobile concern, and the deleted block said it was.
The level and its reason: the atoms page.
The stylesheet and the argument for every value: components/yesno.css. The panel,
the sheet and the dock that hold the trader's face, plus the odds figure inside each side:
components/betpanel.css. The colour decision, once for both families:
DESIGN.md.