Forget normalize or resets; lay your own CSS foundation

⚠️ This article is outdated: Global CSS is an anti-pattern; components should be individually styled using tools such as styled-components. See Fix for the most recent, though still outdated styles from this article.

Most start their front end styles by throwing in a reset such as Eric Myer’s or the more fashionable normalize.css. I will say a few nasty things about both approaches before I present to you the perfectionist alternative: Your own custom foundation.

Foundation is a good word for a different approach. Resets strip user agent default styles back. Normalizing attempts to even out the differences and fix a few things. A foundation strips and adds styles where it makes sense to minimize the amount of declarations and overrides you will have to make later. While a foundation can be replicated elsewhere, it is simple and intended to be built on top of.

Shitty things about normalize.css

  1. Declarations that aren’t helpful. What’s the point in specifying arbitrary sizes for headings when you are going to overwrite them all anyway?
  2. Support for browsers you might not need to support. Do you really need the IE8 hacks?
  3. It’s over 8 KB. Hopefully you strip the comments, minify and include it in the main stylesheet to avoid an extra network request, but I know a lot of you don’t.

Shitty things about resets

  1. Reset declarations can really clog your inspector when you try to trace styles.
  2. Resets don’t attempt to normalize specific cross-browser issues or set helpful styles.

Bad things about both

  1. Declarations you don’t need. Do you use <hr> tags? Neither do I. Chances are you won’t be using <kbd>, <var>, <ruby> and a bunch of other bloat. Eric Myer’s reset, the most popular, still accounts for deprecated tags such as <tt> and <big>.
  2. Both tend to include display: block styles for HTML5 element support in old IE. This is already handled by the HTML5 shiv.
  3. Essentials that technically fall outside of the scope of cross-browser normalization are missing. E.g. making images responsive.
  4. Neither are perfect: You should try to be. Front end developers need to be intimate with user agent default styles for a million reasons. There will be times that you work on existing projects that don’t use normalize.css and you will need to know what you are doing.

Lets get on to that perfectionist solution.

The foundation

The following essential styles should set the foundation for pretty much every project.

html {
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  font: 16px/1 sans-serif;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
h4,
p,
blockquote,
figure,
ol,
ul {
  margin: 0;
  padding: 0;
}
main,
li {
  display: block;
}
h1,
h2,
h3,
h4 {
  font-size: inherit;
}
strong {
  font-weight: bold;
}
a,
button {
  color: inherit;
  transition: 0.3s;
}
a {
  text-decoration: none;
}
button {
  overflow: visible;
  border: 0;
  font: inherit;
  -webkit-font-smoothing: inherit;
  letter-spacing: inherit;
  background: none;
  cursor: pointer;
}
::-moz-focus-inner {
  padding: 0;
  border: 0;
}
:focus {
  outline: 0;
}
img {
  max-width: 100%;
  height: auto;
  border: 0;
}

Breakdown

Lets go over this line-by-line.

html {
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

The text-size-adjust declarations prevent certain mobile browsers from automatically zooming fonts. Importantly this ensures iOS Safari matches the page width to wider landscape viewports instead of zooming everything. Unlike none, a value of 100% allows users to still pinch-zoom. These rules appear in Normalize.css, which is in turn used in the HTML5 Boilerplate and Bootstrap.

body {
  margin: 0;
  font: 16px/1 sans-serif;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
}

Often undesired page margins are removed. The default font size can vary between browsers; normalize this to a legible base size suited to your chosen main font, which should replace sans-serif. A line-height of 1 resets inconsistent browser interpretations of the default normal to a more robust base value that will not cause undesired spacing.

The font-smoothing declarations are a good idea for a few reasons:

  1. Fonts on OS X will look more consistent with other systems that do not render text using sub-pixel anti-aliasing.
  2. Although grayscale is inferior to sub-pixel anti-aliasing, most fonts were designed for the “thinner” anti-aliasing and look better that way.
  3. Setting this smoothing beforehand prevents an ugly flickering of font “thickness” when animations start and stop. Browsers use this simpler font smoothing to save on resources when animating or transitioning elements.
h1,
h2,
h3,
h4,
p,
blockquote,
figure,
ol,
ul {
  margin: 0;
  padding: 0;
}

Here we head into reset territory. Stripping the margin and padding from these core elements brings them to the same predictable blank slate as div, section and article. The vast majority of the time you style these elements you want either a 0 value or something very specific. You will override 0 a lot less than an arbitrary value such as 1em.

Only specify elements that exist in your project. For example, you may never use h4 or blockquote tags. Add or remove as needed to avoid redundant code.

main,
li {
  display: block;
}

This fixes the main tag being unsupported and horribly breaking the layout in even recent versions of IE.

It also gets rid of the little bullets and numbers from list items, saving you having to strip them off repeatedly every time you make a menu or any other component that is semantically a list. When you actually need bullets or numbering pseudo-elements provide more customization options than the standard list-style approach anyway.

h1,
h2,
h3,
h4 {
  font-size: inherit;
}

A heading’s level should be determined by it’s placement in the document outline and not by the stylistic appearance of the words. Sometimes a h2 will be designed smaller that a h3 elsewhere on the same page. Components of a modern web page tend to have very localized heading styles; it’s never one-size-fits-all. Often headings are the same size as the accompanying text and are distinguished by bolding, capitalization and letter spacing instead.

It makes much more sense for headings to inherit the same size as the surrounding text by default and not some arbitrary larger size. You will have greater consistency and override less.

strong {
  font-weight: bold;
}

This corrects some browsers defaulting to bolder.

a,
button {
  color: inherit;
  transition: 0.3s;
}
a {
  text-decoration: none;
}

Long-gone are the days where all hyperlinks were blue, underlined and only wrapped inline content.

color: inherit fixes buttons not inheriting text color in most browsers. This, along with text-decoration: none makes for less surprises when applying links to things. Links are styled many different ways on the same page. Think menus, sidebars, footers, buttons and within text. Some text is light-on-dark, some is dark-on-light. The browser default blue or any other color would be illegible in many places. It makes for less complex styles when links inherit the color intended for the region they are in. Sometimes you just want to apply an underline, sometimes only a color. Sometimes nothing, because you just wrapped an entire thumbnail and caption.

90% of your transitions apply to links and buttons, and most of them have a similar duration. Take a look. I bet many of the remainder could have, had you assigned more styles to the link and not the parent or child (which is a good idea anyway for a few reasons, namely a larger touch-friendly clickable area). Rarely you need to remove transitions from a link or button.

button {
  overflow: visible;
  border: 0;
  font: inherit;
  -webkit-font-smoothing: inherit;
  letter-spacing: inherit;
  background: none;
  cursor: pointer;
}

overflow: visible brings IE into line. The border, font, letter-spacing and background rules conveniently reset the native appearance in all browsers. The -webkit-font-smoothing rule fixes buggy inheritance. For UX reasons and consistency with links it is a good idea to give all buttons a pointer cursor.

::-moz-focus-inner {
  padding: 0;
  border: 0;
}

This undoes a bummer pseudo-element Firefox adds inside buttons that causes them to display larger than expected. It’s a shame this has been a recognized bug since 2002.

:focus {
  outline: 0;
}

Be careful with this! It removes the gross user agent default outline that appears when focusing interactive elements. For accessibility only do this if you are absolutely sure you have custom focus state styles set everywhere.

img {
  max-width: 100%;
  height: auto;
  border: 0;
}

One of the fundamentals of responsive web development, this makes sure images respect the bounds of a fluid container. The image never exceeds the width of it’s container. The image’s aspect ratio is preserved even if it has an inline height set.

border: 0 fixes images within links gaining a border in IE 8, 9 and 10.

Extras

This foundation is a minimal starting point suitable for any project; tailor it to your individual needs and avoid unnecessary rules.

Some common extras you may need to cater for:

  • Heading levels beyond h4.
  • supand sub.
  • Form components.