Cover image for Things I Wish I’d Known About CSS

Things I Wish I’d Known About CSS

websmyth profile image Dave Smyth Originally published at cssfordesigners.com ・7 min read

I learned how to build websites the old fashioned way: looking at website source code and trying to replicate the things I saw. I threw in the odd book for the stuff I couldn’t see (like PHP/MySQL), and I was on my way.

This was back in 1999, when we’d write things like <font size="4" color="#000000"> and DHTML was a thing.

When CSS came along my approach to learning didn’t differ. But I really wish I’d taken the time to learn CSS properly: there was so much fundamental stuff I missed.

Here are some things I didn’t know that I wish I’d learned earlier.

Apologies for the lack of Codepen embeds: private pens don’t seem to be embeddable. The original article – with embedded examples – can be read at CSS For Designers.

Block, inline and inline-block

Even though I knew about these properties, I didn’t fully understand them for a long time.

Here’s a breakdown:

  • block elements expand horizontally to take up a whole line (like a heading). We can apply vertical margin to these.
  • inline elements expand horizontally just enough to contain their content (like strong or em elements). We cannot apply vertical margins to these and they should usually be placed inside a block element.
  • inline-block elements are like inline elements, but you can apply vertical margins to these (making them useful for things like buttons).

In this example, block elements have a blue border, inline elements have an orange background, and our inline-block element has a red border.

Images are inline

Images being inline by default isn’t a problem, but it can cause confusion when trying to position them or add vertical margins.

If your CSS reset doesn’t include this already, I’d suggest adding the following rule:

img {
        display: block;

That will make their behaviour much more predictable. You might also want to add max-width: 100%; to stop them breaking out of their container, too.

How height and width are calculated

By default, the width/height of a box is calculated by adding together the dimensions of the:

  • Content area
  • Padding area
  • Border area

This isn’t usually a problem for an element’s height: we’re usually not too bothered about how content reflows vertically. The problems usually occur when trying to calculate an element’s width, especially if there’s more than one element in a row.

If we had the following CSS:

.some-class {
        width: 50%;
        padding: 2em;
        border: 0.1rem;

The total calculated width for .some-class would be:

50% + 4em + 0.2rem

That’s because of a property called box-sizing which has a default value of content-box. This value means the width property applies to the content area: everything else is added on top of this.

We can change this for all elements with the following rule:

* {
        box-sizing: border-box;

Returning to our example, the element’s width would now apply to the border, so our element’s total width would be 50%.

What happens to the border and padding? Those properties/values still apply, but they don’t affect the total width of the element: they sit within the defined area.

Check out this Codepen to see this in action:


We haven’t discussed margin here because margin is the space between elements. For that reason, it is never part of this calculation.

Padding & margin aren’t the same

If an element has no background or border, it can appear that padding and margin are the same. They are not!

  • margin is the space between elements
  • padding is space inside an element

That makes padding useful for elements that have a background. We often don’t want the content to be close to the edge of the element’s box and padding helps us achieve that.

Margins collapse

This has been the source of frustration for CSS newcomers for a long time. Rachel Andrew describes the behaviour as:

When margins collapse, they will combine so that the space between the two elements becomes the larger of the two margins. The smaller margin essentially ending up inside the larger one.

If we have two block elements with margin-bottom: 1em on one element and margin-top: 1.5em on the element directly below it, the total space between the two elements would be 1.5em.

We can see that here.

When two margins meet, the larger margin absorbs the smaller margin. If the margins are the same value, they absord each other: margins are never added to each other.

As soon as we know this, margin calculations become easier. It might also change our approach to managing them, and that’s where something like the Lobotomised Owl selector can be useful.

Note: Margins don’t collapse when the parent element is set to display: grid or display: flex.

Browsers have a default stylesheet

CSS stands for Cascading Style Sheets. It’s no surprise therefore that the cascade is one of the fundamental concepts of CSS.

Though we might be aware of how our own stylesheets interact with each other, we have to remember that there’s always a default browser stylesheet. This is loaded before any custom stylesheets, making it easy to redeclare existing values.

The declared styles vary by browser, but they’re the reason that, by default:

  • Headings have different sizes
  • Text is black
  • Lists have bullet points
  • Elements have any display property (such as block or inline)

And many other things.

Even if a website only has a single stylesheet, that stylesheet will always be merged with the browser’s default styles.

Use relative units everywhere

Using pixels (px) is tempting because they’re simple to understand: declare font-size: 24px and the text will be 24px. But that won’t provide a great user experience, particularly for users who resize content at the browser level or zoom into content.

I started using em (and later rem) for font sizing early. It took much longer to feel comfortable using em and rem for other things such as padding, margin, letter-spacing and border.

Understanding the difference between em and rem is critical to making relative units manageable. For instance, we might use em for @media queries and vertical margins, but rem for consistent border-width.

The benefits of going all-in on relative sizing are well-worth the small adjustment in thinking that requires.

::before and ::after need content

When using either the ::before or ::after pseudo-elements, they require the content property, even if it’s left blank:

.some-class::before {
        content: '';

If this isn’t included, the pseudo-element won’t display.

The ch unit

The ch (character) unit is useful, particularly to set an element’s width based roughly on the number of characters in a line of text.

Only roughly? Technically, the ch unit doesn’t count the number of characters in a line.

ch is based on the width of the 0 character. Eric Meyer wrote that:

1ch is usually wider than the average character width, usually by around 20-30%.

If you’re using this to control the measure of paragraphs or similar, this is a useful distinction to be aware of.

Normal flow

This was a term I’d heard a lot but didn’t fully understand for a long time. The “normal flow” means that elements appear on the page as they appear in source code.

For instance, if we wrote:

<p>Paragraph text.</p>

We would expect <h2>Heading</h2> to appear before/above <p>Paragraph text.</p>. That is the normal flow.

If an element is taken out of the normal flow, that means it won’t appear in this place. Floated and absolutely positioned elements are good examples of this.

Styling :focus states

I first learned about :hover, :focus and :active pseudo-selectors in the context of styling links. At the time, all of the examples I’d seen looked something like this:

a {
    color: black;
a:active {
    color: red;

However, it’s better if we style our :focus states differently.

:focus is the state when a user tabs to or through focusable elements on a page (like links). When a user presses [tab], they don’t know where the focus will land.

Additionally, if a user focuses on already-hovered item, they won’t know where the focus is.

For all of these reasons, it’s best to style :focus in a different way to :hover and :active:. For instance:

   a:active {
   /* styles */
a:focus {
    /* styles */


Check out this Codepen.

Notice how it’s the odd-numbered rows with a background? Given our selector (p:nth-child(even)), we might expect the even-numbered rows to be highlighted instead.

However, the :nth-child() selector counts all sibling elements. Specifying an element in the selector (e.g. p:nth-child()) does not cause the selector to start counting from the first of that element type.

Instead, specifying an element in the selector means that the rule will only be applied to that type of element. If we switch our example to be p:nth-child(odd), we will see that:

  • h1 is not highlighted, even though it’s an odd sibling element
  • p elements that match the critera (paragraph two, four, six) are highlighted

That can be seen in this Codepen.

Returning to our first example, let’s assume we want the even-numbered p elements to have a background. In that case, we’re better off using a different pseudo-selector altogether: p:nth-of-type(even)

See that here.

This is demonstrates a key difference between :nth-child() and :nth-of-type(). It’s subtle, but knowing this might help to avoid some confusion.

Wrapping up

It’s easy to get to grips with the basics of CSS, but understanding how and why things work is critical to writing better CSS.

Taking the time to learn these things not only helped me to write CSS more quickly, but it has also helped to make my code more efficient and resilient.

This article was originally published at cssfordesigners.com

Posted on by:

websmyth profile

Dave Smyth


Independent Web Designer ☞ Writing: davesmyth.com ☞ Learn CSS: cssfordesigners.com ☞ Work with me: websmyth.co ☞ Thoughts on freelancing: worknotes.co.uk


Editor guide

I have a few tips I want to say around the image thing which can help to understand relation of inline, inline-block and block:

  1. Images are like inline-block.
  2. inline-blocks are like a single character of text.
  3. The "mysterious" space with default images is just the way characters are laid.
  4. Images are vertical-align: baseline by default.
  5. Instead of display: block on image you can say vertical-align: top.
  6. Text is always inline (this is why you need a span wrapper to do cool stuff with characters).

If you study technicalities of fonts you can understand a whole lot better the whole vertical-align stuff and relation to inline and inline-block.


This is such a great list!!! This is the "foundations" that so many people skip / and can't seem to get back to.

"Things we wish - everyone knew about CSS."

Side note on your formatting: You can write


.selector {
  color: red;

(or whatever language, html, css, js) and get the right syntax highlighting. : )

Great post!


Thank you! Great tip :)


Three things about CSS I wish I'd understood earlier:

  • HTML elements are not CSS boxes.
  • The whole concept of the line box.
  • The difference between block level boxes and block container boxes.

oh wait you have an article about the first one dev.to/alohci/mapping-html-to-css-...


Can you explain the first 2 in a couple clarifying sentences please?


We always strip collapsing margins and use margin-top. Do you like the collapsing margin?


I’m all-in on the Lobotomised Owl :)


Wow! Really! I haven't used it in forever. I'll have to look at it again.

Yeah! Wrote a bit more about it here:

Ok. It's coming back to me: Good ol 2014

I've been teaching some CSS typography this last week - and I was giving them this challenge.

Here's my solution. (of course I tell them they don't need to do it that way / and it's more about the outcome - and understanding the tools)

I think the owl would do the trick... but - it also might give me a little less control. In many cases - we're just styling one big article CMS block. What are your thoughts on that?


article > * + * {
    margin-top: 1em;

vs / a more specific rule

article h1 + h2 {
    margin-top: 1.2em;
article h2 + p {
    margin-top: 1em;

and I guess - to add to that, sometimes we're working with 'event' type lists / and there might be a headliner band... and 1 or 4 support bands, and a DJ - and a special note about the costs, and all ages or not, - and so the markup is always on the fly. In those cases there isn't necessarily space between each of those - so, that's another reason why we use the + with so much specificity. It's complex at first / but then it just works and can handle anything we throw at it. So, that's another edge on that case. Same might be if your headings had padding and background colors.

My general approach is to set the general spacing then define exceptions, so it might be something like:

article > * + * {
  margin-top: 1em;
* + h2,
* + h3 {
  margin-top: 2em;

...etc. I write exceptions often, but I reckon it’s still fewer than if I wrote each margin. There are other benefits to this approach, though, particularly when trying to make CMS blocks that are interchangeable.

It’s also really useful for horizontal lists, but I guess you might already use it for that. :)

.some-class {
        width: 50%;
        padding: 2em;
        border: 0.1rem;

The total calculated width for .some-class would be:
50% + 2em + 0.1rem

It will actually be 50% + 4em + 0.2rem, because you use padding and border and not just padding-left (right) / border-left (right)


Ahhhh, of course! 🤦🏻‍♂️

Consider it updated. Thanks 🙌


I like your point and explanation about styling :focus states differently. I had seen similar examples (probably while googling a related problem) and took away that :focus and :active states are meant to be styled together. I hadn't thought to question why those two states are distinct. This seems like an easy/fast way to start incorporating accessibility into our initial builds, and a great cue to read up on those pseudo-classes!


Thanks, Jack. Yeah, it’s definitely something that seems obvious in hindsight, but something that isn’t seen that regularly on the web.

Glad that was useful :)


DHTML was a thing. But <p size="4" color="#000000"> wasn't. I think you mean <font size="4" color="#000000">


Thanks! What a typo :) Corrected...


I learned stuff that had evaded me because it had never been explained this simply even though it was always dreadfully simple I guess. So embarrassed and grateful.


Thank you, John. Absolutely no embarrassment needed: lots of these things aren’t explained clearly in docs, and some of these things took me a longer time to discover than I’d care to admit. :)