One decision, one sentence of why, per component — grounded in real
screens (diet-guard, dufs-cloud, screen-locker), not invented examples.
Where a component isn't built yet, that's stated rather than faked.
Radius: radius-sm, 8px. Same token as inputs and chips — one family of interactive elements, not a size picked for buttons alone.
Padding: 24px / 12px. Horizontal is exactly 2× vertical (rule 22) — never tuned per component.
Label: 16px, body. A button is read, not decorative chrome — never below the 16px floor.
Fill only, no shadow. Dark surfaces never carry a shadow — weight comes from the fill itself.
Secondary
Disabled
Fill vs. border-only is the only weight distinction — never two filled buttons on one screen, so Log meal (primary) and Add item (secondary) read as different weight at a glance.
✓Fixed this session: label is on-fill (#211d1b, dark), not near-white — near-white measured ≈3.2:1 on this fill, below the 4.5:1 floor. Dark text passes 5.8–9.7:1 on all four semantic fills; it's a closed rule, not a per-color pick.
Radius: radius-md, 12px. One step up from a button's 8px — a card contains interactive elements, so it must read as a larger unit than they are.
Elevation: fill step, not shadow.surface-1 against the page background — dark surfaces never use a shadow for depth.
Nested radius: 6px. Rule 24 — inner cell radius = outer (12px) − the gap to its edge (≈6px), not a reused constant.
Status cells use on-fill. The ✓/! cells sit directly on success/danger — this is the one place in the app that already got the on-fill rule right.
The status colors sit at mid brightness, so ink gives strong on-palette contrast — this is the real code comment in day_status_calendar.dart, written before today's on-fill token existed to name the pattern.
Not yet themed. No InputDecorationTheme is set in theme.dart — every TextField renders M3's default indigo focus color, off-palette.
Tkinter side too.gatelock's tk.Entry sets bg/fg but no relief/bd — Tk's default sunken bevel border, not the flat line-dark outline.
⚠Open finding, not fixed: the input shown above is the target spec (radius-sm, flat 1px border, no bevel) — the actual rendered app still falls back to each toolkit's own default. Queued with the pilot-code palette migration, same class of bug as the Chip finding below.
Property
Value
Rule
Radius
8px
shared with buttons/chips
Border
1px line-dark
flat, no bevel
Label
12px caption
chrome, not read content
Fill on focus
surface-1 → surface-2
fill step, not a shadow/glow
Chip 04live
diet-guard/app/lib/widgets/slot_selector_row.dart
✓9am
🍎 Snack
Fill/border at 15%/40% alpha of the status color. Not a fixed chip palette — logged/due/upcoming reuse success/danger/muted, so a chip's meaning matches the calendar's.
Bug, still open: the Snack chip sets no backgroundColor/side at all, so it falls through to Flutter's own default — a literal pure-white #FFFFFF border, the one banned value in the whole system.
The hour chips (left) do this correctly: icon at 72% opacity of the status color (rule 28), selected border 2px vs. unselected 1px at 40% alpha. Snack just needs the same three properties applied — not a new pattern, a missed one.
Same radius (radius-md), different depth technique, and that's correct, not inconsistent: the web dialog floats over other content (shadow allowed, per policy), the screen-locker dialog fills the entire lock screen — there's nothing behind it to cast a shadow onto, so it's fill/border only, same as every other dark surface.
Property
Value
Rule
Radius
12px
radius-md, one step above buttons
Web shadow
0 4px 8px rgba(ink,.16)
level 2 — bigger than a dropdown's level 1
Actions
right-aligned, 8px gap
space-2 on the spacing scale
Switch 06spec only
no live usage yet — locking the values in before the first one gets built
Track = accent, thumb = on-fill. Same fill/on-fill pairing as a button — a switch is a tiny filled control, not a new color relationship to invent.
Fully rounded track. The one place in this system a pill shape is correct: an on/off track has no other elements to stay visually consistent with, unlike a button.
Off state: track becomes surface-2, thumb becomes muted — a fill-step change, matching how every other "inactive" state in this system works (never a separate grey palette).
⚠Open finding, not fixed:settings_screen.dart uses const Divider() with no dividerTheme set app-wide — same missing-theme pattern as Input above, this time landing on Material's default grey rather than line-dark.
Property
Value
Rule
Weight
1px
hairline — never a filled bar
Color
line-dark / line-light
same border token as cards/inputs
Spacing
space-3 (16px) each side
never touches adjacent text
Checkbox 08live
dufs-cloud/web/src/index.css — .tile-select
Chip sits on a scrim, not a fill.rgba(ink,0.45) over a photo thumbnail — this is on-scrim territory, not on-fill, since the thumbnail behind it varies.
Open finding: the checkbox itself is an unstyled native <input type="checkbox"> — its checked-state color is the browser's default blue, off-palette. No custom checkbox styling exists yet.
The 8px-radius scrim chip behind it is deliberately correct (matches radius-sm, dark translucent per the scrim rule) — only the checkbox glyph inside it is unstyled.
Snack bar / toast 09spec only
no live usage yet — no repo has a toast/snackbar today
Upload failed — connection lostRetry
Never danger-filled. A toast is transient chrome, not a status cell — surface-2 + danger-colored icon/text reads as urgent without becoming a full alert box.
One action max.Retry here, styled as text, not a button — a toast disappears on its own; a second filled button would compete with the page's real primary action.
Copy above is realistic (a dufs-cloud upload failure), not a placeholder — written to spec the tone (states what broke, offers the one useful next step) alongside the visual values.