Search code examples
csspseudo-elementcss-content

Why do the :before and :after pseudo-elements require a 'content' property?


Given the following scenario, why does the :after selector require a content property to function?

.test {
    width: 20px;
    height: 20px;
    background: blue;
    position:relative;
}
			
.test:after {
    width: 20px;
    height: 20px;
    background: red;
    display: block;
    position: absolute;
    top: 0px;
    left: 20px;
}
<div class="test"></div>

Notice how you do not see the pseudo element until you specify the content property:

.test {
    width: 20px;
    height: 20px;
    background: blue;
    position:relative;
}
			
.test:after {
    width: 20px;
    height: 20px;
    background: red;
    display: block;
    position: absolute;
    top: 0px;
    left: 20px;
    content:"hi";
}
<div class="test"></div>

Why is this the intended functionality? You would think that the display block would force the element to show up. Oddly enough, you can actually see the styles inside web debuggers; however, they do not display on the page.


Solution

  • The reason you need a content: '' declaration for each ::before and/or ::after pseudo-element is because the initial value of content is normal, which computes to none on the ::before and ::after pseudo-elements. See the spec.

    The reason the initial value of content isn't an empty string but a value that computes to none for the ::before and ::after pseudo-elements, is twofold:

    1. Having empty inline content at the start and end of every element is rather silly. Remember that the original purpose of the ::before and ::after pseudo-elements is to insert generated content before and after the main content of an originating element. When there's no content to insert, creating an additional box just to insert nothing is pointless. So the none value is there to tell the browser not to bother with creating an additional box.

      The practice of using empty ::before and ::after pseudo-elements to create additional boxes for the sole purpose of layout aesthetics is relatively new, and some purists might even go so far as to call it a hack for this reason.

    2. Having empty inline content at the start and end of every element means that every (non-replaced) element — including html and body — would by default generate not one box, but up to three boxes (and more in the case of elements that already generate more than just the principal box, like elements with list styles). How many of the two extra boxes per element will you actually use? That's potentially tripling the cost of layout for very little gain.

      Realistically, even in this decade, less than 10% of the elements on a page will ever need ::before and ::after pseudo-elements for layout.

    And so these pseudo-elements are made opt-in — because making them opt-out is not only a waste of system resources, but just plain illogical given their original purpose. The performance reason is also why I do not recommend generating pseudo-elements for every element using ::before, ::after.

    But then you might ask: why not have the display property default to none on ::before, ::after? Simple: because the initial value of display is not none; it is inline. Having inline compute to none on ::before, ::after is not an option because then you could never display them inline. Having the initial value of display be none on ::before, ::after is not an option because a property can only have one initial value. (This is why the initial value of content is always normal and it is simply defined to compute to none on ::before, ::after.)