Doing it Wrong #1: Using CSS to Render Content
Tags: css, sass, dontdothis
Published: 2023-04-19 12:07:00

---
title: Doing it Wrong #1: Using CSS to Render Content
published: true
description: Using `content` to render content in your content
tags: CSS, Sass, dontdothis
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3yvgynvytgdmolbo09z8.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-19 12:07 +0000
canonical_url: https://elliotderhay.com/blog/doing-it-wrong-1-using-css-to-render-content
---

It should go without saying, if you’re adding blocks of written content to a page using CSS, you’re “doing it wrong”.

But since we’re just having fun here, why not? Also, I’ll be using Sass right from the beginning to save some keystrokes.

## Initial idea

At first I thought I’d just style a bunch of HTML tags with `content` and be done with it. That’s how that property works, right?

So this was more-or-less my initial solution:

```scss
.event {
  .title { content: "Awesome Regional Conference 2023" }
  .date { content: "5/21/2023" }
  .venue { content: "Totally Real Conference Hall" }
  .location { content: "123 Totally Real Blvd, Austin, Tx" }
}
```

And I thought it was done. Let’s throw this into [JSFiddle](https://jsfiddle.net/jsn1nj4/kxLphyf0/3/ "‌").

![Attempt 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wiqaq6fstkbakzp5z9nt.png)

## Wow, who else didn’t see that coming…? Really, only me…?

If you use `content` a lot, you probably saw this coming. I obviously don’t. So first I confirmed it was being set on the elements I expected it to be set on.

![Confirming styles are there](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3eq44zdcagpr5i9z0pf.png)

Then I started messing around with styles: margins, dimensions, font sizes and colors etc.

After scratching my head for a little while, I remembered that the only times I actually had used `content` were on `::before` and `::after`. So I [gave that a shot](https://jsfiddle.net/jsn1nj4/kxLphyf0/5/ "‌").

And look at that, it worked right away!

![It works!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7b0l3k4brdiq3ol11nmf.png)


I suppose it makes sense that `content` can’t work directly on the main content area of an HTML element. That would probably break its actual HTML and text content. In hindsight, I should’ve learned how `content` worked sooner when messing with `::before` and `::after` styles defined by tools like Bootstrap and FontAwesome.

## Taking it further

Ok, so now we’re rendering written content into HTML via CSS. How can this be a bit more reusable? With [CSS variables](https://jsfiddle.net/jsn1nj4/kxLphyf0/20/ "‌")!

![Using CSS vars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yab3tzzvp3tpgcyogy4h.png)

Awesome! So now we can generate styles for a specific event ID and it’ll work.

But we’re also making things a bit repetitive now. Imagine if you had 10-20 elements like this with matching vars. Blech.

## Mixins to the rescue!

Since Sass has mixins, this’ll help us clean up a bit more (I know, this is all wrong and messy anyway, but humor me). If we write a mixin name—say, `before`—we can automatically generate all of the classes and look for the right CSS vars, just with a list of strings.

```scss
@mixin before($labels...) {
    @for $i from 0 to length($labels) {
        .#{nth($labels, $i + 1)}::before { content: var(--#{nth($labels, $i + 1)}) }
    }
}
```

Using the `$var...` syntax in the mixin signature, we can condense all arguments to the mixin into 1 list.

If we had 2 params instead of 1, then it would work more like this:

```scss
@mixin before($param1, $param2...) {}

// here, $param1 gets the first argument, and $param2 gets all
// other arguments after that: "something else" and
// "something else again".
@include before("something", "something else", "something else again")
```

Then using the `@for` loop above combined with `length($labels)`, we can loop over the whole list. And using `nth`, we can use each string in the list as both the class selector and CSS var name.

### Some things to note about `@for` and `nth()`

You’ll notice the range in the loop is basically 0 to `length($labels)`. This works how you’d expect most `for` loops to work: start at `0`, stop when you reach `length`.

But lists in Sass are actually _indexed_ starting at `1`. So there is no “element 0”, and the last element’s index number is the same as the length of the list.

This forces us to do a little extra math in 1 of 2 places: the loop definition:

```scss
@for $i from 1 to length($labels) + 1 {}
```

… or the loop body, like above: `$i + 1`.

## Ahh, nice and clean

Here’s the working version of [this](https://jsfiddle.net/jsn1nj4/kxLphyf0/23/ "‌").

## But wait, there’s more…

What if we wanted to do something even crazier, like adding extra content into our HTML?

![Getting crazy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sd3gzbtfapiirs9gllc2.png)

Oh right, we’re using `::before`, so our CSS content is _literally_ before the actual HTML content.

Let’s switch that around a bit: rename the mixin and use the `::after` pseudo-element.

![Before and After](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gulss3rb883am75palxu.png)

And there it is, the [final version](https://jsfiddle.net/jsn1nj4/kxLphyf0/26/ "‌") of our backwards little CSS project.

## Pitfalls

This is a given, but there are problems with doing this unironically.

First, content set in `content` [does not exist in the DOM](https://developer.mozilla.org/en-US/docs/Web/CSS/content#accessibility_concerns "‌"). This makes getting the content back out very difficult and hurts accessibility.

Next, it adds overhead where none is needed. CSS is for changing the appearance of content, not _rendering_ content directly.

Lastly, along with that last point, it couples entire content placements and even parts of your frontend implementation to your stylesheet. Your frontend should not need to _use_ your stylesheet to render content when it’s already rendering everything else.

## What was learned?

For starters, This type of thing is insane to do on a serious project, so maybe _don't_—unless you do code golf. Do be crazy in code golf. :wink:

### More practically:

1. If you don't use `content` much (raising my hand :raised_hand:), you learned it doesn't do anything to HTML elements that actually have their own content.

2. _Why_ it doesn't affect HTML content is important: what it renders is outside the DOM, which can negatively impact your overall user experience, especially for accessibility.

**Thanks for reading!**