TocTable of Contents

Css baseline 2021

Overview of 2021 baseline css features

All the features are Widely available because they all passed 36 months since being newly available.

fit-content

The fit-content keyword (and fit-content() function) sizes an element to fit its content, up to the available space in the container. It behaves like max-content — growing to wrap content without breaking — but is capped at the available width, after which it wraps like a normal block.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.tag {
  width: fit-content; /* Shrinks to content, won't overflow container */
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
}

.tooltip {
  width: fit-content;
  max-width: 20rem;
  white-space: normal;
}

In grid layout, fit-content() as a track sizing function accepts a clamp argument — the maximum size the track will grow to, even if the content is larger:

1
2
3
.grid {
  grid-template-columns: fit-content(200px) 1fr fit-content(300px);
}

This creates columns that size to their content up to the given maximum, then stop growing — the remaining space goes to the 1fr column.

As a width or height value, fit-content is equivalent to min(max-content, max(min-content, available)) — it takes as much space as the content needs, but never more than the available space, and never less than the minimum content size. This makes it useful for buttons, badges, tooltips, and any element that should hug its content without overflowing.

text-indent: each-line

The each-line keyword for text-indent applies the indentation not just to the first line, but to every line that follows a forced line break — giving you more control over indented text blocks.

By default, text-indent only affects the very first line of a block:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
p {
  text-indent: 2em;
}
/* with each-line */
p {
  text-indent: 2em each-line;
}

p {
    text-indent: 2em hanging each-line;
}

Now every line that begins after a <br> or a forced break is also indented. Lines that wrap naturally (soft wraps) are not affected.

Both keywords can be used together — hanging reverses the indent direction (outdent the first line) while each-line applies it after forced breaks too.

Use cases:

  • Poetry formatting where each verse line should be indented
  • Legal or script formatting with explicit line breaks
  • Custom typographic layouts
Ik ben de blauwbilgorgel.

Ik ben de blauwbilgorgel,
Mijn vader was een porgel,
Mijn moeder was een porulan,
Daar komen vreemde kind'ren van.
Raban! Raban! Raban!

Ik ben een blauwbilgorgel
Ik lust alleen maar korgel,
Behalve als de nachtuil krijst,
Dan eet ik riep en rimmelrijst.
Rabijst! Rabijst! Rabijst!

Ik ben een blauwbilgorgel,
Als ik niet wok of worgel,
Dan lig ik languit in de zon
En knoester met mijn knezidon.
Rabon! Rabon! Rabon!

Ik ben een blauwbilgorgel
Eens sterf ik aan de schorgel,
En schrompel als een kriks ineen
En word een blauwe kiezelsteen.
Ga heen! Ga heen! Ga heen!

Author: Cees Buddingh'

text-indent: hanging

The hanging keyword inverts the direction of text-indent — instead of indenting the first line, it indents every line except the first. This is also known as a hanging indent or outdent.

1
2
3
4
5
/* Standard: first line indented */
p { text-indent: 2em; }

/* Hanging: all lines except the first */
p { text-indent: 2em hanging; }

Hanging indents are the standard format for bibliography entries:

A negative value combined with hanging creates a classic bibliography style where the first line starts at the left edge and subsequent lines are indented.

1
2
3
4
5
6
7
8
9
.reference {
  text-indent: -2em hanging;
  padding-left: 2em;
}

p {
  /* Applies the hanging indent after every forced line break as well.*/
  text-indent: 1.5em hanging each-line;
}

Use cases:

  • Bibliography and works-cited lists
  • Dictionary or glossary entries
  • Definition lists in custom typographic styles

:where()

:where() takes a selector list as its argument, and matches any element that can be selected by one of the selectors in that list, without increasing specificity.

1
2
3
4
5
6
/* Base reset — zero specificity, always overridable */
:where(h1, h2, h3, h4, h5, h6) {
  font-weight: 700;
  line-height: 1.2;
  margin-top: 0;
}

:where() works exactly like :is() — it matches elements that satisfy any selector in its list — but it always contributes zero specificity. This makes it perfect for default and base styles.

1
2
:is(header, main) h2    { color: navy; } /* specificity: 0,0,1 */
:where(header, main) h2 { color: navy; } /* specificity: 0,0,0 */

With :where(), any user override with a simple h2 selector will win.

Like :is() , :where() uses forgiving parsing — invalid selectors are silently ignored.

:not()

:not() matches every element that does not match the selector inside it. Since CSS Selectors Level 4, it accepts a full selector list, making exclusions much more expressive.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* Style all paragraphs that don't have .intro */
p:not(.intro) {
  color: #444;
}

/* Exclude multiple classes at once */
li:not(.active, .disabled) {
  opacity: 0.6;
}

/* Add dividers between items, not after the last one */
.menu-item:not(:last-child) {
  border-bottom: 1px solid #eee;
}

:not() itself adds no specificity — but its argument does. :not(.active) adds class-level specificity (0,1,0).

:user-valid and :user-invalid

:user-valid and :user-invalid

Quotes

quotes The default value of quotes is auto. The browser provides language specific quotation marks when the <q> element is used.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<style>
.custom {
    quotes: '--' '--';
}
</style>
<div lang="en">
    <q>This is an English quote.</q>
</div>
<div lang="nl">
    <q>Dit is een Nederlandse quote.</q>
</div>
<div lang="en">
    <q class="custom">Dit is een Nederlandse quote.</q>
</div>
This is an English quote.
Dit is een Nederlandse quote.
This is a custom quote.

Tab size

tab-size defines the width of the tab character (\t) when it appears in rendered text. It only affects white-space: pre or white-space: pre-wrap contexts, like <pre> and <code> elements.

1
2
3
pre {
  tab-size: 2;
}

The browser default is 8 spaces. Setting it to 2 or 4 better matches common code style guides.

Instead of a number (in space characters), you can use a length:

1
2
3
pre {
  tab-size: 1.5rem;
}

Without tab-size, actual tab characters render as huge 8-space gaps. Setting tab-size: 4 makes code look as intended.

:is()

:is() accepts a list of selectors and matches any element that matches at least one of them. It is primarily a readability and maintenance tool.

1
2
3
4
5
6
/* before :is() */
header h1, header h2, header h3,
main h1, main h2, main h3,
footer h1, footer h2, footer h3 {
  color: #333;
}

1
2
3
4
/* width :is() */
:is(header, main, footer) :is(h1, h2, h3) {
  color: #333;
}

Much cleaner with identical results.:is() takes the specificity of its most specific argument. So :is(#id, p) has ID-level specificity even when matching a <p>.

`:is()` uses forgiving parsing — an invalid selector in the list is ignored rather than invalidating the entire rule.

Flexbox Gap

The very useful gap from grid layout gap column-gap row-gap is now also available for flex containers as gap.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="parent">
    <div class="box">box</div>
    <div class="box">box</div>
    <div class="box">box</div>
</div>
<style>
    .box {
        background: #00fb29;
        padding: 0.5rem 1rem;
    }
    .parent {
        border: 1px solid #00fb29;
        display: flex;
        flex-wrap: wrap;
        gap: 1rem;
        margin: 1rem 0;
        padding: 0.125rem;
        max-width: fit-content;
        width: 212px;
    }
</style>
box
box
box
box
box
box

File Selector Button

::file-selector-button selects the button part of a <input type="file>

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<input type="file">
<input type="file" class="styled">

<style>
    .styled::file-selector-button {
        background: rebeccapurple;
        border-radius: 0.25em;
        padding: 0.5em;
    }
</style>

Font Family System

font-family: system-ui uses the operating system default font for text.

Just as this website does.

Logical Properties

Logical properties replace physical directions (top, left, right, bottom) with flow-relative equivalents (block-start, inline-end, etc.), making layouts that automatically adapt to different writing modes and text directions.

PhysicalLogical
width / heightinline-size / block-size
margin-topmargin-block-start
padding-leftpadding-inline-start
border-rightborder-inline-end
top / leftinset-block-start / inset-inline-start

Shorthand

1
2
3
margin-block: 1rem 2rem;   /* block-start, block-end */
margin-inline: auto;       /* inline-start and inline-end */
inset: 0;                  /* all four sides */

Aspect Ratio

aspect-ratio sets the aspect ratio of the element. When using width and height aspect-ratio is overruled.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="aspect-video box">
    
</div>
<div class="aspect-video sized-box">
    
</div>
<style>
    .box {
        background: #00fb29;
        padding: 1rem;
    }
    .sized-box {
        background: #00fb29;
        padding: 1rem;
        width: 100px;
        height: 100px;
    }
    .aspect-video {
        aspect-ratio: 16 / 9;
    }
    .aspect-square {
        aspect-ratio: 1;
    }
</style>
box
sized-box

Counter style

Some examples


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div class="fits square">
    Lorem ipsum dolor sit amet.
</div>
<div class="fits">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit
</div>
<div class="fits">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</div>
<div class="fits">
    Lorem ipsum dolor sit amet.
    <em>consectetur adipiscing elit</em>
</div>

<style>
.fits { 
    max-width: fit-content;
    padding: 1em;
    border-inline-start: 1px solid red;
    border-inline-end: 1px solid blue;
    margin-block-end: 0.5rem;

    &:has(em) {
        border-width: 1px;
        background-color: #495050;
    }
}
.square {
    aspect-ratio: 1;
}
</style>

Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet. consectetur adipiscing elit