CSSCommon CSS Mistakes

Common CSS Mistakes

Unlike the classic "bugs" (broken behavior with a specific cause), this page covers anti-patterns — code that works today but creates ongoing pain: harder maintenance, fragile layouts, and CSS nobody wants to touch. Each one comes from a reasonable-seeming shortcut that stops paying off at scale.

1. Overusing !important

CSS
.button {
  color: blue !important;
}

/* Six months later, someone needs a red variant... */
.button.danger {
  color: red !important; /* had to match the !important just to win */
}
Why this is a mistake
!important does not just win a specificity fight — it exits the normal cascade entirely, so the only way to ever override it again is another !important with equal or higher specificity. Codebases that reach for it casually end up in an !important arms race where nothing can be safely adjusted without more of it.

Instead of

Do this

!important to beat a competing rule

Fix the actual specificity conflict, or reorder source so the correct rule wins naturally

!important on a utility class

Only acceptable there, since utilities are meant to always win — document this exception clearly

2. Deep, over-specific selectors

CSS
/* Fragile: breaks the moment the markup structure changes */
.page .sidebar .widget ul li a.active {
  color: red;
}

/* Robust: one class, targets the thing directly */
.widget-link--active {
  color: red;
}
Why this is a mistake
Long descendant chains couple your CSS to a specific HTML structure. Move one <div> and the rule silently stops matching. They also rack up specificity, making the selector harder to override later without escalating the specificity war further.
3. Magic numbers

CSS
/* What is 37px? Why 37? */
.dropdown {
  top: 37px;
}

/* Better: derive it from something meaningful, or name it */
.dropdown {
  top: calc(var(--header-height) + var(--spacing-sm));
}
  • A magic number is any value with no explanation for why that specific number was chosen

  • They tend to be discovered by trial and error to "look right" in one browser, one viewport, one moment in time

  • They break silently the next time something nearby changes size

  • Fix: derive the value from a variable, a calculation, or at minimum leave a comment explaining the reasoning

4. Absolute positioning used for layout

CSS
/* Anti-pattern: positioning everything by hand */
.header { position: absolute; top: 0; left: 0; width: 100%; }
.sidebar { position: absolute; top: 60px; left: 0; width: 200px; }
.content { position: absolute; top: 60px; left: 200px; right: 0; }
Why this is a mistake
Absolute positioning removes elements from normal flow entirely. Content that changes height (a longer header on a smaller screen, a taller sidebar item) has nothing pushing siblings out of the way — everything just overlaps. Flexbox and Grid exist specifically to solve multi-element layout without any of this manual coordinate math.

CSS
.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: 60px 1fr;
}
/* Elements naturally push each other, no coordinates to maintain */
5. Fixed pixel widths everywhere

CSS
/* Breaks on any screen narrower than 960px */
.container {
  width: 960px;
}

CSS
.container {
  width: 100%;
  max-width: 960px; /* caps on large screens, shrinks freely on small ones */
  margin-inline: auto;
  padding-inline: 16px;
}
6. Not using custom properties

CSS
/* The same brand color, copy-pasted forty times across the codebase */
.button { background: #2563eb; }
.link { color: #2563eb; }
.badge { border-color: #2563eb; }
/* Rebrand day: forty places to find and change, guaranteed to miss one */

CSS
:root {
  --color-brand: #2563eb;
}

.button { background: var(--color-brand); }
.link { color: var(--color-brand); }
.badge { border-color: var(--color-brand); }
/* Rebrand day: one line to change */
Why this is a mistake
Repeated literal values (colors, spacing, font sizes) scattered through a codebase have no single source of truth. Custom properties turn "find every place this value appears" into "change one variable."
7. Resetting everything with a giant reset

CSS
/* Overkill: strips useful semantics along with the unwanted defaults */
* {
  all: unset;
}
Why this is a mistake
A blanket all: unset (or an outdated Eric Meyer-style full reset) strips out genuinely useful browser defaults too — list markers, focus rings, form control affordances — that then have to be manually rebuilt from scratch, often imperfectly, at a real accessibility cost.

CSS
/* Prefer a minimal, deliberate reset (roughly Josh Comeau's / modern
   normalize style) that removes surprises without deleting useful defaults */
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  margin: 0;
}

img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}
Summary table

Anti-pattern

Cost

Better approach

Overusing !important

Cascade becomes unpredictable, escalates over time

Fix specificity/order instead; reserve for true utility overrides

Deep selectors

Tightly coupled to markup structure, fragile

Flat, class-based selectors

Magic numbers

Unexplained values that silently break

Derive from variables/calc, or comment the reasoning

Absolute positioning for layout

Overlaps on any content-size change

Flexbox/Grid for multi-element layout

Fixed pixel widths

Breaks on smaller viewports

max-width + fluid width: 100%

No custom properties

No single source of truth for repeated values

Tokenize colors/spacing/type with --variables

Blanket resets

Deletes useful, accessible defaults

Minimal, deliberate reset

8. Ignoring the default focus outline

CSS
/* Anti-pattern: removing focus styles without replacing them */
button:focus {
  outline: none;
}
Why this is a mistake
Removing the outline without providing an alternative makes the page unusable for keyboard-only users, who rely entirely on that visual indicator to know which element is currently active. This is one of the most common, and most damaging, accessibility mistakes in CSS.

CSS
/* Better: keep it, or replace it with an equally visible alternative,
   and only for pointer users who don't need it */
button:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}
9. Mixing units inconsistently

CSS
/* A grab-bag of units with no underlying system */
.card {
  padding: 15px;
  margin-bottom: 1.3em;
  font-size: 18px;
  border-radius: 0.6rem;
}
Why this is a mistake
Random mixing of px, em, and rem with no consistent rule makes spacing unpredictable when a user changes their base font size, and makes it hard to keep values in step with a design system's actual scale.

CSS
/* Consistent: rem for anything that should scale with user font-size
   preferences, px only for things that genuinely should not scale
   (like a 1px hairline border) */
.card {
  padding: 1rem;
  margin-bottom: 1.25rem;
  font-size: 1.125rem;
  border-radius: 0.5rem;
  border: 1px solid #e5e7eb;
}
10. Forgetting print styles entirely

Many sites never define a single @media print rule, which means navigation bars, ads, and interactive controls all print exactly as they appear on screen — often wasting paper and looking unprofessional on anything a user actually prints (receipts, articles, directions).

CSS
@media print {
  nav, .ads, .no-print {
    display: none;
  }

  body {
    color: black;
    background: white;
  }

  a[href]::after {
    content: " (" attr(href) ")"; /* show URLs since links aren't clickable on paper */
  }
}
Summary table

Anti-pattern

Cost

Better approach

Overusing !important

Cascade becomes unpredictable, escalates over time

Fix specificity/order instead; reserve for true utility overrides

Deep selectors

Tightly coupled to markup structure, fragile

Flat, class-based selectors

Magic numbers

Unexplained values that silently break

Derive from variables/calc, or comment the reasoning

Absolute positioning for layout

Overlaps on any content-size change

Flexbox/Grid for multi-element layout

Fixed pixel widths

Breaks on smaller viewports

max-width + fluid width: 100%

No custom properties

No single source of truth for repeated values

Tokenize colors/spacing/type with --variables

Blanket resets

Deletes useful, accessible defaults

Minimal, deliberate reset

Removing focus outlines

Breaks keyboard navigation entirely

:focus-visible with a clear alternative style

Inconsistent units

Unpredictable scaling, hard to maintain a design scale

rem for scalable values, px only for fixed hairlines

No print styles

Unusable, unprofessional printed output

A minimal @media print block hiding non-content chrome

Next
See how these ideas scale up to whole-codebase conventions in CSS Architecture Overview.