Generation Style by Eric Meyer

It’s time for the afternoon talks at An Event Apart in Seattle. We’re going to have back-to-back CSS, kicking off with Eric Meyer. His talk is called Generation Style. The blurb says:

Consider, if you will, CSS generated content. We can, and sometimes even do, use it to insert icons before or after pieces of text. Occasionally we even use it add a bit of extra information. And once upon a time, we pressed it into service as a hack to get containers to wrap around their floated children. That’s all fine—but what good is generated content, really? What can we do with it? What are its limitations? And how far can we push content generation in a new landscape full of flexible boxes, grids, and more? Join Eric as he turns a spotlight on generated content and shows how it can be a generator of creativity as well as a powerful, practical tool for everyday use.

Wish me luck, ‘cause I’m going to try to capture the sense of this presentation…

So we had a morning of personas and user journeys. This afternoon: code, baby! Eric is going to dive into a very specific corner of CSS—generated content. For an hour. Let’s do it!

He shows the CSS Generated Content Module Level 3. Eric wants to focus on one bit: the pseudo-elements ::before and ::after. What does pseudo-element mean?

You might have used one of these pseudo-elements for blockquotes. Perhaps you’ve put a great big quotation mark in front of them.

blockquote:: after {
    content: "“";
    font-size: 4em;
    opacity: 0.67;
/* placement styles here */
}

Why is Eric using ::after? Because you can. You can put the ::after content wherever you want. But if your placement styles fail, this isn’t a good place for the generated content. So don’t do this. Use ::before.

Another example of using generated content is putting icons beside certain links:

a[href$=".pdf"]::after {
    content: url(i/icon.png);
    height: 1em;
    margin-right: 0.5em;
    vertical-align: top;
}

But these icons look yucky. But if you use larger images, they will be shown full size. You only have so much control over what happens in there. I mean, that’s true of all CSS: think of CSS as a series of strong suggestions. But here, we have even less control than we’re used to. Why isn’t the image 1em tall like I’ve specified in the CSS? Well, the generated content box is 1em tall but the image is breaking out of this box. How about this:

a[href]::after * {
    max-width: 100%;
    max-height: 100%
}

This doesn’t work. The image isn’t an element so it can’t be selected for.

The way around it is to use background images instead:

a[href$=".pdf"]::after {
    content: '';
    height: 1em; width: 1em;
    margin-right: 0.5em;
    vertical-align: top;
    background: center/contain;
    background-image: url(i/icon.png);
}

Notice there’s a right margin there. That stretches out the width of the whole link. That’s exactly the same as if there were an actual span in there:

a[href$=".pdf"] span {
    height: 1em; width: 1em;
    margin-right: 0.5em;
    vertical-align: top;
    background: center/contain;
    background-image: url(i/icon.png);
}

So why use generated content instead of a span? So that you don’t have to put extra spans in your markup.

Generated content is great for things that work great when they’re there, but still work fine if they’re not. It’s progressive enhancement.

You’ve almost certainly used generated content for the clearfix hack.

.clearfix::after {
    content: '';
    display: table;
    clear: both;
}

Ask your parents. It’s when we wanted to make the containing element for a group of floating elements to encompass the height of those elements. Ancient history, right? Well, Eric is showing an example of a certain large media company today. There are a lot of clearfixes in there.

Eric makes the clearfix visible:

.clearfix::after {
    content: '';
    display: table;
    clear: both;
    border: 10px solid purple;
}

It looks like a span: a 10 pixel wide box. Now change the display property:

.clearfix::after {
    content: '';
    display: block;
    clear: both;
    border: 10px solid purple;
}

Now it behaves more like a div than a span.

The big question here is: who cares?

Let’s say we’re making a site about corduroy pillows (I hear they’re really making headlines).

<header>
<h1>Corduroy pillows</h1>
<p>Lorum ipsum...</p>
</header>

We can add a box under the header:

header::after {
    content: " ";
    display: block;
    height: 1em;
}

You can do stuff with that extra content, like using a linear gradient:

header::after {
    content: " ";
    display: block;
    height: 1em;
    background: linear-gradient(to right, #DDD, #000, #DDD) center / 100% 1px no-repeat;
}

The colour stops are #DDD, #000, and #DDD. You get this nice gradiated line under the header. You can chain a bunch of of radial gradients together to get some nice effects. You could mix in some background images too. Now you’ve got some on-brand separators. You could use generated content to add some “under construction” separators.

By the way, ever struggled to keep track of the order of backgrounds? Think about how you would order layers in Photoshop.

How about if we could use generated content to make design tools?

div[id]::before {
    content: attr(id);
}

Now the generated content is taken from the id attribute. You can make it look like Firebug:

div[id]::before {
    content: '#' attr(id);
    font: 0.75rem monospace;
    position: absolute;
    top: 0;
    left: 0;
    border: 1px dashed red;
    padding: 0 0.25em;
    background: #FFD;
}

You can even make the content cover the whole box with bottom and right values too:

div[id]::before {
    content: '#' attr(id);
    font: 0.75rem monospace;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    border: 1px dashed red;
    padding: 0 0.25em;
    background: #FFD8;
}

(And yes, that is a hex value with opacity.)

Let’s make it less code-y:

div[id]::before {
    content: attr(id);
    font: bold 1.5rem Georgia serif;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    border: 1px dashed red;
    padding: 0 0.25em;  
    background: #FFD8;
}

Throw in some text-shadow. Maybe some radial gradients. We’re at the wireframe stage. Let’s drop in some SVG images to show lines across the boxes.

How about automating design touches?

pre {
    padding: 0.75em 1.5em;
    background: #EEE;
    font: medium Consolas, monospace;
    position: relative;
}

Let’s say that applies to:

<pre class="css">
...
</pre>

You can generate labels with that class attribute:

pre::before {
    content: attr(class);
    display: block;
    padding: 0.25em 0 0.15em;
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

Let’s align it to the top of it’s parent with negative margins:

pre::before {
    content: attr(class);
    display: block;
    padding: 0.25em 0 0.15em;
    margin: -0.75em -1.5em 1em;
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

Or you can use absolute positioning:

pre::before {
    content: attr(class);
    display: block;
    padding: 0.25em 0 0.15em;
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

Now let’s change the writing mode:

pre::before {
    content: attr(class);
    display: block;
    padding: 0.25em 0 0.15em;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    writing-mode: vertical-rl;
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

Now the text is running down the side, but it’s turned on its side. You can transform it:

pre::before {
    content: attr(class);
    display: block;
    padding: 0.25em 0 0.15em;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

But if you this, be careful. Your left margin is no longer on the left. Everything’s flipped around.

You could also update the generated content according to the value of the class attribute:

pre.css:: before {
    content: '{ CSS }';
}

pre.html::before {
    content: '< HTML >';
}

pre.js::before,
pre.javascript::before {
    content: '({ JS })();';
}

It’s presentational, so CSS feels like the right place to do this. But you can’t generate markup—just text. Angle brackets will be displayed in their raw form.

But positioning is so old-school. Let’s use CSS grid:

pre {
    display: grid;
    grid-template-columns: min-content 1fr;
    grid-gap: 0.75em;
}

pre::before {
    content: attr(class);
    margin: -1em 0;
    padding: 0.25em 0.1em 0.25em 0;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font: bold 1em Noah, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

Heck, you could get rid of the negative margins by putting the code content inside a code element and giving that a margin of 1em.

You can see generated content in action on the website of An Event Apart:

li.news::before {
    content: attr(data-cat);
    background-color: orange;
    color: white;
}

The data-cat attribute (which contains a category value) is displayed in the generated content.

Cool. That’s all stuff we can do now. What about next?

Well, suppose you had to put some legalese on your website. You could generate the numbers of nested sections:

h1 { counter-reset: section; }
h2 { counter-reset: subsection; }

Increment the numbers each time:

h2 { counter-increment: section; }
h3 { counter-increment: subsection; }

And display those values:

h2::before {
    content: counter(section) ".";
}
h2::before {
    content: counter(section) counter ":" (subsection, upper-roman);
}

Soon you’ll be able to cycle through a list of counter styles of your own creation with a @counter-style block.

But remember, if you really need that content to be visible for everyone, don’t rely on generated content: put it in your markup. It’s for styles.

So, generated content. It’s pretty cool. You can do some surprising things with it. Maybe ::before this talk, you didn’t think about generated content much, but ::after this talk ,you will.

Have you published a response to this? :

Responses

4 Shares

# Shared by An Event Apart on Tuesday, March 5th, 2019 at 5:30am

# Shared by zeldman on Tuesday, March 5th, 2019 at 5:31am

# Shared by Todd on Tuesday, March 5th, 2019 at 12:35pm

# Shared by Shaun Rashid on Tuesday, March 5th, 2019 at 2:59pm

20 Likes

# Liked by Scott Gruber on Tuesday, March 5th, 2019 at 1:54am

# Liked by Craig "subscribe to my newsletters" Mod on Tuesday, March 5th, 2019 at 1:54am

# Liked by Corina Rudel on Tuesday, March 5th, 2019 at 1:54am

# Liked by Eric Wallace on Tuesday, March 5th, 2019 at 1:54am

# Liked by Brianna Kirkland on Tuesday, March 5th, 2019 at 2:53am

# Liked by John F Croston III on Tuesday, March 5th, 2019 at 4:22am

# Liked by Ally on Tuesday, March 5th, 2019 at 4:46am

# Liked by zeldman on Tuesday, March 5th, 2019 at 5:49am

# Liked by Pat Ramsey on Tuesday, March 5th, 2019 at 5:49am

# Liked by Dallas Reedy on Tuesday, March 5th, 2019 at 5:49am

# Liked by 🍎 🖥 🔥 🐣 on Tuesday, March 5th, 2019 at 6:21am

# Liked by cincylee on Tuesday, March 5th, 2019 at 7:12am

# Liked by Clearleft on Tuesday, March 5th, 2019 at 7:12am

# Liked by Natalie Chin on Tuesday, March 5th, 2019 at 11:58am

# Liked by Jeremy Pinnix on Tuesday, March 5th, 2019 at 12:30pm

# Liked by Todd on Tuesday, March 5th, 2019 at 1:01pm

# Liked by Shaun Rashid on Tuesday, March 5th, 2019 at 3:20pm

# Liked by Letra Studio on Tuesday, March 5th, 2019 at 5:17pm

# Liked by Matthias Ott on Wednesday, March 6th, 2019 at 11:25pm

# Liked by lvdesign on Thursday, March 7th, 2019 at 3:37am

2 Bookmarks

# Bookmarked by https://vincentp.me on Thursday, March 7th, 2019 at 11:03pm

# Bookmarked by https://vincentp.me on Thursday, March 7th, 2019 at 11:03pm

Related posts

The State of the Web — the links

A collection of hyperlinks for a talk.

Done

The bittersweet feeling of finishing something you’ve been working on for quite a while.

Preparing an online conference talk

Less like preparing to give a speech and more like making a short film.

Design Principles For The Web—the links

Hyperlinks to accompany a talk.

The Technical Side of Design Systems by Brad Frost

A presentation at An Event Apart San Francisco 2019

Related links

“Evaluating Technology” by Jeremy Keith – An Event Apart video on Vimeo

This is a recording of my Evaluating Technology talk from An Event Apart in Denver just over a year ago. This was the last time I ever gave this talk, and I think you can tell that the delivery is well-practiced; I’m very happy with how this turned out.

In this 60-minute presentation recorded live at An Event Apart Denver 2017, Jeremy Keith helps you learn to evaluate tools and technologies in a way that best benefits the people who use the websites you design and develop.

Tagged with

A Redesign with CSS Shapes · An A List Apart Article

Eric walks through a really nice use of CSS shapes and @supports on a page of the An Event Apart site.

It’s a nice little illustration of how we can use advanced features of CSS right now, without the usual wait for widespread support.

Tagged with

The Long Web by Jeremy Keith – An Event Apart Video on Vimeo

This is a talk I gave at An Event Apart about eighteen months ago, all about irish music, the web, long-term thinking, and yes, you guessed it—progressive enhancement.

Tagged with

Ethan Marcotte AEA Boston June 18, 2012 on Vimeo

Ethan’s excellent talk from last year’s An Event Apart.

In this session Ethan reviews strategies for handling trickier elements that would make even the most seasoned designer quail: stuff like advertising, complex layouts, deep navigation patterns, third-party media, and, yes, actual, honest-to-goodness content.

Tagged with

CSS { In Real Life } | Thoughts From CSS Day

It’s clear that companies don’t value CSS skills in the same way as, say Javascript — which is reflected in pay disparity, bootcamp priorities, and the lack of visibility in job descriptions. It’s not uncommon to see front end job specifications listing React, Redux, Typescript and more, with barely a passing mention of HTML and CSS, despite being core web technologies. New developers are encouraged to learn just enough CSS to get by, rather than cultivate a deep knowledge and appreciation for the language, and that’s reflected in the messy, convoluted code, riddled with bad practices, that many of us have to clear up afterwards.

Tagged with

Previously on this day

12 years ago I wrote Responsive audio out

All the talks are available for your listening and huffduffing pleasure.

13 years ago I wrote Remembering Ralph McQuarrie

A visionary.

13 years ago I wrote What do I know?

Five things I learned from the internet.

14 years ago I wrote American Odyssey

Brighton to Illinois to Texas to Florida to Oregon to Washington to Brighton.

16 years ago I wrote £5 slides

Talking about Huffduffer.

18 years ago I wrote Watching the stream

This life stream idea has legs. Also: why Yahoo Pipes and Ning won’t be duking it out.

19 years ago I wrote Adactio Austin

Party like it’s SXSW06

22 years ago I wrote Celebrity Hair

A transcript of part of an iChat conversation with Jamie over at Message: