CSS variable and :root with media query

Traditional Approach

When defining custom styles, we traditionally rely on media queries to style the elements for different viewports. The styles among different viewport differs for font-size, margin and padding usually. Also, the layout between mobile and above would differ.

h1 {
  font-size: 2.5rem;
  line-height: 5rem;
}
h2 {
  font-size: 1.6rem;
  line-height: 3.2rem;
}

main,
aside {
  padding: 0.5rem;
}

@media screen and (min-width: 550px) {
  h1 {
    font-size: 3rem;
    line-height: 6rem;
  }
  h2 {
    font-size: 2rem;
    line-height: 4rem;
  }

  main,
  aside {
    padding: 1rem;
  }
}

Introducing :root

The :root CSS pseudo-class matches the root element of a tree representing the document.

NOTE: In HTML, :root represents the <html> element and is identical to the selector html, except that its specificity is higher.

:root {
  background: yellow;
}

Introducing CSS variables (Custom Properties)

Property names that are prefixed with --, like --font-size, represent custom properties that contain a value that can be used in other declarations using the var() function.

NOTE: Custom properties are scoped to the element(s) they are declared on.

We could define the CSS variable as follows:

h1 {
  --font-size: 3rem
  font-size: var(--font-size);
  line-height: calc(var(--font-size) * 2)
}

h2 {
  --font-size: 2rem
  font-size: var(--font-size);
  line-height: calc(var(--font-size) * 2)
}

For global variables, :root is where we define the CSS variables.

:root {
  --font-size-1: 3rem;
  --font-size-2: 2rem;
}

h1 {
  font-size: var(--font-size-1);
  line-height: calc(var(--font-size-1) * 2);
}

h2 {
  font-size: var(--font-size-2);
  line-height: calc(var(--font-size-2) * 2);
}

Using CSS variables and :root with media queries

Taking the first example here, we could do the same using CSS variables and :root. Here instead of defining media queries on element level, we are adding it to the variables that are used within elements:

:root {
  --font-size-1: 2.5rem;
  --font-size-2: 1.6rem;
  --padding-base: 0.5rem;
}

@media screen and (min-width: 550px) {
  :root {
    --font-size-1: 3rem;
    --font-size-2: 2rem;
    --padding-base: 1rem;
  }
}

h1 {
  font-size: var(--font-size-1);
  line-height: calc(var(--font-size-1) * 2);
}

h2 {
  font-size: var(--font-size-2);
  line-height: calc(var(--font-size-2) * 2);
}

main,
aside {
  padding: var(--padding-base);
}

Another exampmle for layout styles

Say you want to style a container whose elements needs to be laid out in columns while viewed on larger screen, otherwise on mobile it should be laid out as rows.

<div class="tiles grid-box">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>

<div class="cards grid-box">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>

In the example above, you could have added styles as follows to the .grid-box class thats shared between these list item components .tiles and .cards.

.grid-box {
  display: grid;
  grid-template-columns: 1fr;
}

@media screen and (min-width: 550px) {
  .grid-box {
    grid-template-columns: 1fr 1fr;
  }
}

@media screen and (min-width: 992px) {
  .grid-box {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

Depending on your use case, you could do it in :root to make styles cleaner and simpler.

:root {
  --grid-template-columns: 1fr;
}

@media screen and (min-width: 550px) {
  :root {
    grid-template-columns: 1fr 1fr;
  }
}

@media screen and (min-width: 992px) {
  :root {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

.grid-box {
  display: grid;
  grid-template-columns: var(--grid-template-columns);
}

Let me know what style do you prefer and why.