slop: code
The code behind an interface obeys the same test as the pixels: every value has one owner and a name, every element has a tag that already knows its job. Each section puts the common coding default (A) next to the typed, tokenized version (B) with the same rendered content, then states what the slop costs the next reader. Patterns collected from impeccable.style/slop; the reasoning, demos and samples are ours.
padded container + negative margins
The slop: a container pads itself and its full-bleed children climb back out with negative margins. The rule: containers arrange, leaves inset; a negative margin is a defect paying for padding that sits in the wrong place.
Every escaped child re-derives the container's inset by hand, so one spacing change becomes a hunt across every child that fought it. The site's lesson on this rule is leaf padding; here it counts as code slop for the same reason the visual ones do.
A: the card pads, the footer escapes
deploy preview
built from commit 8f2c.
the footer band must re-derive the card's p-4 as -mx-4 -mb-4; change the inset and every escape edits too.
B: the card stacks, the band spans
deploy preview
built from commit 8f2c.
same look, zero negative margins. the card only stacks; the band is a child of the unpadded container, so it spans by construction, and a spacer pseudo insets the button.
<div class="card"> <!-- stacks; zero padding --><div class="card-body"> <!-- leaf: pads itself --><h3>deploy preview</h3><p>built from commit 8f2c.</p></div><div class="card-foot"> <!-- band: spans by construction --><button>open logs</button></div></div>.card { flex; flex-col; overflow-hidden; rounded-xl }.card-foot { flex; min-h-12; items-center; justify-end; gap-2; pr-4 }<!-- nowhere: p-4 on .card, -mx-4 -mb-4 on the band -->
magic numbers
The slop: one-off values (13px, 38px, #8b8e94) written straight into components. The rule: spacing, size and color come from the design tokens; a value that exists only in one component is a decision nobody can find again.
Tokens are the search index of the interface: "everything at 4px" is one query, while "everything at 13px" is a grep for a literal that also matches your test fixtures. When two components drift to 13px and 14px, no rule tells you which one is wrong, because neither is on a scale.
A: values invented per component
mai
looks right on staging.
13, 22, 38, 9, #8b8e94: five numbers that exist nowhere else and are re-derived by eye in the next component.
B: values from the scale
mai
looks right on staging.
gap-3, size-9, rounded-md, text-sm, text-muted-foreground: every value resolves to a token the whole codebase shares.
<div class="flex gap-3 rounded-md bg-zinc-900 p-3"><div class="size-9 rounded-full bg-violet-500" /><div class="flex flex-col gap-0.5"><p class="text-sm">mai</p><p class="text-sm text-muted-foreground">looks right on staging.</p></div></div><!-- every value resolves to a token: gap-3, size-9, rounded-md,text-sm, text-muted-foreground. nowhere: 13, 22, 38, #8b8e94 -->
z-index 9999
The slop: layers fight with escalating numbers, 9999 against 10000. The rule: layering comes from dom order plus small, local z values inside one stacking context per overlay layer; a five-digit z-index is a symptom, not a fix.
A 9999 wins globally, so it punches through every overlay that opens after it: the tooltip in A stays readable over the confirm dialog's dimmer, which is exactly the bug the number was hired to prevent. In B the tooltip lives in its own stacking context, so the dialog, a later sibling, covers it without ever naming a bigger number.
A: 9999 punches through the dialog
build 214 rolled out to 10 percent of traffic. error budget untouched.
queued: deploy logsconfirm rollout
this dialog opened last; it should paint last.
the tooltip's z-[9999] beats the dialog's z-[500], so a debug tooltip bleeds through a modal that opened on top of it.
B: dom order decides
build 214 rolled out to 10 percent of traffic. error budget untouched.
queued: deploy logsconfirm rollout
this dialog opened last; it paints last.
same tooltip inside an isolate context; the dialog is a later sibling at z-10 and covers it. nobody named a number above 10.
<div class="relative"> <!-- the stage --><div class="relative isolate"> <!-- tooltip's stacking context --><p>build 214 rolled out ...</p><span class="absolute z-10">queued: deploy logs</span></div><div class="absolute inset-0 z-10"> <!-- later sibling paints above --><div class="absolute inset-0 bg-black/60" /><div class="dialog">confirm rollout</div></div></div><!-- layers: dom order plus local z values. nowhere: 999 -->
!important
The slop: an override that will not win by specificity gets !important, then the next override needs one too. The rule: a style conflict is settled by picking the right owner (a variant, a context, the cascade); !important ends the argument by ending the cascade.
Each !important raises the price of the next legitimate change: the only way to beat one is another one, further from the component that owns the look. Three contexts later, the save button's color is decided in a stylesheet nobody dares delete.
A: the color is decided by war
.save-btn { background: #2563eb }
.sidebar .save-btn { background: #7c3aed !important }
.settings .save-btn { background: #059669 !important }
.dark .save-btn { background: #dc2626 !important }three !important rules fight over one button; the winner is whichever context shipped last, not a decision anyone can point to.
B: the variant decides
<Button intent="destructive">save</Button>
intent: {
destructive: 'bg-red-600 text-white hover:bg-red-500',
}same button, one owner: the destructive variant is picked where the button is used. the cascade stays intact.
<Button intent="destructive">save</Button>// in the button component (cva)intent: {destructive: 'bg-red-600 text-white hover:bg-red-500',}<!-- one owner: the variant, picked at the call site.nowhere: !important -->
div soup
The slop: lists, buttons and labels rendered as a uniform pile of divs with click handlers. The rule: an element's tag is its contract; pick the tag that already behaves like the thing (ul lists, button activates, time dates).
The soup version ships broken for free: no keyboard focus, no list semantics for a screen reader, no enter key. Every behavior the tag would have given is re-implemented by hand in javascript, and the first reader who tabs to it finds nothing.
A: eleven divs and an onClick
mouse users get a cursor; keyboard and screen reader users get three unlabelled divs and no tab stop.
B: the tags do the work
same rows: ul announces a list, button takes focus and enter, tab order is free. no handler was written for any of it.
<ul class="flex list-none flex-col rounded-xl bg-zinc-900">{events.map((event) => (<li key={event.id}><button type="button" onClick={event.open}>{event.label}</button></li>))}</ul><!-- ul announces the list; button takes focus, enter and tab orderwithout a single hand-written handler -->
inline styles
The slop: static values written as style objects on the element. The rule: the style prop carries values react computes at runtime; everything static is a class in the token system.
A static style object is invisible to the theme (recolor the app and it stays), to pseudo-states (no hover, no focus ring), and to overrides (a parent cannot restyle it without !important, which is the previous section). It also re-derives the same constants in every file that copies it.
A: five constants live on the element
the theme, hover states and parent overrides all stop at the style attribute; the values exist only here.
B: the classes carry it
same badge, five tokens; it recolors with the theme, takes a hover rule, and reads identically in every other file.
<span class="rounded-md bg-zinc-800 px-3 py-1.5 text-xs text-zinc-300">deprecated</span><!-- static values live in classes: theme, hover and overrides keepworking. style={{ ... }} is for values react computed -->
copy-pasted variants
The slop: a third button means a fourth copy of the class string, or a ternary chain as long as the variant list. The rule: variants are data on one component (cva), not copies of the component.
Copies drift: the copy gets the new radius, the original does not, and the diff that should be one line is a sweep. A cva variant list is also the documentation: the next reader sees every allowed look in one place instead of grepping class strings.
A: one function, three copies of the string
adding a look means cloning the whole class string again; the base classes already disagree with nothing, because there are four of them.
B: one cva, named variants
same three buttons from one component; a new look is a new variant line, not a new copy.
const button = cva('inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium',{variants: {intent: {primary: 'bg-blue-600 text-white hover:bg-blue-500',danger: 'bg-red-600 text-white hover:bg-red-500',ghost: 'bg-zinc-700 text-zinc-200 hover:bg-zinc-600',},},},)<button className={button({ intent: 'danger' })} /><!-- one component, a named variant list. a new look is one line -->
any and untyped props
The slop: props typed as any and values reached through as. The rule: a component's props are its contract, written as a type; any deletes the contract and moves every mistake to runtime.
With any, the compiler checks nothing: a renamed field, a missing item, a string where a number belongs all ship. The as casts then hide exactly the lines where the assumption breaks, so the bug surfaces in a browser tab instead of a build.
A: the compiler is a bystander
function InvoiceRow(props: any) {
const total = (props.data as any).items.reduce(
(sum: number, item: any) => sum + item.price,
0,
)
return <li>{props.data.customer}: {total}</li>
}total is computed through two anys; rename customer or make price a string and nothing fails until a visitor opens the page.
B: the type is the contract
type InvoiceRowProps = { data: Invoice }
function InvoiceRow({ data }: InvoiceRowProps) {
const total = data.items.reduce((sum, item) => sum + item.price, 0)
return <li>{data.customer}: {total}</li>
}same row; the shape of data is declared once, the reduce is checked, and a rename fails the build instead of the page.
type InvoiceRowProps = { data: Invoice }function InvoiceRow({ data }: InvoiceRowProps) {const total = data.items.reduce((sum, item) => sum + item.price, 0)return <li>{data.customer}: {total}</li>}<!-- the props type is the contract; the compiler reads it at everycall site. nowhere: any, as any -->
Each A side is the default a rushed change reaches for; each B side is the same interface with every decision owned once, named, and checkable by the compiler or the token scale.