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.

ruling
A container never pads itself for a child's sake. If a child needs the edge, the container was not the leaf's owner; move the padding down or drop it.
B markup + rules
<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.

ruling
An arbitrary value needs a reason the token scale cannot express, written next to it. If the reason is "close enough", use the token.
B markup + rules
<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 logs

confirm 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 logs

confirm 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.

ruling
One stacking context per overlay layer, small z values inside it, dom order for the rest. If two layers both need to be on top, the layer list is wrong, not the numbers.
B markup + rules
<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.

ruling
If an override cannot win through the cascade, the override is in the wrong place. Move it to the owner (a variant, a wrapper's styling hook) and delete the fight.
B markup + rules
<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

deploy finished2m
review requested14m
backup completed1h

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.

ruling
Reach for a div only when no element means the thing. If a click activates, the tag is button; if a set repeats, the tag is the list; if it names a time, the tag is time.
B markup + rules
<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 order
without 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

deprecated

the theme, hover states and parent overrides all stop at the style attribute; the values exist only here.

B: the classes carry it

deprecated

same badge, five tokens; it recolors with the theme, takes a hover rule, and reads identically in every other file.

ruling
The style prop is for values the code computed at runtime (a drag offset, a measured width). If you can write the value as a literal, it belongs in a class.
B markup + rules
<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 keep
working. 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.

ruling
Two elements that differ only in class strings are one component with a variant. Write the variant list once, at the component, with cva.
B markup + rules
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>
}
acme: 42.00

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>
}
acme: 42.00

same row; the shape of data is declared once, the reduce is checked, and a rename fails the build instead of the page.

ruling
Every component exports its props type. When a value's shape is genuinely unknown, type it unknown and narrow it; any is not a narrowing, it is a shrug.
B markup + rules
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 every
call 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.

search pages

go to any page