Simple CSS pitfalls

A few simple CSS mistakes and how to fix them.

Content-driven margins

We have a few items in a series, and we control the spacing by giving each item its own bottom margin, except the last. Perhaps we have a form, and we give fields bottom margins, as well as labels. Instead, consider spacing out the items by defining them as children of a parent, where the parent controls the space between its children.

To expand this principle more broadly, containers and content should both be spaced by spacing parents. Content should be spaced by simple listing or repeating objects, and containers should be spaced by layout objects such as grids, tiles, or blocks. In rare cases, one-off spacing shims will be useful, but most spacing can be defined as a relationship between a group of children.

<form class="form form--vertical">
  <div class="field field--vertical">
    <label>...</label>
    <input />
  </div>
  <div class="field field--vertical">
    <label>...</label>
    <input />
  </div>
  <div class="field field--vertical">
    <label>...</label>
    <input />
  </div>
</form>
.form--vertical > :not(:last-child{
  margin-bottom: 2rem;
}
 
.field--vertical > :not(:last-child{
  margin-bottom: 1rem;
}

Content-aware containers

A container should be nothing more than a slot for the content that goes into it. Consider a card: we can assume a card will always have a certain style of heading, or we can instead provide a slot for a heading to be rendered within. In nearly every case, the latter will prove the correct choice.

Containers should style padding, borders, background, etc. They shouldn’t be aware of foreground content. For bonus extensibility points, consider making regions within a container optional; for example, a card could be used without a header or footer.

<div class="card">
  <div class="card__header">
    ...
  </div>
  <div class="card__body">
    ...
  </div>
  <div class="card__footer">
    ...
  </div>
</div>

Deep selection

A CSS object should be relatively flat. Once we exceed a couple layers within a single object, it’s likely to have mixed concepts best abstracted into their own concerns. Don’t think in terms of business concerns, but design and layout abstractions. Compose CSS objects to form business components, which can be encapsulated with our rendering layer as a SPA framework component or server-side partial.

To put it concisely, if a page layout knows how to style a button, it’s too deep. Make objects configurable, and the composed component template can configure them; don’t allow for parents to alter the style of descendants. Avoid selecting deeply or across objects.

<section class="region region--dark">
  <div class="text text--inverse">
    <p>...</p>
    <a class="button button--large button--inverse" href="#">...</a>
  </div>
</section>
.region--dark {
  background: #111;
}
 
.text--inverse {
  color: #fff;
}
 
.button--large {
  padding: 1rem 2rem;
  font-weight: 700;
}
 
.button--inverse {
  background: #fff;
  color: #111;
}
Related articles

Exploring responsive grid systems for complex but structured layouts.

2018
CSS UI patterns
HTML + CSS

Using the flag or media pattern for locking up graphics and text for comments, alerts, and more.

2018
CSS UI patterns
HTML + CSS

A hidden element within a link can stretch the link’s focus outline to the edge of the viewport — and beyond.

2017
Firefox
HTML + CSS