On Type Patterns and Style Guides
Publikováno: 19.1.2021
Over the last six years or so, I’ve been using these things I’ve been calling “type patterns” in my web design work, and they’ve worked out pretty well for me. I’ll dig into what they are and how they can …
The post On Type Patterns and Style Guides appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.
Over the last six years or so, I’ve been using these things I’ve been calling “type patterns” in my web design work, and they’ve worked out pretty well for me. I’ll dig into what they are and how they can make their way into CSS, and maybe the idea will click with you too and help with your day-to-day typography needs.
If you’ve used print design desktop software like QuarkXPress, Adobe InDesign, or CorelDraw, then imagine this idea is an HTML/CSS translation of “paragraph styles.”
When designing a book (that spans hundreds of pages), you might want to change something about the heading typography across the whole book (on the fly). You would define how a certain piece of typography behaves in one central place to be applied across the entire project (a book, in our example). You need control of the patterns.
Most programs use this naming style, but their user interfaces vary a little.
When you pull up the pane, there’s usually a “base” paragraph style that all default text belongs to. From there, you can create as many as you want. Paragraph styles are for “block” level-like elements, and character styles are for “inline” level-like elements, such as bold or unique spans.
The user interface specifics shouldn’t matter — but you can see that there are a lot of controls to define how this text behaves visually. Under the hood, it’s just key: value pairs, which is just like CSS property: value pairs
h1 {
font-family: "Helvetica Neue", sans-serif;
font-size: 20px;
font-weight: bold;
color: fuchsia;
}
Once defined, the styles can be applied to any piece of text. The little +
(next to the paragraph style name below) in this case, means that the style definition has changed.
If you want to apply those changes to everything with that paragraph style, then you can “redefine” the style and it will apply project-wide.
When I say it like that, it might make you think: that’s what a CSS class is.
But things are a little more complicated for a website. You never know what size your website will be displayed at (it could be small, like on a mobile device, or giant, like on a desktop monitor, or even on monochrome tablet, who knows), so we need to be contextual with those classes and have then change based on their context.
The bare minimum of typographic control
In your very earliest days as a developer, you may have been introduced to semantic HTML, like this:
<h1>Here's some HTML stuff. I'm a heading level 1</h1>
<p>And some more. I'm a paragraph.</p>
<h2>This is a heading level 2</h2>
<p>And some more pragraph stuff.</p>
And that pairs with CSS that targets those elements and applies styles, like this:
h1 {
font-size: 50px; /* key: value pairs */
color: #ff0066;
}
h2 {
font-size: 32px;
color: rgba(0,0,0,.8);
}
p {
font-size: 16px;
color: deepskyblue;
line-height: 1.5;
}
This works!
You can write rules that target the headers and style them in descending order, so they are biggest > big > medium, and so on.
Headers also already have some styles that we accept as normal, thanks to User Agent styles (i.e. default styles applied to HTML by the browser itself). They are meant to be helpful. They add things like font-weight
and margin
to the headers, as well as collapsing margins. That way — without any CSS at all — we can rest assured we get at least some basic styling in place to establish a hierarchy. This is beginner-friendly, fallback-friendly… and a good thing!
As you build more complex sites, things get more complicated
You add more pages. More modules. More components. Things start to get more complex. You might start out by adding unique classes and styles for every single little thing, but it’ll catch up to you.
First, you start having classes for special situations:
<h1 class="page-title">
Be <span class='super-ultra-magic-rainbow'>excellent</span> to each other
</h1>
<p class="special-mantra">Party on, <em>dudes</em>.</p>
<p>And yeah. I'm just a regular paragraph.</p>
Then, you start having classes everywhere (most CSS methodologies even encourage this):
<header class="site-header">
<h1 class="page-title">
Be <span class='ultra-magic-rainbow'>excellent</span> to each other
</h1>
</header>
<main class="page-content">
<section class="welcome">
<h2 class="special-mantra">Party on <em>dudes</em></h2>
<p class="regular-paragraph">And yeah. I'm just regular</p>
</section>
</main>
Newcomers will try and work around the default font sizes and collapsing margins if they don’t feel confident “resetting”them.
This is where people start trying out margin-top: -20px
… and then stuff gets whacky. As you keep writing more rules to wrangle things in, it will feel like you are “fixing” things instead of declaring how you actually want them to work. It can quickly feel like you are “fighting” the CSS cascade when you’re unaware of the browser’s User Agent styles.
A real-world example
Imagine you’re at your real job and your boss (or the visual designer) gives you this “pixel perfect” Adobe Photoshop document. There are a bunch of colors, layout, and typography.
You open up Photoshop and start to poke around, but there are so many pages and so many different type styles that you’ll need to take inventory and gather what styles can be combined or used in combination.
It feels great to finish a visual layout and hand it off. However, I can’t tell you how many full days I’ve spent trying to figure out what the heck is happening in a Photoshop document. For example, sometimes small-screens aren’t taken into consideration at all; and when they are, the patterns you find aren’t always shared by each group as they change for different screen types. Some fonts start at 16px and go up to 18px, while others go up to 19px and become italic. How can spot context changes in a static mockup?
Sometimes this is with fervent purpose; other times the visual designer is just going on feel and is happy to round things up and down to create reusable patterns. You’ll have to talk to them about it. But this article is advocating that we talk about it muchearlier in the process.
You might get a style guide to reference. But even that might not be specific enough to identify contexts.
Let’s zoom in on one of those guidelines:
You may even get a formal, but generic, style guide with no pixel sizes or notes on varying screen sizes at all!
Don’t get me wrong: this sort of thing is defintely a nice thought, and it might even be useful for others, like in some client meeting or something. But, as far as front-end development goes, it’s more confusing than helpful. I’ve received very thorough style guides that looked nice and gave lots of excellent guidelines for things like font sizes, but are completely mismatched with the accompanying Photoshop document. On the other end of the spectrum, there are style guides that have unholy amounts of specifics for every type of heading and combination you could ever imagine — and more.
It’s hard to parse this stuff, even with the best of intentions!
Early in your development career, you’d probably assume it’s your job to “figure it out” and get to work, writing down all the pixels and trying your best to make sense of it. Go get ‘em!
But, as you start coding all the specifics, things can get a little overwhelming with the amount of duplication going on. Just look at all the repeated properties going on here:
.blog article p {
font-family: 'Georgia', serif;
font-size: 17px;
line-height: 1.4;
letter-spacing: 0.02em;
margin-bottom: 10px;
}
.welcome .main-message {
font-family: 'Georgia', serif;
font-size: 17px;
line-height: 1.4;
letter-spacing: 0.02em;
margin-bottom: 20px;
}
@media (min-width; 700px) {
.welcome .main-message {
font-size: 18px;
}
}
.welcome .other-thing {
font-family: 'Georgia', serif;
font-size: 17px;
line-height: 1.4;
letter-spacing: 0.02em;
margin-bottom: 20px;
}
.site-footer .link list a {
font-family: 'Georgia', serif;
font-size: 17px;
line-height: 1.4;
letter-spacing: 0.02em;
margin-bottom: 20px;
}
You might take the common declarations and apply them to the body instead. In smaller projects, that might even be a good way to go. There are ways to use the cascade to your advantage, and others that seem to tie too many things together. Just like in an Object Oriented programming language, you don’t necessarily want everything inheriting everything.
body {
font-family: 'Georgia', serif;
font-size: 17px;
line-height: 1.4;
letter-spacing: 0.02em;
}
Things will work out OK. Most of the web was built like this. We’re just looking for something even better.
Dealing with design revisions
One day, there will be a meeting. In that meeting, you’ll find out that the client and the visual designer decided to change a bunch of the typography. Now you need to go back and change it in 293 places in the CSS file. If you get paid by the hour, that might be great!
As you begin to adjust the rules, things will start to conflict. That rule that worked for two things now only works for the one. Or you’ll notice patterns that could now be used in many more places than before. You may even be tempted to just totally delete the CSS and start over! Yikes!
I won’t write it out here, but you’ll try a bunch of different things over time, and people usually come to the conclusion that you can create a class — and add it to the element instead of duplicating rules/declarations for every element. You’ll go even further to try and pull patterns out of the visual designer’s documents. (You might even round a few 19px down to 18px without telling them…)
.standard-text { /* or something */
font-family: serif;
font-size: 16px; /* px: up for debate */
line-height: 1.4; /* no unit: so it's relative to the font-size */
letter-spacing: 0.02em; /* em: so it's relative to the font-size */
}
.heading-1 {
font-family: sans-Serif;
font-size: 30px;
line-height: 1.5;
letter-spacing: 0.03em;
}
.medium-heading {
font-family: sans-Serif;
font-size: 24px;
line-height: 1.3;
letter-spacing: 0.04em;
}
Then you’d apply the class to anything that needs it.
<header class="site-header">
<h1 class="page-title heading-1">
Be <mark>excellent</mark> to each other
</h1>
</header>
<main class="page-content">
<section class="welcome">
<h2 class="medium-heading">Party on <em>dudes</em></h2>
<p class="standard-text">And yeah. I'm just regular</p>
</section>
</main>
This way of doing things can be really helpful for teams that have people of all skill levels changing the HTML. You can plug and play with these CSS classes to get the style you want, even if you’re the new intern.
It’s really helpful to separate the idea of “layout” elements (structure/parents) and the idea of “modules” or “components.” In this way, we’re treating the pieces of text as lower level components.
The key is to keep the typography separate from the layout. You don’t want all.medium-heading
elements to have the same margins or colors. It’s going to depend on where they are. This way you are styling based on context. You aren’t ‘using’ the cascade necessarily, but you also aren’t fighting it because you keep the techniques separate.
.site-header {
padding: 20px 0;
}
.welcome .medium-heading { /* the context — not the type-pattern */
margin-bottom: 10px;
}
This is keeping things reasonable and tidy. This technique is used all over the web.
Working with a CMS
Great, but what about situations where you can’t change the HTML?
You might just be typing up a quick CodePen or a business-card site. In that case, these concerns are going to seem like overkill. On the other hand, you might be working with a CMS where you aren’t sure what is going to happen. You might need to plan for anything and everything that could get thrown at you. In those cases, you’re unable to simply add classes to individual elements. You’re likely to get a dump of HTML from some templating language.
<?php echo getContent()?>
<?=getContent()?>
${data.content}
{{model.cmsContent}}
So, if you can’t work with the HTML what can you do?
<article class="post cms-blog-dump">
<h1>Talking type-patterns on CSS-tricks</h1>
<p>Intoduction paragraph - and we'd like to style this with a slightly different size font then the next (normal) paragraphs</p>
<h2>Some headings</h2>
<h2>And maybe someone accidentally puts 2 headings in a row</h2>
<ol>
<li>and some <strong>list</strong></li>
<li>and here</li>
</ol>
<p>Or if a blog post is too boring - then think of a list of bands on an event site. You never know how many there will be or which ones are headlining, so you have to write rules that will handle whatever happens.
</article>
You don’t have any control over this markup, so you won’t be able to add classes, meaning that the cool plug-and-play classes you made aren’t going to work! You might just copy and paste them into some larger .article { }
class that defines the rules for a kitchen sink. That could work.
What other tools are there to play with?
Mixins
If you could create some reusable concept of a “type pattern” with Sass, then you could apply those in a similar way to how the classes work.
@mixin my-useful-rule { /* define the mixin */
background-color: blue;
color: lime;
}
.thing {
@include my-useful-rule(); /* employ the mixin */
}
/* This compiles to: */
.thing {
background-color: blue;
color: lime;
}
/* (just so that we're on the same page) */
Less, Sass, Stylus and other CSS preprocessors all have their own syntax for this. I’m going to use Sass/SCSS in these examples because it is the most common at the time of writing.
@mixin standard-type() { /* define the mixin */
font-family: Serif;
font-size: 16px;
line-height: 1.4;
letter-spacing: 0.02em;
}
.context .thing {
@include standard-type(); /* employ it in context */
}
You can use heading-1()
and heading-2()
and a lot of big-name style guides do this. But what if you want to use those type styles on something that isn’t a “heading”? I personally don’t end up connecting the headings with “size” and I use the patterns in all sorts of different places. Sometimes my headings are “mean” and “stout.” They can be piercing red and uppercase with the same x-height as the paragraph text.
Instead, I define the visual styles in association with how I want the “voice” of the content to come across. This also helps the team discuss “tone” and other content strategy stuff across disciplines.
For example, in Jason Santa Maria’s book, On Web Typography, he talks about “type for a moment” and “type to live with.” There’s type to grab your attention and break up the page, and then those paragraphs to settle into. Instead of .standard-text
or .normal-font
, I’ve been enjoying the idea of organizing styles by voice. This is all that type that a user should spend time consuming. It’s what I’d likely use for most paragraphs and lists, and I won’t set it on the body.
@mixin calm-voice() { /* define the mixin */
font-family: Serif;
font-size: 16px;
line-height: 1.4;
letter-spacing: 0.02em;
}
@mixin loud-voice() {
font-family: Sans-Serif;
font-size: 30px;
line-height: 1.5;
letter-spacing: 0.03em;
}
@mixin attention-voice() {
font-family: Sans-Serif;
font-size: 24px;
line-height: 1.3;
letter-spacing: 0.04em;
}
This idea of “voices” helps me keep things meaningful because it requires you to name things by the context. The name heading-1b
, for example, doesn’t help me connect to the content to any sort of storytelling or to the other people on the team.
Now to style the mystery article. I’ll be using SCSS syntax here:
article {
padding: 20px 0;
h1 {
@include loud-voice();
margin-bottom: 20px;
}
h2 {
@include attention-voice();
margin-bottom: 20px;
}
p {
@include calm-voice();
margin-bottom: 10px;
}
}
Pretty, right?
But it’s not that easy, is it? No. It’s a little more complicated because you don’t know what might be above or below each other — or what might be left out, because articles aren’t always structured or organized the same. Those CMS authors can put whatever they want in there! Three <h3>
elements in a row? You have to prepare for lots of possible outcomes.
/* Styles */
article {
padding: 20px 0;
h1 {
@include loud-voice();
}
h2 {
@include attention-voice();
}
p {
@include calm-voice();
&:first-of-type {
background: lightgreen;
padding: 1em;
}
}
ol {
@include styled-ordered-list();
}
* + * {
margin-top: 1em
}
}
To see the regular CSS you can always “View Compiled” in CodePen.
Some people are really happy with the lobotomized owl approach (* + *
) but I usually end up writing explicit rules for “any <h2>
that comes after a paragraph” and getting really detailed. After all, it’s the written content that everyone wants to read… and I really only need to dial it in one time in one place.
/* Slightly more filled out pattern */
@mixin calm-voice() {
font-family: serif;
font-size: 16px;
line-height: 1.4;
letter-spacing: 0.02em;
max-width: 80ch;
strong {
font-weight: 700;
}
em {
font-style: italic;
}
mark {
background-color: wheat;
}
sup {
/* maybe? */
}
a {
text-decoration: underline;
color: $highlight;
}
@media (min-width: 600px) {
font-size: 17px;
}
}
Stylus
It’s nice to think about the “ideal” workflow. What could browsers implement that would make this fun and play well with their future systems?
Here’s an example of the most stripped down preprocessor syntax:
calm-voice()
font-family: serif
font-size: 16px
line-height: 1.4
letter-spacing: 0.02em
article
h1
loud-voice()
h2
attention-voice()
p
calm-voice()
I’ll be honest… I love Stylus. I love writing it, and I love using it for examples. It keeps people on their toes. It’s so fast to type in CodePen! If you already have your own little framework of styles like this, it’s insane how fast you can build UI. But! The tooling has fallen behind and at this point, I can’t recommend that anyone use it.
I only add it here because it’s important to dream. We have what we have, but what if you could invent a new way of writing it? That’s a crucial part of the conversation too. If you can’t describe what you want, you wont get it.
We’re here: Type Patterns
Can you see where all of this is going?
You can use these “patterns” or “mixins’ or “whatever” you want to call them and plug and play. It’s fun. And you can totally combine it with the utility class idea too (if you must).
.calm-voice {
@include calm-voice();
}
<p class="calm-voice">Welcome to this code snippet!</p>
Style guides
If you can start to share a common language and break down the barriers between “creatives” and “coders,” then everyone can work with these type patterns in mind from the start.
Sometimes you can simply publish a style guide as a “brand” subdomain or directly on the site, like at /style-guide
. There are tons of these floating around on the web. The point is that some style guides are standalone, and others are built into the site itself. Wherever they go, they are considered “live” and they allow you to develop things in one place that take effect globally, and use the guide itself as a sort of artifact.
By building live style guides with type patterns as a core and shared concept, everyone will have more fun and save time trying to figure out what each other means. It’s not just for big companies either.
Just be mindful when looking at style guides for other organizations. Style guides serve different purposes depending on who is using them and how, so simply diving into someone else’s work could actually contribute more confusion.
Known unknowns
Sometimes you don’t know what the content will be. That means CMS stuff, but also logic. What if you have a list of bands and events and you are building a module full of conditional components? There might be one band… or five… or two co-headliners. The event might be cancelled!
When I was trying to work out some templating ideas for Ticketfly, I separated the concerns of layout and type patterns.
Variable sized font patterns
Some patterns change sizes at various breakpoints.
@mixin attention-voice() {
font-family: Serif;
font-size: 20px;
line-height: 1.4;
letter-spacing: 0.02em;
@media (min-width: 700px) {
font-size: 24px;
}
@media (min-width: 1100px) {
font-size: 30px;
}
}
I used to do something like this and it had a few yucky side effects. For example, what if you plug and play based on the breakpoints and there are sizes that conflict or slip through?
clamp()
and vmin
units to the rescue!
@mixin attention-voice() {
font-family: Serif;
font-size: clamp(18px, 10vmin, 100px);
line-height: 1.4;
letter-spacing: 0.02em;
}
Now, in theory, you could even jump between the patterns with no rub.
.context {
@include calm-voice();
@media (min-width: 840px) {
@include some-other-voice();
}
}
But now you have to make sure that they both have the same properties and that they don’t conflict with or bleed through to others! And think about the new variable font options too. Sometimes you’ll want your heading to be a little less heavy on smaller screens or a little taller to work with the space.
Aren’t you duplicating style rules all over the place?
Yikes! You caught me. I care way more about making the authoring and maintaining process pleasurable than I care about CSS byte size. I’m conflicted though. I get it. But I also think that the solution should happen somewhere else in the pipeline. There’s a reason that we don’t write machine code by hand.
Sometimes you have to examine a programming pattern closely and really poke at it, trying it in every place you can to prove how useful it is. Humor me for a movement and look at how you’d use this type pattern and other mixins to style a common “card” interface.
In a way, type patterns are like the utility class style of Bootstrap or Tailwind. But these are human readable. The patterns are added in the CSS instead of the HTML. The concept is the same. I like to think that anyone could look at the living style guide and jump into a component and style it. What do you think?
It is creating more CSS though. Those kilobytes are stacking up. But I think we should work towards something ideal instead of just “possible.” We can probably build something that works this out at build time. I’ll have to learn more about the CSSOM and there are many new things coming down the pipeline that could make this possible without a preprocessor.
It’s bigger than just the CSS. It’s about the people.
Having a set of patterns for type in your project allows visual designers to focus on their magic. More and more, we are building fast and in the browser. Visual designers focus on feel and typography and color with simple frameworks, like Style Tiles. Then developers can organize the data, resource structures and layouts, and everyone can work at the same time. We can have clearer communication and shared understanding of the entire process. We are all UX designers.
When living style guides are created as a team, there’s a lot less need for pixel-pushing. Visual designers can actually spend more time thinking and trying ideas in prototypes, and less time mocking out unnecessary production art. This allows you to work on your brand as a whole and have one single source of truth. It even helps with really small sites, like this.
InDesign and Illustrator have always had “paragraph styles” and “character styles” for this reason, but they don’t take into account variable screen sizes.
Toss in a few padding type sizes/ratios, some colors and some line widths. It doesn’t have to really be “pixel perfect” but more of a collection of patterns that work together. Colors as variables and maybe some $thick
, $thin
, or $pad*2
type conventions can help streamline design patterns.
You can find your inspiration in the graphics program, then jump straight to the live style guide. People of all backgrounds can start playing with the styles in a CodePen and dial them in across devices.
In the end, you’ll decide the details on real devices — together, as a team.
The post On Type Patterns and Style Guides appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.