Emoji lists

February 05, 2024 | Work: 2024-01

Client sends this copy:

With the request:

Is it possible to make the animal emoji be the list bullets?

Let’s find out.

Ye Olde Waye

The way we used to do this was by omitting the bullets entirely and then putting the content before. Traditionally this was done to get lists with cool angled-brackets like this:

ul {
  list-style: none;
}
ul li {
  margin: 0;
  padding: 0;
}
ul li::before {
  content: "Β»";
  padding-right: 1rem;
}
mk 0
  • Cool
  • Angled
  • brackets

We could do this.

  <ul>
    <li>Dive right into an animal adventure!</li>
    <li>Pick up the game's intuitive mechanics in minutes</li>
    <li>Get everything you need to play in one box</li>
    <li>Take control of a strangely familiar world</li>
    <li>Turn eco-anxiety into a story with friends</li>
  </ul>
ul {
  list-style: none;
}
ul li {
  margin: 0;
  padding: 0;
}
ul li::before {
  padding-right: 1rem;
}
ul li:nth-of-type(1)::before {
  content: "🐴";
}
ul li:nth-of-type(2)::before {
  content: "🐷";
}
ul li:nth-of-type(3)::before {
  content: "🐼";
}
ul li:nth-of-type(4)::before {
  content: "🐸";
}
ul li:nth-of-type(5)::before {
  content: "🐯";
}
mk 1
  • Dive right into an animal adventure!
  • Pick up the game's intuitive mechanics in minutes
  • Get everything you need to play in one box
  • Take control of a strangely familiar world
  • Turn eco-anxiety into a story with friends
  • (demoing 6th item)

Not bad! Notice that the 6th item has nothing. We could do some CSS math to make it cycle indefinitely; maybe that works for the client, maybe it doesn’t. I’d like a bit more control over it though.

Let’s first re-work it to be more explicit.

<ul>
  <li data-bullet="🐴">Dive right into an animal adventure!</li>
  <li data-bullet="🐷">Pick up the game's intuitive mechanics in minutes</li>
  <li data-bullet="🐼">Get everything you need to play in one box</li>
  <li data-bullet="🐸">Take control of a strangely familiar world</li>
  <li data-bullet="🐯">Turn eco-anxiety into a story with friends</li>
  <li data-bullet="🐼">(demoing 6th item)</li>
</ul>
ul li::before {
  padding-right: 1em;
}
ul li[data-bullet="🐴"]::before {
  content: "🐴";
}
ul li[data-bullet="🐷"]::before {
  content: "🐷";
}
ul li[data-bullet="🐼"]::before {
  content: "🐼";
}
ul li[data-bullet="🐸"]::before {
  content: "🐸";
}
ul li[data-bullet="🐯"]::before {
  content: "🐯";
}
mk 2
  • Dive right into an animal adventure!
  • Pick up the game's intuitive mechanics in minutes
  • Get everything you need to play in one box
  • Take control of a strangely familiar world
  • Turn eco-anxiety into a story with friends
  • (demoing 6th item)

I think in my actual implementation I used classes instead of data-bullet, so my CSS looked more like this:

<li class="🐯">...</li>
ul li.🐯::before {
  content: "🐯";
}

Technically this is OK. Emoji are just UTF-8 characters that are rendered in a particular way by text-renderers that support the Emoji charset. They’re actually really neat, and there is far more technical stuff there than I had thought there would be.

This is still a pseudo-bullet though. It’s visually shippable. But what if we wanted to get our semantic 🌟?

Teh NΓΌ WΓ€y

Enter the marker pseudo-element.

The ::marker CSS pseudo-element selects the marker box of a list item, which typically contains a bullet or number. It works on any element or pseudo-element set to display: list-item, such as the <li> and <summary> elements.

Basically, we swap out ::before for ::marker.

ul li {
  padding-left: 1em;
}
ul li[data-bullet="🐴"]::marker {
  content: "🐴";
}
ul li[data-bullet="🐷"]::marker {
  content: "🐷";
}
ul li[data-bullet="🐼"]::marker {
  content: "🐼";
}
ul li[data-bullet="🐸"]::marker {
  content: "🐸";
}
ul li[data-bullet="🐯"]::marker {
  content: "🐯";
}
mk 3
  • Dive right into an animal adventure!
  • Pick up the game's intuitive mechanics in minutes
  • Get everything you need to play in one box
  • Take control of a strangely familiar world
  • Turn eco-anxiety into a story with friends
  • (demoing 6th item)

One key, but subtle, difference is the change from this:

ul li::before {
  padding-right: 1em;
}

to this:

ul li {
  padding-left: 1em;
}

This is because we’re now returning the list to it’s normal behavior and just replacing the bullet being used.

In fact, we don’t even have to do a reset on the lists at all. The default spacing for lists is:

The <ul> and <ol> elements have a top and bottom margin of 16px (1em) and a padding-left of 40px (2.5em).

We can do our padding and margins normally, as if we were using a standard disc (the default) marker.

Caveat: browser support

The ::marker pseudoelement is unfortunately still pretty new. So we want to play it safe by using the supports() CSS query.

ul li {
  padding-left: 1em;
}
@supports selector(::marker) {
  ul li[data-bullet="🐴"]::marker {
    content: "🐴";
  }
  ul li[data-bullet="🐷"]::marker {
    content: "🐷";
  }
  ul li[data-bullet="🐼"]::marker {
    content: "🐼";
  }
  ul li[data-bullet="🐸"]::marker {
    content: "🐸";
  }
  ul li[data-bullet="🐯"]::marker {
    content: "🐯";
  }
}
@supports not selector(::marker) {
  ul li[data-bullet="🐴"]::before {
    content: "🐴";
  }
  ul li[data-bullet="🐷"]::before {
    content: "🐷";
  }
  ul li[data-bullet="🐼"]::before {
    content: "🐼";
  }
  ul li[data-bullet="🐸"]::before {
    content: "🐸";
  }
  ul li[data-bullet="🐯"]::before {
    content: "🐯";
  }
}
mk 4
  • Dive right into an animal adventure!
  • Pick up the game's intuitive mechanics in minutes
  • Get everything you need to play in one box
  • Take control of a strangely familiar world
  • Turn eco-anxiety into a story with friends
  • (demoing 6th item)

For most users, this will look exactly the same. If it looks different, your browser might be one that doesn’t support the ::marker property and one of the earlier examples might look incorrect!