How to use CSS pseudo-classes for styling typographic elements
Tags: webdev, css, tutorial, ui
Published: 2021-01-22 15:39:08

Today let's work with CSS pseudo-selectors like `:first-letter`, `:last-of-type` & `:after` to build nice little typpgraphic elements in four different color themese that will spice up your text.

Read the full article or watch me code this on Youtube (TL;DW Speedrun):

{% youtube 4zOy352xv7w %}

# Result

{% codepen https://codepen.io/crayon-code/pen/yLarNyd %} 

## First Things First - Colors

To have all the colors at hand, each one is defined as a CSS custom property (aka variable) inside the `:root` scope. For a slight taste of theming the variables `background-color` and `foreground-color` are defined and used later on.

```scss
:root {
  --cc-p-1: #d4aee0;
  --cc-p-2: #8975b4;
  --cc-p-3: #64518a;
  --cc-p-4: #565190;
  --cc-b-1: #44abac;
  --cc-b-2: #2ca7d8;
  --cc-b-3: #1482ce;
  --cc-b-4: #05597c;
  --cc-g-1: #b2dd57;
  --cc-g-2: #57c443;
  --cc-g-3: #05b853;
  --cc-g-4: #19962e;
  --cc-y-1: #fdc82e;
  --cc-y-2: #fd9c2e;
  --cc-y-3: #d5385a;
  --cc-y-4: #911750;
  --cc-s-1: #d9d9d9;
  --cc-s-2: #9e9e9e;
  --cc-s-3: #666666;
  --cc-s-4: #2b2b2b;

  --foreground-color: white;
  --background-color: var(--cc-s-4);
}
```

## Color Themes

There will be four different color themes for the typographic elements: Amethyst (purple), sapphire (blue), emerald (green) and amber (yellow/orange/red). To make these themes easily usable, a `<section>` element must have one of the four classes given below, whereas each class simply defines a set of CSS variables which are later used for the gradients.

```scss
section {
  &.amethyst {
    --c-1: var(--cc-p-1);
    --c-2: var(--cc-p-2);
    --c-3: var(--cc-p-3);
    --c-4: var(--cc-p-4);
  }

  &.sapphire {
    --c-1: var(--cc-b-1);
    --c-2: var(--cc-b-2);
    --c-3: var(--cc-b-3);
    --c-4: var(--cc-b-4);
  }

  &.emerald {
    --c-1: var(--cc-g-1);
    --c-2: var(--cc-g-2);
    --c-3: var(--cc-g-3);
    --c-4: var(--cc-g-4);
  }

  &.amber {
    --c-1: var(--cc-y-1);
    --c-2: var(--cc-y-2);
    --c-3: var(--cc-y-3);
    --c-4: var(--cc-y-4);
  }
}
```

## Basic Principle

CSS allows us to specify multiple `linear-gradients` which are then nicely blended into each other. Luckily also `background-size` & `background-position` accept multiple, comma separated values, so each gradient can be sized and positioned inidividually. However, this time we're not intersted in creating actual gradients, but tiny mosaic squares each having a unique, so the gradients are created with two times the same color. So in order to create a background containing four squares, the `background` property is setup to have four linear gradients with two times the same color. Also note that setting `background-origin` to `border-box` is essential for this to work.

```scss
<selector> {
  background:
    linear-gradient(
      var(--c-1),
      var(--c-1)
    ),
    linear-gradient(
      var(--c-2),
      var(--c-2)
    ),
    linear-gradient(
      var(--c-3),
      var(--c-3)
    ),
    linear-gradient(
      var(--c-4),
      var(--c-4)
    );
  background-origin: border-box;
  background-repeat: no-repeat;
}
```

Now each gradient is positioned in the top left, top right, bottom left and bottom right corner which is achived by providing four x/y pairs tothe `background-position` property:

```scss
<selector> {
  ... 

  background-position:
    0% 0%,
    100% 0%,
    0% 100%,
    100% 100%;
  background-size: 50% 50%;
}
```

## SASS Mixins for Reusability

Since we'll be using the gradient setup from above multiple times, we'll group it into two mixins for easy reusing:

```scss
@mixin mosaic-base() {
  background:
    linear-gradient(
      var(--c-1),
      var(--c-1)
    ),
    linear-gradient(
      var(--c-2),
      var(--c-2)
    ),
    linear-gradient(
      var(--c-3),
      var(--c-3)
    ),
    linear-gradient(
      var(--c-4),
      var(--c-4)
    );
  background-origin: border-box;
  background-repeat: no-repeat;
}

@mixin mosaic() {
  @include mosaic-base;

  background-position:
    0% 0%,
    100% 0%,
    0% 100%,
    100% 100%;
  background-size: 50% 50%;
}
```

## Headline

The headline is going to have a bar below itself having four partial lines positioned one after another:

```html
<section>
  <h2>Mosaic Typographic Elements</h2>
</section>
```

The mosaic gradients are simply applied to an `::after` pseudo-class so there is no additional markup needed. Since the `h2` styles are nested within the `section` selector, the variables for the color theme defined there can be used perfectly well.

```scss
section {
  h2 {
    display: block;
    font-weight: 300;
    text-align: center;
    font-size: 2rem;

    position: relative;

    &::after {
      --width: 8rem;
      --height: 2px;

      content: "";

      display: inline-block;
      width: var(--width);
      height: var(--height);

      position: absolute;
      left: 50%;
      bottom: -1rem;
      transform: translate(-50%, -50%);

      @include mosaic-base;
      background-position:
        0% 0%,
        33% 0%,
        67% 0%,
        100% 0%;
      background-size:
        25% 100%,
        26% 100%,
        25% 100%,
        25% 100%;
    }
  }
}
```

## First Letter Styling

In order to style each first letter that appears after a heading let's use the so called _adjacent sibling selector_ to identity each paragraph of text `<p>` coming directly after a heading `<h2>`. The `:first-letter` pseudo-class allows us to apply styles very selectively to - as the name strongly indicates - the very first letter of the paragraph. In order to have text flowing nicely around the letter setting `float: right;` is necessary. The rest is simply adjusting font size and spacings to make it look nice.

```scss
section {
  h2 + p:first-letter {
    font-weight: 900;
    font-size: 1.6em;
    float: left;
    margin-right: 0.1em;
    margin-top: 0.2em;
    padding: 0em 0.1em;

    color:
      var(--background-color);
    
    
    @include mosaic;
  }
}
```

## Ruler Styling

The HTML `<hr>` element alows us to put a horizontal ruler for dividing e.g. paragraphs or any other elements from each other. 

```html
<hr>
```

So, here we'll be overriding the styling of the `<hr>` element to have a mosaic in its center tilted by 45 degrees. Again, we're using the `::after` pseudo-class to create the necessary element for the mosaic without requiring additional markup.

```scss
section {
  hr {
    --size: 1rem;
    
    width: 60%;
    height: 1px;
    border: none;
    background: var(--cc-s-2);

    &::after {
      content: '';
      display: inline-block;
      width: var(--size);
      height: var(--size);

      position: absolute;
      left: 50%;
      transform:
        translate(-50%, -50%)
        rotate(45deg);

      border: 4px solid var(--background-color);

      @include mosaic;
    }
  }
}
```

## Tombstone (you read that right)

A so called _tombstone_ appears at the end of e.g. an article letting the reader know, that the end was reached. The `:last-of-type` pseudo-class helps us to identify the last element of a given type, so e.g. the last paragraph of text in a section, which is exactly what we need. Now, all we have to do is to simply add another `::after` pseudo-class containing the mosaic gradients and being floated to the right. And done! 😎

```scss
section {
  p:last-of-type::after {
    --size: 0.5em;
    content: '';

    float: right;
    width: var(--size);
    height: var(--size);
    position: relative;
    top: 0.5em;

    @include mosaic;
  }
}
```