How do we avoid the most common mistakes when it comes to setting type on the web? That’s the question that’s been stuck in my head lately as I’ve noticed a lot of typography that’s lackluster, frustrating, and difficult to read. So, how can we improve interfaces so that our content is easy to read at all times and contexts? How do learn from everyone else’s mistakes, too?
These questions encouraged me to jot down some rules that are easy to apply and have the greatest impact on legibility, based on my own personal experience. And, if you didn’t know, I’m kinda a geek when it comes to typography. So, I thought I’d share the following six rules that I’ve come to adopt to get us started.
Rule #1: Set a good max-width for paragraphs
This is typically referred to as the measure in typographic circles and highly esteemed typographers will recommend that a paragraph have a width of around 75 characters for legibility reasons. Anything longer than that becomes difficult to read and causes unnecessary strain on the eyes because of the distance the eye has to travel left-to-right and back again (assuming ltr that is).
Here’s a quick example of a good max-width setting for a paragraph. Oh, and make sure to check out this demo on larger screen devices.
Now, this all depends on a ton of factors that great designers contemplate when setting a paragraph. However, as web designers, the difficulty for us is that we have to make sure that paragraphs feel good in additional contexts, like on mobile devices, too. So, while this rule of ~75 characters is nice to have in our back pocket, it’s most helpful when we’re trying to figure out the maximum width of our text block. More on this in a bit.
Also, I’d recommend setting that width on a container or grid class that wraps the paragraph instead of setting a max-width value on the paragraph element itself.
Kind of like this:
<div class="container">
<p>This is where our text goes.</p>
</div>
Otherwise, there may be moments in the future when there’s a need for certain paragraphs to be bigger and have a wider measure (like for introductory paragraphs perhaps). In those situations, making a different container class that just handles the larger width of the elements inside them is a nice and modular approach.
I’ve found that by having a system of classes that just deals with the width of things encourages writing much less code but also much more legible code as well. Although, yes, there is more HTML to write but I’d say that’s preferable to a lot of whacky CSS that has to be refactored in the future.
In short: make sure to set a good max-width for our paragraphs but also ensure that we set the widths on a parent class to keep our code readable.
Rule #2: Make the line height smaller than you think
One problem I often see in the wild is with paragraphs that have a line height that’s just way too big. This makes reading long blocks of text pretty exhausting and cumbersome as each hop from one line to the next feels like an enormous jump.
On mobile devices this is particularly egregious as I tend to see something like this often:
For some reason, a lot of folks tend to think that paragraphs on smaller devices require a larger line-height value — but this isn’t the case! Because the width of paragraphs are smaller, line-height can be even smaller than you might on desktop displays. That’s because on smaller screens, and with smaller paragraphs, the reader’s eye has a much shorter distance to hop from the end of one line to the beginning of the next.
This demo certainly isn’t typographically perfect (there’s no such thing), but it’s much easier to read than the majority of websites I stumble across today. In this example, notice how the line-height is probably much smaller than you’re familiar with and see how it feels as you read it:
Another common mistake I frequently see is the use of very large margins on either side of a paragraph of text, it’s often on mobile devices that make for blocks of text that are difficult to read like this:
Just — yikes! How are we expected to read this?
Instead try using no more than 10-15px of margin on either side of the paragraph because we need to ensure that our paragraphs are as wide as possible on smaller devices.
I even see folks bump the font-size down on mobile to try and have a nice paragraph width but I’d highly recommend to avoid this as well. Think of the context, because mobile devices are often held in front of the user’s face. There’s no need to force the user to bring the device any closer to read a small block of text.
Most of the time, smaller margins are the better solve.
Rule #4: Make sure that the type isn’t too thin
This is perhaps my biggest complaint when it comes to typography on the web because so many websites use extraordinarily thin sans-serif typefaces for paragraph text. This makes reading difficult because it’s harder to see each stroke in a letter when they begin to fade away into the background due to the lack of contrast.
Here’s an example of using a typeface that’s too thin:
Try and read the text there. Do you notice yourself struggling to read it? Because we’re using the light weight of Open Sans in this example, letterforms start to break apart and fall to bits. More focus is required to read it. Legibility decreases and reading becomes much more annoying than it really has to be.
I recommend picking a regular weight for body text, then trying to read a long string of text with those settings. Thin fonts look cute and pretty at a glance, but reading it in a longer form will reveal the difficulties.
Rule #5: Use bold weights for headings
Clear hierarchy is vital for controlling the focus of the reader, especially in complex applications that show a ton of data. And although it used to be more common a couple of years ago, I still tend to see a lot folks use very thin weights or regular weights for headings on websites. Again, this isn’t necessarily a die-hard rule — it’s a suggestion. That said, how difficult is it to scan this headline:
It’s a little difficult to see in this example but it’s easy to missing the headline altogether in a large application with lots of UI. I’ve often found that inexperienced typographers tend to create hierarchy with font-size whilst experienced typographers will lead with font-weight instead.
Here’s an example of something much easier to scan:
In this example, I’ve set the paragraph text to a dark gray and the heading to a color closer to black while applying a bold weight. It’s not a substantial change in the code but it’s an enormous improvement in terms of hierarchy.
Little improvement like this will quickly add up to a better experience overall when the user is asked to slog through a ton of text.
Rule #6: Don’t use Lorem Ipsum to typeset a page
I think this advice might be the most underrated and I rarely hear it raised in front-end, typography, or design groups. I’ve even noticed seasoned designers struggle to typeset a page because Lorem Ipsum is used for the placeholder content, which makes it impossible for to gauge whether a paragraph of text is easy to read or not.
Setting text in Lorem Ipsum makes good typesetting kind of impossible.
Instead, pick text that you really enjoy reading. Ideally, typesetting would be done with finalized content but that’s often a luxury in front-end development. That’s why I’d recommend picking text that sounds close to the voice and tone of the project if there’s a lack of actual content.
Seriously though, this one change will have an enormous impact on legibility and hierarchy because it encourages reading the text instead of looking at it all aesthetically. I noticed a massive improvement in my own designs when I stopped using undecipherable Lorem Ipsum text and picked content from my favorite novels instead.
And that’s it! There sure are a lot of rules when it comes to typography and these are merely the ones I tend to see broken the most. What kind of typographic issues do you see on the web though? Let us know in the comments!
Taimur Abdaal leads design at Retool, a fast way to build internal tools. They’re working on a new design system for their platform, to let anyone easily build beautiful custom apps. Typography will be a huge part of this and Taimur wrote this based on that experience.
You may have read the title for this post and thought, “Why on earth does a developer need to know anything about typography?” I mean, there’s already a lot on your plate and you’re making hundreds of decisions a day. Should you use React or Vue? npm or Yarn? ES6 or ES7? Sadly, this often leaves something like type as an afterthought. But, let’s remember that web design is 95% typography:
95% of the information on the web is written language. It is only logical to say that a web designer should get good training in the main discipline of shaping written information, in other words: Typography.
Even though we deal with content everyday — whether reading, writing, or designing it — typography can be daunting to delve into because it’s filled with jargon and subjectivity, and it’s uncommon to see it taught extensively at school.
This is intended as a practical guide for developers to learn web typography. We’ll cover a range of practical and useful topics, like how to choose and use custom fonts on the web, but more importantly, how to lay text out to create a pleasant user experience. We’ll go over the principles of typography and the CSS properties that control them, as well as handy tips to get good results, quickly.
What is typography?
First and foremost, typography is about usability. Type is the user interface for conveying information, and conveying information is what we’re here to do on the web. There are many levers we can pull to affect the usability of reading text, and only by being deliberate about these can we create a pleasant experience for our users.
After (and only after) usability, typography is about emotion. Do the letters complement your content, or contradict it? Do they amplify your brand’s personality, or dampen it? Applied to the same text, different type will make people feel different things. Being deliberate about typography lets us control these feelings.
Despite what many golden ratio enthusiasts might try to tell you, typography isn’t an exact science. Good guidelines will get you most of the way there, but you’ll need to apply a little intuition too. Luckily, you’ve been reading text your whole life — books, magazines, the Internet — so you have a lot more untapped intuition than you think!
What’s in a font?
Let’s start off by getting a handle on some basic terminology and how fonts are categorized.
Typeface vs. Font
This is how the two have traditionally been defined and distinguished from one another:
Typeface: The design of a collection of glyphs (e.g. letters, numbers, symbols)
Font: A specific size, weight, or style of a typeface (e.g. regular, bold, italic)
In essence, a typeface is like a song and a font is like its MP3 file. You’ll see both terms used in typography literature, so it’s worth knowing the distinction. “Font vs. Typeface” is also a bit of meme in the design community — you might see it crop up on Twitter, so it’ll help to be “in the know.”
HOW 2 MAJOR IN GRAPHIC DESIGN: – CHOOSE A FONT – “ITS CALLED A TYPEFACE NOT A FONT” – CHOOSE A GODDAMN TYPEFACE – U MADE THE WRONG CHOICE
But, more recently, you can essentially use both terms interchangeably and people will know what you mean.
How fonts are categorized
The broadest split is between serif and sans-serif typefaces. It’s likely you’ve come across these terms just by seeing some some typeface names floating around (like, ahem, Comic Sans).
A “serif” is a small stroke attached to the ends of letters, giving them a traditional feel. In fact, most books and newspapers are set in serif typefaces. On the contrary, sans-serif typefaces don’t have these extra strokes, giving them a smooth, modern feel.
Both serif and sans-serif have categories within them. For example, serif has sub-categories including didone, slab and old style.
As far as sans-serif goes, it includes humanist, geometric and grotesk as sub-categories.
Monospace fonts (yes, fonts) are a noteworthy category all its own. Each glyph (i.e. letter/number/symbol) in a monospace font has the same width (hence the mono spacing terminology), so it’s possible to arrange them into visual structures. You may be well familiar with monospace because we see it often when writing code, where it’s helpful to make brackets and indents line up visually. The code editor you have open right now is likely using monospace.
How to choose fonts and font pairings
This is subjective, and depends on what you’re trying to do. Each typeface has its own personality, so you’ll have to find one that aligns with your brand or the content it’s communicating. Typefaces have to be considered within the context that they’re being applied. Are we looking for formality? Maybe start with the serif family. Warm, fun and friendly? That might be a cue for sans-serif. Heck, there’s a time and a place even for Comic Sans… really! Just know that there is no hard science to choosing a font, and even the most trained of typographers are considering contextual cues to find the “right” one.
But fonts can be paired together in the same design. For example, some designs use one typeface for headings and another for body text. This is a good way to get a unique look, but it does take a fair bit of work to get it right. Like colors in a palette, some typefaces work well together while others clash. And even purposeful clashing can make sense, again, given the right context.
The only way to find out if two fonts are complementary is to see them together. Tools like FontPair and TypeConnection can help with this. If a font catches your eye when surfing the web, WhatFont is a great browser extension to help identify it. Another great resource is Typewolf which allows you to see examples of great web typography, including different font combinations in use:
While there’s a lot of subjectivity in choosing fonts, there are some objective considerations.
Font weight as a consideration
Some font families have a wide range of font weights — Light, Book, Regular, Medium, Semi-Bold, Bold, Black — whereas others just have a couple.
If you’re building a complex web app UI, you might need a range of font weights to establish hierarchy in different contexts. For something less complex, say a blog, you’ll likely be fine with just a couple.
It’s still early days — there aren’t many variable fonts available right now, and browser support is limited. But definitely a space worth watching!
Consider the legibility of a font
Some typefaces are harder to read than others. Stay away from elaborate fonts when it comes to paragraphs, and if you must have tiny text somewhere, make sure its typeface is legible at those tiny sizes.
Of these two examples, which is easier on the eyes? (There is a right answer here. ?)
Making a font bold isn’t as simple as adding an outline to the text, and italics are more than slanted letters. Good fonts have specific bold and italic styles, without which the browser tries to “fake” it:
These faux styles tend to reduce legibility and break the text’s visual cohesion because the browser can only do so much with what it’s given. Stick to fontsthat offertrue bold/italics styles, if you can.
Other things worth consideration
What we’ve looked at so far are the high level characteristics and features of fonts that can help us decide which to choose for a particular design or context. There are many other things that can (and probably should) be taken into consideration, including:
Language support: Some fonts have glyphs for foreign characters, which may be a requirement for a project geared toward a multilingual audience or that contains multilingual content.
Ligatures: Some fonts (typically serifs) have glyphs to replace otherwise awkward characters, like ? and ? which are multiple characters in a single glyph.
File size: You’re a developer, so I know you care about performance. Some fonts come in bigger files than others and those can cause a site to take a hit on load times.
Using fonts on the world wide web
A mere 10 years ago, there were two complex ways to use custom fonts on the web:
SiFR: Embedding a Flash (remember that thing?) widget to render text.
cufon: converting the font to a proprietary format, and using a specific JavaScript rendering engine to generate the text using VML on an HTML .
Today, there’s all we need is a single simple one: @font-face. It lets you specify a font file URL, which the browser then downloads and uses on your site.
You can use the @font-face declaration to define your custom font:
And then refer to it as normal, by the font-family attribute:
body {
font-family: 'MyWebFont', Fallback, sans-serif;
}
Even though @font-face is light years ahead of the old school approaches to custom web fonts, there is quite a bit to consider as far as browser support goes. Here’s a guide to getting the right amount of cross-browser support.
The impact of fonts on performance
Performance is the only downside to using custom fonts on the web. Fonts are relatively large assets — often hundreds of kilobytes in size — and will have and adverse effect on the speed of any site.
Disable font features you don’t need, e.g. hinting, kerning.
Remove glyphs that you don’t need, e.g. foreign language characters.
One tool that can help tailor a font file to suit your needs is Transfonter. Just make sure you have the rights to any font that you want to use on your site because, well, it’s just the right thing to do.
Some services will host web fonts for you
One other significant way to shed some of the weight of using custom web fonts is to use a service that will host and serve them for you. The benefit here is that third-parties store the files on a speedy CDN, optimize them, and serve them to your site via a JavaScript snippet that gets dropped into the document head. In short: it takes a lot of hassle off your hands.
How easy is it to use a hosted service? Consider that using anything on Google Fonts only requires a single in the HTML head:
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<style>
body {
font-family: 'Roboto', sans-serif;
}
</style>
</head>
<!-- Rest of the document -->
This example was taken from Google Fonts — the most popular hosted font service. It’s free to use, and, at the time of writing, has a catalogue of 915 font families. The quality of these can be hit-or-miss. Some are truly top-notch — beautiful design, many weights, true bold/italics, and advanced features like ligatures. Others, typically the more novel designs, are bare-bones, and might not be suitable for serious projects with evolving needs.
Adobe Fonts is also very popular. It comes bundled with Adobe’s Creative Cloud offering, which starts at $20.99 per month. At the time of writing, it has a catalogue of 1,636 font families. These are typically high quality fonts that you’d have to pay for, but with Adobe Fonts, you can access them all in a Netflix all-you-can-eat model.
Here are some other options:
Adobe Edge Web Fonts: This is the free version of Adobe Fonts. Adobe partnered with Google on this, so there’s a lot of overlap with the Google Fonts library. It has 503 font families in total.
Fontspring: This is a massive library of over 14,000 font families, but with an individual licensing model. This means paying more up-front, starting at ~$20 per individual font (and per weight or style), but no recurring costs. It’s also self-serve — you’ll have to host the files yourself.
MyFonts by Monotype: This is major type foundry. Similar to Fontspring: massive library of font families with an individual licensing model.
Fonts.com: This is similar to Fontspring. Options for one-off or pay-as-you go pricing (based on page views).
Cloud.typography by Hoefler&Co: This is a major type foundry. Over 1,000 font families (all by Hoefler&Co), with options for hosted (subscription, starting at $99 per year) or self-hosted (individually licensed) web fonts. The benefit here is that you get access to a library of fonts you cannot find anywhere else.
Fontstand: This services allows you to “rent” individual fonts cheaply on a monthly basis, starting ~$10 per month), which you get to own automatically after 12 months. To date, it boasts 1,500 families, with a hosted web font service.
CSS and Typography
OK, let’s move on to the intersection of design and development. We’ve covered a lot of the foundational terms and concepts of typography, but now we can turn our attention to what affordances we have to manipulate and style type, specifically with CSS.
Adjusting font sizing
Changing the size of a font is something that inevitably pops up in a typical project. Size is important because it creates hierarchy, implicitly guiding the user through the page. So, in this section, we’re going to look at two CSS features that are available in our tool belt for adjusting font sizes to create better content hierarchy.
Sizes can be expressed in a different units of measure
Did you know that there are 15 different units for sizing in CSS? Crazy, right? Most of us are probably aware of and comfortable working with pixels (px) but there are so many other ways we can define the size of a font.
Some of these are relative units, and others are absolute. The actual size of an element expressed in relative units depends on other parts of the page, while the actual size of an element expressed in absolute units is always the same. And, as you might expect, that difference is important because they serve different functions, depending on a design’s needs.
Of the 15 units we have available, we will selectively look at two in particular: px and em. Not only are these perhaps the two most used units that you’ll see used to define font sizes, but they also perfectly demonstrate the difference between absolute and relative sizing.
First off, px is an absolute unit and it actually doesn’t have much to do with pixels (it’s considered an angular measurement), but it’s defined so that a line of width 1px appears sharp and visible, no matter the screen resolution. The px value corresponds, roughly, to a little more than the distance from the highest ascender (e.g. the top of the letter A) to the lowest descender (e.g. the bottom of the letter p) in the font. Some fonts have taller letters than others, so 16px in one font might look noticeably “bigger” than 16px in another. Yet another consideration to take into account when choosing a font!
Interestingly enough, when it comes to typography, the px is a unit to be understood, but not used. The true purpose of px units is to serve as the foundation of a type system based on relative units. In other words, it’s an absolute value that a relative unit can point to in order to define its own size relative to that value.
Which takes us to em, a relative unit. Text with a font size of 2em would be twice as big as the font-size of its parent element. The body doesn’t have a parent element, but each device has its own default font-size for the body element. For example, desktop browsers usually default to 16px. Other devices (e.g. mobile phones and TVs) might have different defaults that are optimized for their form factors.
The em lets you reason about your typography intuitively: “I want this title to be twice as big as the paragraph text” corresponds directly to 2em. It’s also easy to maintain — if you need a media query to make everything bigger on mobile, it’s a simple matter of increasing one font-size attribute. Dealing in em units also lets you set other typographical elements in relation to font-size, as we’ll see later.
There are semantic HTML elements that CSS can target to adjust size
You’re sold on the em, but how many em units should each element be? You’ll want to have a range of text sizes to establish hierarchy between different elements of your page. More often than not, you’ll see hierarchies that are six level deep, which corresponds to the HTML heading elements, h1 through h6.
The right size scale depends on the use case. As a guideline, many people choose to use a modular scale. That refers to a scale based on a constant ratio for successive text elements in the hierarchy.
Modular Scales is a tool by Tim Brown, who popularized this approach, which makes it easy to visualize different size scales.
Adjusting a font’s vertical spacing and alignment
The physical size of a font is a super common way we use CSS to style a font, but vertical spacing is another incredibly powerful way CSS can help improve the legibility of content.
Use line-height to define how tall to make a line of text
Line height is one of the most important factors affecting readability. It’s controlled by the line-height property, best expressed as a unit-less number that corresponds to a multiple of the defined font size.
Let’s say we are working with a computed font size of 16px (specified in the CSS as em units, of course), a line-height of 1.2 would make each line 19.2px tall. (Remember that px aren’t actually pixels, so decimals are valid!)
Most browsers default to a line-height of 1.2 but the problem is that this is usually too tight — you probably want something closer to 1.5 because provides a little more breathing room for your eyes when reading.
Here are some general guidelines to define a good line height:
Increase line-height for thick fonts
Increase line-height when fonts are a dark color
Increase line-height for long-form content
Fonts can’t dance, but they still have rhythm
Rhythm describes how text flows vertically on a page. Much like music, some amount of consistency and structure usually leads to good “rhythm.” Like most design techniques, rhythm isn’t an exact science but there are some sensible guidelines that give good results.
One school of thought prescribes the use of paragraph line height as a baseline unit from which all other spacing is derived. This includes gaps between paragraphs and headings, and padding between the text and other page elements.
Under this system, you might set the line height of a heading to twice the line height of paragraphs, and leave a one-line gap between a heading and paragraph. Handily, you can use the same em units to define margins and padding, so there’s really no need to hard-code anything.
about rhythm here.
Horizontal Shape
Hopefully you’re convinced by now that vertical spacing is an important factor in improving legibility that we can control in CSS. Equally important is the horizontal spacing of individual characters and the overall width of content.
CSS can control the space between letters
Letter spacing is one of the most important factors affecting legibility. It is controlled by the CSS letter-spacing property, best expressed (again) in em units to keep everything relative. Tracking (the typography term for “letter spacing”) depends entirely on the font — some fonts will look great by default, while others might need a little tweaking.
In general, you only need to worry about letter spacing for text elements that are particularly big or small because most fonts are spaced well at a typical paragraph size.
For larger text, like headings and titles, you’ll often want to reduce the space between letters. And a little bit of space goes a long way. For example, -0.02em is a tiny decimal, but a good starting point, which can be tweaked until it looks just right to your eye. The only time you should think about increasing letter spacing is when dealing with stylistically unusual text — things like all-capped titles and even some number sequences.
Some specific pairs of letters, like AV, can look awkwardly spaced without a little manual tweaking. Well-crafted fonts typically specify custom spacing for such pairs, and setting the font-kerning property to normal lets you enable this. Browsers disable this on smaller text by default. Here is a little more on letter spacing, as well as other CSS tools we have to control font spacing in general.
The length of a line of text is more important than you might think
It’s unpleasant for our eyes to move long distances while reading, so the width of lines should be deliberate. General consensus is that a good width is somewhere between 60 and 70 characters per line. If you find that a line of text (especially for long-form content) drastically falls outside this range, then start adjusting.
The ch is a little-known CSS unit that we didn’t cover earlier, but can be helpful to keep line length in check. It’s a relative unit, defined as the width of the 0 character in the element’s font. Because narrow characters like l and i are relatively frequent, setting the width of a text container to something like 50ch should result in lines that are 60-70 character long.
p {
width: 50ch;
}
CSS can smooth otherwise crispy fonts
-webkit-font-smoothing is a nifty CSS property that controls how fonts are anti-aliased. This is a fancy way of saying it can draw shades of gray around otherwise square pixels to create a smoother appearance. Do note, however, the -webkit prefix there, because that indicates that the property is only supported by WebKit browsers, e.g. Safari.
The default setting is subpixel-antialiased, but it’s worth seeing what your type looks like when it’s set to just antialiased. It can improve the way many fonts look, especially for text on non-white backgrounds.
At small font sizes, this should be used with caution — it lowers contrast, affecting readability. You should also make sure to adjust letter-spacing when using this, since greater anti-aliasing will increase the space between letters.
Wrapping up
Phew! We covered a lot of ground in a relatively short amount of space. Still, this is by no means an exhaustive guide, but rather, something that I hope will encourage you to take more control over the typography in your future projects, and to seek an even deeper knowledge of the topic. For that, I’ve compiled a list of additional resources to help you level up from here.
In this article, I’m going to introduce you to the CSS Fragmentation specification. You might never have heard of it, however, if you have ever created a print stylesheet and wanted to control where the content breaks between pages, or multi-column layout and wanted to stop a figure breaking between columns, you have encountered it.
I find that quite often problems people report with multicol are really problems with browser support of fragmentation. After a quick rundown of the properties contained in this specification, I’ll be explaining the current state of browser support and some of the things you can do to get it working as well as it can in your multicol and print projects.
What Is Fragmentation?
Fragmentation in CSS describes the process by which content becomes broken up into different boxes. Currently, we have two places in which we might run into fragmentation on the web: when we print a document, and if we use multi-column layout. These two things are essentially the same. When you print (or save to PDF) a webpage, the content is fragmented into as many pages as are required to print your content.
When you use multicol, the content is fragmented into columns. Each column box is like a page in the paged context. If you think of a set of columns as being much like a set of pages it can be a helpful way to think about multicol and how fragmentation works in it.
If you take a look at the CSS Fragmentation Specification you will see a third fragmented context mentioned — that context is Regions. As there are no current usable implementations of Regions, we won’t be dealing with that in this article, but instead looking at the two contexts that you might come across in your work.
Block And Inline Boxes
I am going to mention block boxes a lot in this article. Every element of your page has a box. Some of those boxes are laid out as blocks: paragraphs, list items, headings. These are said to be participating in a block formatting context. Others are inline such as the words in a paragraph, spans and anchor elements. These participate in an inline formatting context. Put simply, when I refer to a block box, I’m talking about boxes around things like paragraphs. When dealing with fragmentation, it is important to know which kind of box you are dealing with.
For more information on block and inline layout, see the MDN article “Block And Inline Layout In Normal Flow”. It is one of those things that we probably all understand on some level but might not have encountered the terminology of before.
Controlling Breaks
Whether you are creating a print stylesheet, using a specific print user agent to make a PDF,or using multicol, you will sometimes run into problems that look like this.
In the below multicol example, I have some content which I am displaying as three columns. In the middle of the content is a boxed out area, which is being broken across two columns. I don’t want this behavior — I would like the box to stay together.
To fix this, I add the property break-inside: avoid to the box. The break-inside property controls breaks inside elements when they are in a fragmented context. In a browser which supports this property, the box will now stay in one of the columns. The columns will look less well balanced, however, that is generally a better thing than ending up with the boxout split across columns.
The break-inside property is one of the properties detailed in the fragmentation spec. The full list of properties is as follows:
break-before
break-after
break-inside
orphans
widows
box-decoration-break
Let’s have a look at how these are supposed to work before we move onto what actually happens in browsers.
The break-before And break-after Properties
There are two properties that control breaks between block-level boxes: break-before and break-after. If you have an h2 followed by two paragraphs
you have three block boxes and you would use these properties to control the breaks between the heading and first paragraph, or between the two paragraphs.
The properties are used on selectors which target the element you want to break before or after.
For example, you might want your print stylesheet to break onto a new page every time there is a level 2 heading. In this case, you would use break-before: page on the h2 element. This controls the fragmentation and ensures there is always a break before the box of the h2 element.
h2 {
break-before: page;
}
Another common requirement is to prevent headings ending up as the last thing on a page or column. In this case, you might use break-after with a value of avoid. This should prevent a break directly after the box of the element:
h1, h2, h3, h4 {
break-after: avoid;
}
Fragments Within Fragments
It is possible that you might have an element that is fragmented nested inside another. For example, having a multicol inside something which is paged. In that case, you might want to control breaks for pages but not for columns, or the other way around. This is why we have values such as page which would always force a break before or after the element but only when the fragment is a page. Or avoid-page which would avoid a break before or after the element only for paged contexts.
The same applies to columns. If you use the value column, this would always force a break before or after that element, but only for multicol contexts. The value avoid-column would prevent a break in multicol contexts.
There is an always value in the Level 4 specification which indicates that you want to break through everything – page or column. However, as a recent addition to the spec it is not currently useful to us.
Additional Values For Paged Media
If you are creating a book or magazine, you have left and right pages. You might want to control breaking in order to force something onto the left or right page of a spread. Therefore, using the following would insert one or two-page breaks before the h2 to ensure it was formatted as a right page.
h2 {
break-before: right;
}
There are also recto and verso values which relate to page progression as books written in a vertical or right to left language have a different page progression than books written in English. I’m not going to cover these values further in this article as I’m primarily concerned with what is possible from the browser this time.
break-inside
We have already seen an example of the break-inside property. This property controls breaking inside block boxes, e.g. inside a paragraph, heading or a div.
Things that you may not want to break can include a boxout as described above: figures where you do not want the caption detached from the image, tables, lists and so on. Add break-inside: avoid to any container you don’t wish to break in any fragmentation context. If you only wish to avoid breaks between columns use break-inside: avoid-column and between pages break-inside: avoid-page.
The orphans And widows Properties
The orphans and widows properties deal with how many lines should be left before or after a break (either caused by a column or a new page). For example, if I want to avoid a single line being left at the end of a column, I would use the orphans property, as in typography, an orphan is the first line of a paragraph that appears alone at the bottom of a page with the rest of the paragraph broken onto another page. The property should be added to the same element which is fragmenting (in our case, the multicol container).
.container {
column-count: 3;
orphans: 2;
}
To control how many lines should be at the top of a column or page after a break, use widows:
.container {
column-count: 3;
widows: 2;
}
These properties deal with breaks between inline boxes such as the lines of words inside a paragraph. Therefore, they don’t help in the situation where a heading or other block element is alone at the bottom of a column or page, you need the break properties discussed above for that.
Box Decoration
A final property that may be of interest is the box-decoration-break property. This controls the situation where you have a box with a border broken between two column boxes or pages. Do you want the border to essentially be sliced in half? Or do you want each of the two halves of the box to be wrapped fully in a border?
The first scenario is the default, and is as if you set the box-decoration-break property to slice on the box.
.box {
box-decoration-break: slice;
}
To get the second behavior, set box-decoration-break to clone.
.box {
box-decoration-break: clone;
}
Browser Support For Fragmentation
Now we come to the reason I don’t have a bunch of CodePen examples above to demo all of this to you, and the main reason for my writing this article. Browser support for these properties is not great.
If you are working in Paged Media with a specific user agent such as Prince, then you can enjoy really good support for fragmentation, and will probably find these properties very useful. If you are working with a web browser, either in multicol, creating print stylesheets, or using something like Headless Chrome to generate PDFs, support is somewhat patchy. You’ll find that the browser with the best support is Edge — until it moves to Chromium anyway!
Can I Use isn’t overly helpful with explaining support due to mixing the fragmentation properties in with multicol, then having some separate data for legacy properties. So, as part of the work I’ve been doing for MDN to document the properties and their support, I began testing the actual browser support. What follows is some advice based on that testing.
Legacy And Vendor Prefixed Properties
I can’t go much further without a history lesson. If you find you really need support for fragmentation then you may find some relief in the legacy properties which were originally part of CSS2 (or in some prefixed properties that exist).
In CSS2, there were properties to control page breaking. Multicol didn’t exist at that point, so the only fragmented context was a paged one. This meant that three specific page breaking properties were introduced:
page-break-before
page-break-after
page-break-inside
These work in a similar way to the more generic properties without the page- prefix, controlling breaks before, after and inside boxes. For print stylesheets, you will find that some older browsers which do not support the new break- properties, do support these page prefixed properties. The properties are being treated as aliases for the new properties.
In a 2005 Working Draft of the multicol specification are details of breaking properties for multicol — using properties prefixed with column- (i.e. column-break-before, column-break-after, and column-break-inside). By 2009, these had gone, and a draft was in the multicol specification for unprefixed break properties which eventually made their way into the CSS Fragmentation specification.
However, some vendor prefixed column-specific properties were implemented based on these properties. These are:
-webkit-column-break-before
-webkit-column-break-after
-webkit-column-break-inside
Support For Fragmentation In Multicol
The following is based on testing these features in multicol contexts. I’ve tried to explain what is possible, but do take a look at the CodePens in whichever browsers you have available.
Multicol And break-inside
Support in multicol is best for the break-inside property. Up to date versions of Chrome, Firefox, Edge, and Safari all support break-inside: avoid. So you should find that you can prevent boxes from breaking between columns when using multicol.
Several browsers, with the exception of Firefox, support the -webkit-column-break-inside property, this can be used with a value of avoid and may prevent boxes breaking between columns which do not have support for break-inside.
Firefox supports page-break-inside: avoid in multicol. Therefore, using this property will prevent breaks inside boxes in Firefox browsers prior to Firefox 65.
This means that if you want to prevent breaks between boxes in multicol, using the following CSS will cover as many browsers as possible, going back as far as possible.
In order to prevent breaks before an element, you should be able to use break-before: avoid or break-before: avoid-column. The avoid property has no browser support.
Edge supports break-before: column to always force a break before the box of the element.
Safari, Chrome and Edge also support -webkit-column-break-before: always which will force a break before the box of the element. Therefore, if you want to force a break before the box of an element, you should use:
To prevent breaks after an element, to avoid it becoming the last thing at the bottom of a column, you should be able to use break-after: avoid and break-after: avoid-column. The only browser with support for these is Edge.
Edge also supports forcing breaks after an element by using break-after: column, Chrome supports break-after: column and also -webkit-column-break-after: always.
Firefox does not support break-after or any of the prefixed properties to force or allow breaks after a box.
Therefore, other than Edge, you cannot really avoid breaks after a box. If you want to force them, you will get results in some browsers by using the following CSS:
Whether you print directly from your desktop browser or generate PDF files using headless Chrome or some other solution reliant on browser technology doesn’t make any difference. You are reliant on the browser support for the fragmentation properties.
If you create a print stylesheet you will find similar support for the break properties as for multicol, however, to support older browsers you should double up the properties to use the page- prefixed properties.
Print Stylesheets And break-inside
In modern browsers ,the break-inside property can be used to prevent breaks inside boxes, add the page-break-inside property to add support for older browsers.
There is better support for the page and avoid-page values than we see for the equivalent multicol values. The majority of modern browsers have support.
Print Stylesheets And break-before
To force breaks after a box, use break-after: page along with page-break-after: always.
The widows and orphans properties enjoy good cross-browser support — the only browser without an implementation being Firefox. I would suggest using these when creating a multicol layout or print stylesheet. If they don’t work for some reason, you will get widows and orphans, which isn’t ideal but also isn’t a disaster. If they do work your typography will look all the better for it.
box-decoration-break
The final property of box-decoration-break has support for multicol and print in Firefox. Safari, Chrome and other Chromium-based browsers support -webkit-box-decoration-break, but only on inline elements. So you can clone borders round lines of a sentence for example; they do not have support in the context we are looking at.
In the CodePen below, you can see that testing for -webkit-box-decoration-break: clone with Feature Queries returns true, however, the property has no effect on the border of the box in the multicol context.
As you can see, the current state of fragmentation in browsers is somewhat fragmented! That said, there is a reasonable amount you can achieve and where it fails, the result tends to be suboptimal but not a disaster. Which means it is worth trying.
It is worth noting that being too heavy handed with these properties could result in something other than what you hoped for. If you are working on the web rather than print and force column breaks after every paragraph, then end up with more paragraphs than space for columns, multicol will end up overflowing in the inline direction. It will run out of columns to place your additional paragraphs. Therefore, even where there is support, you still need to test carefully, and remember that less is more in a lot of cases.
More Resources
To read more about the properties head over to MDN, I’ve recently updated the pages there and am also trying to keep the browser compat data up to date. The main page for CSS Fragmentation links to the individual property pages which have further examples, browser compat data and other information about using these properties.
Choosing a domain name is similar to choosing a company’s name – it requires a lot of thought and consideration.
Your domain name is your identity on the web!
So you have to make sure that you choose a domain name that not only fits your business, but it is also easy to find and promote.
Do you want your website to make an impression and stir the user’s interest ever since the name was pronounced?
Find a domain name relevant to your activity, using Google’s keywords and Keyword Planner to describe your business and the services you provide.
Keyword Planner can be used to find a domain name, but you can also run a marketing and advertising strategy.
Do not use a long and complex domain name, risk being misspelled by users. Better short and easy to write, this is essential for online success.
Appropriate extension for domain names
Extensions are suffixes, such as .com or .net, at the end of web addresses. They may have specific uses, so make sure you choose one that works for your business.
There are a multitude of new generic top-level domains – such as .bar, .pizza, . club , .pro etc – offers a great opportunity to record short and very relevant names.
See here a list of over 400 domain extensions you can buy.
Avoid numbers and crap
Numbers and mistakes are often misunderstood!
Without numbers and hyphens, the domain name is easier to write / pronounce, and you can avoid any confusion that the user can do, but this is not a general rule.
Be memorable
There are millions of registered domain names, having a field that is appealing and memorable is essential. Once you come up with a name, share it with your close friends to make sure it sounds appealing and makes sense to others.
We felt spoiled with CSS grid for a minute there. It arrived hot and fast in all the major browsers all at once. Now that we’re seeing a lot more usage, we’re seeing people want more from grid.
Michelle Barker lists hers wants (and I’ll put my commentary after):
Styling row and column gaps. I’ve also heard requested styling grid cells directly, rather than needing to place an element there and style that element.
Multiple gap values. I wanted this just the other week, and I was told to use an empty column or row instead of a gap. The size of that column can be controlled, and things are placed accordingly to treat it like a gap. Sort of OK, except that isn’t particularly friendly to implicit grids.
Autoflow patterns. This is clever. Check out Michelle’s use case and proposal.
calc() with the fr unit. This is a mindbender. I can see wanting to do something like calc(1fr - 100px), but then isn’t the leftover space 100px short and 1fr recalcuated to fill that space? Seems infinite loopy.
Aspect ratio grid cells. I suspect, if we get this, it’ll be a generic aspect ratio solution that will work on any element, including elements placed onto a grid.
Subgrid is also starting to be hotly requested, and I can see why. While building the last page layout I did using grid, I found myself strongly wishing I could share the overall page grid lines within sub-elements.
Rachel Andrew talked about its status about six months ago in CSS Grid Level 2: Here Comes Subgrid. I’m not sure where it’s at now, but I don’t think any browser is supporting it quite yet. (I’m not even sure if the spec is technically done.)
Brad put a point on the desire here:
Container queries and subgrid would make my design system work so much easier.
Define a grid and throw some components in there, then watch them snap into place to the parent grid and look great no matter what the container dimensions are.
If we combine subgrid with grid-template-areas within the cards (read my last post if you don’t know about Grid Areas, it’ll blow your mind), complex responsive card-based layouts become trivial.
The prospect of a subgrid on both axes gives us a way to sort of accomplish relative grid positioning, at least for semantically grouped items, like I wished I had above! Group your stuff in a container, position your container on the grid, make that container a subgrid on both axes, and declare your tracks relative to the subgrid element’s grid position!
Between Flexbox, Grid, display: contents, and subgrids, we will finally have everything we need to write very slim, clean, semantic markup with basically no fluff or purely structural elements. It will be a huge boon for accessibility, SEO, and just developers trying to understand your markup!
This is why I’ve come to the same conclusion other grid experts (like Rachel) already have: subgrids are a major component of grid layout, and should be a part of any grid layout implementation when it emerges from developer-preview status. If that means delaying the emergence of grids, I think it’s worth it.
Not only has CSS Grid reshaped the way we think and build layouts for the web, but it has also contributed to writing more resilient code, replacing “hacky” techniques we’ve used before, and in some cases, killing the need to rely on code for specific resolutions and viewports. What’s so cool about this era in web development is that we’re capable of doing more and more with fewer lines of code.
In this article, we’ll start dipping our toes into the power of CSS Grid by building a couple of common responsive navigation layouts. It’s easier than what you may think, and since CSS Grid was built with responsiveness in mind, it’ll take less code than writing media queries all over the place. Let’s do this!
We’ll kick off this set of examples by creating a common website layout: A full-width hero section, with a grid of cards below.
Both elements will respond to window resizing and adapt accordingly. Though this might seem like a lot of code at first glance, the responsive behavior is done with only six lines of CSS Grid code, and without writing a single @media rule. Let’s break down the code to see what’s going on:
The hero section
Let’s take a look at the code for the .hero element:
<section class="hero">
<h1>You thirsty?</h1>
<article>
<p>Explore local breweries with just one click and stirred by starlight across the centuries light years great turbulent clouds circumnavigated paroxysm of global death.</p>
<a href="#breweries">Browse Breweries</a>
</article>
</section>
We have a bunch of background styles to enable the beer background, a bit of padding to separate the content from the edge of the screen, and then three lines of grid styles:
The first line (display: grid;) is changing the behavior of the .hero element to be a grid container. That means the elements inside .hero are now grid items.
The second line (align-items: center;) is going to vertically center the columns on our grid. But these two lines don’t do anything on their own until we set the columns of our grid.
And that’s where the third line comes in. A lot of stuff is going on in that single property, so let’s go one step at a time.
The repeat() function
Generally speaking, what we usually do to define our columns and rows on a CSS Grid is to add the value for each track after defining the property, like this:
.element {
/* This will result on four columns, each one of 1fr */
grid-template-columns: 1fr 1fr 1fr 1fr;
/* This will result on two rows, each one of 300px */
grid-template-rows: 300px 300px;
}
Now, that’s quite dull. We can use the repeat() function to make that less verbose and easier to follow. The function takes two parameters:
The number of times to repeat the value.
The value itself.
After refactoring our code to use repeat(), we should expect the same results from these lines of code:
.element {
/* this is the same as grid-template-columns: 1fr 1fr 1fr 1fr; */
grid-template-columns: repeat(4, 1fr);
/* this is the same as grid-template-rows: 300px 300px; */
grid-template-rows: repeat(2, 300px);
}
Much cleaner, yeah?
The minmax() function
Now, the above examples are explicitly defining sizes for the tracks (1fr and 300px). That might work for some scenarios, but for our beer example here, we need to be able to automatically calculate the size of the track, based on the width of the viewport, and automatically adjust the number of columns shown. To be able to do that, we’ll define a range of values using the minmax() function. What will we be defining? You’ve probably guessed by now: The *minimum* and *maximum* values we want these columns to be able to resize to.
In the hero for our beer example above, we set our minmax() property to be 240px at its minimum size, and 1fr at its maximum size. fr units, if you’ve never heard of them, stand for fractional units. Nobody can explain them better than Jen Simmons on this video and Robin Rendle in this post.
That results in our tracks being 1fr when there’s plenty of space on our viewport (aka desktop resolutions), and 240px when there’s not enough space for both columns (like on mobile devices). That’s why they nicely grow when we make our browser wider, since they’re taking the remaining space and equally dividing it across the existing columns. Now, moving to the last piece of the puzzle!
The auto-fit keyword
The auto-fit keyword allows us to wrap our columns into rows when there’s not enough space in our viewport to fit the 240px minimum value without overflowing the content. Sara Soueidan wrote an excellent article about auto-sizing columns using the auto-fill and auto-fit keywords, in case you want to dive a little deeper into what’s going on under the hood. Now, with that last bit of code in place, we should be able to achieve this result:
The article list
Now that we’ve thoroughly reviewed the behavior of the elements inside our hero element, it’s likely that the first two lines of CSS code for the breweries list below it might already seem familiar to you:
That’s right! We’re using the exact same approach: On the first line we define our grid, on the second one we size our tracks using the same magic one-liner, and on the third line we set a gap for these columns. Nothing new under the sun, and what’s really neat about this, is that our code is resilient enough to adjust the number of tracks and their sizes, according to the number of items we have inside our unordered list:
That’s all, folks! A fully responsive website layout, using just six lines of CSS code. Not bad, huh? Make sure you check the source code and play around with this example on CodePen.
On this next example, we’ll embrace the power of our newly learned combination of repeat(), auto-fit and minmax() to create this responsive image gallery. We’ll also be sizing our tracks using grid-column and grid-row, and learning about the handy property:value combination of grid-auto-flow: dense; that allows us to change the default behavior of the elements that can’t fit on our explicit tracks: Instead of wrapping themselves in new rows or columns, we’ll make them fit into the unused spots on our grid. Let’s get into the coding!
The grid setup
The grid is created using our familiar display: grid; property, where columns are defined using repeat(), auto-fit and minmax(). We also added a bunch rows with a repeat() function and defined a gap to our images, using grid-gap. But the new player here is the grid-auto-flow: dense;. We’ll get to it in a second.
We also created a repetition pattern using the nth-child() pseudo-selector to set different sizes for our tracks using grid-column and grid-row. Notice here that we’re using the span keyword to allow the selected item to occupy more than one column or row.
/* This will create 2x images every 4 elements */
.gallery > .gallery__list > li:nth-child(4n) {
grid-column: span 2; /* Spans two columns */
grid-row: span 2; /* Spans two rows */
}
/* This will create 3x images every 8 elements */
.gallery > .gallery__list > li:nth-child(8n) {
grid-column: span 3;
grid-row: span 3;
}
And finally, we’ll make sure our images cover the entire area of its container, regardless if it’s 1x, 2x or 3x, using object-fit: cover;. If you have never heard of object-fit, it works fairly similar to how background-image does, but with HTML tags:
Now, the real deal here is grid-auto-flow: dense;. Check what happens when we take that out from our code:
See those holes on our beautifully crafted grid? That’s because some of the elements on it are taking 2x or 3x spots, and when there isn’t enough space on our tracks to fit them, they’ll wrap into a new row, since that’s the default behavior. By changing it from row to dense, we’re telling the grid to fill any gaps we might have with elements that could fit them, regardless of their source order on the DOM.
That’s why this technique might come especially handy for things like image galleries, but might not be suitable for other use cases where you might need to preserve the order of the markup. Feel free to play around with the CodePen demo to check the differences between where items are placed.
Now, on to the last demo, where we’ll take advantage of the ability to nest grids to recreate this Trello Board. We’ll be creating a grid to hold our four different columns, and inside of those, we’ll create a child grid for our cards. Even though this example won’t explore new properties or revolutionary methods, it’ll help us to get a grasp on how easy it is to build complex layouts with a few lines of CSS code. This demo has a lot of extra code to achieve the styling of the Trello layout, so we’ll focus solely on the grid styles.
The columns
To create the four columns, we’ll use display: grid; on the container and use our magical one-liner for our grid-template-columns. We’ll also be defining a gap between them, and use align-items: flex-start; to ensure that our columns don’t stretch to the bottom of the screen.
Now, the original Trello is not responsive by default: If you resize your browser on a Trello Board, you’ll notice that you’ll end up having a horizontal scroll on your columns, rather than wrapping them on a new row. We’re not following that behavior here since we want to build responsive layouts, but in case you’re curious, and want to emulate Trello’s functionality, you can achieve that by adding two more lines of CSS code:
.column__list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: .5rem;
align-items: flex-start;
/* Uncomment these lines if you want to have the standard Trello behavior instead of the column wrapping */
grid-auto-flow: column;
grid-auto-columns: minmax(260px, 1fr);
}
We learned about grid-auto-flow in our previous demo and discovered that it let us control how the auto-placement algorithm work, and how implicit elements should be added in the flow of the grid. The default behavior is row, meaning that any extra element that won’t fit on our grid will wrap into a new line. We changed that to be dense on our previous demo, and we’ll change it to be column on this one: That way, any new column added here will end up in an implicit column, and have a horizontal scroll. We’ll also define a width for those auto-generated columns with the grid-auto-columns property.
The cards
For the cards grid, we’ll use a similar approach. We’ll display: grid; on the container. We won’t define any columns here, since we don’t want to have any, and we’ll put grid-template-rows: auto; to use to avoid all cards having the same height — we want some of them to be bigger and some of them smaller, based on the type of content being added to them.
And, again, that’s all folks! Two more lines to set a gap and a margin to the cards, and we’re done! Everything else in the Pen is standard CSS to achieve the Trello look and feel.
So then… are media queries dead?
Back in the day, when we were building layouts using display: inline-block or floats, media queries made a lot of sense in order to change the size of our elements as the viewport got smaller. But now, with the incredibly powerful layouts that we’re able to create with a couple of CSS lines, you might feel tempted to think that media queries are doomed. I strongly disagree with that: I believe that we should change the way we think about them, and therefore use them differently.
As Rachel Andrew stated about a year ago, we should use media queries to fix our layout when it breaks, rather than targeting devices: There are so many out there! With the advent of Media Queries Level 4 and 5, we’re not only able to detect screen sizes now, but pointer types as well. As a result, we can dig into a user’s system preferences and adapt our code for those who prefer reduced motion or whether we should use inverted colors. That means media queries are not dead; on the flipside, I’d say it’s an exciting time for using media queries, but we need to learn to use them right. In the meantime, building robust layouts using modern techniques such as Flexbox or CSS Grid, will save you a bunch of time, code, and headaches.
Last time, we looked at a few possible approaches for declaring and using CSS custom properties in responsive designs. In this article, we’ll take a closer look at CSS variables and how to use them in reusable components and modules. We will learn how to make our variables optional and set fallback values.
As an example, we will build a simple grid system based on flexbox. Grid systems play a vital role in responsive designs. However, building a grid system that is flexible and lightweight at the same time can be a tricky task. Let’s see what the common approaches towards grid systems are and how CSS custom properties can help us build them.
There’s quite a lot of repetition and hard-coded values here. Not to mention how many more will be generated once we add more breakpoints, offset classes, etc.
Building a grid system with Sass
To make our grid example more readable and maintainable, let’s use Sass to preprocess our CSS:
$columns: 12; // Number of columns in the grid system
.container {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 960px;
}
@for $width from 1 through $columns {
.col-#{$width} {
flex-basis: $width / $columns * 100%;
}
}
This is definitely much easier to work with. As we develop our grid further and, let’s say, would like to change it from 12 columns to 16 columns, all we have to do is to update a single variable (in comparison to dozens of classes and values). But… as long as our Sass is shorter and more maintainable now, the compiled code is identical to the first example. We are still going to end up with a massive amount of code in the final CSS file. Let’s explore what happens if we try to replace the Sass variables with CSS custom properties instead.
Building a grid system with CSS custom properties
Before we start playing with CSS custom properties, let’s start with some HTML first. Here’s the layout we’re aiming for:
It consists of three elements: a header, a content section and a sidebar. Let’s create markup for this view, giving each of the elements a unique semantic class (header, content, sidebar) and a column class which indicates that this element is a part of a grid system:
Our grid system, as before, is based on a 12-column layout. You can envision it as an overlay covering our content areas:
So .header takes all 12 columns, .content takes eight columns (66.(6)% of the total width) and .sidebar takes four columns (33.(3)% of the total width). In our CSS, we would like to be able to control the width of each section by changing a single custom property:
To make it work, all we need to do is write a rule for the .column class. Lucky for us, most of the work is already done! We can re-use the Sass from the previous chapter and replace the Sass variables with CSS custom properties:
.container {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 960px;
}
.column {
--columns: 12; /* Number of columns in the grid system */
--width: 0; /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
Notice two important changes here:
The --columns variable is now declared inside of the .column rule. The reason is that this variable is not supposed to be used outside of the scope of this class.
The math equation we perform in the flex-basis property is now enclosed within a calc() function. Math calculations that are written in Sass are compiled by the preprocessor and don’t need additional syntax. calc(), on the other hand, lets us perform math calculations in live CSS. The equation always needs to be wrapped within a calc() function.
On a very basic level, that’s it! We’ve just built a 12-column grid system with CSS custom properties. Congratulations! We could call it a day and happily finish this article right now, but… we usually need a grid system that is a bit more sophisticated. And this is when things are getting really interesting.
Most times, we need layouts to look different on various screen sizes. Let’s say that in our case we want the layout to remain as it is on a large viewport (e.g. desktop) but have all three elements become full-width on smaller screens (e.g. mobile).
So, in this case, we would like our variables to look as follows:
.content and .sidebar each hold two variables now. The first variable (--width-mobile) is a number of columns an element should take by default, and the second one (--width-tablet) is the number of columns an element should take on larger screens. The .header element doesn’t change; it always takes the full width. On larger screens, the header should simply inherit the width it has on mobile.
Now, let’s update our .column class.
CSS variables and fallback
To make the mobile version work as expected, we need to alter the .column class as follows:
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
Basically, we replace the value of the --width variable with --width-mobile. Notice that the var() function takes two arguments now. The first of them is a default value. It says: “If a --width-mobile variable exists in a given scope, assign its value to the --width variable.” The second argument is a fallback. In other words: “If a --width-mobile variable is not declared in a given scope, assign this fallback value to the --width variable.” We set this fallback to prepare for a scenario where some grid elements won’t have a specified width.
For example, our .header element has a declared --width-mobile variable which means the --width variable will be equal to it and the flex-basis property of this element will compute to 100%:
Now, as we understand how to set fallback for CSS custom properties, we can create a breakpoint, by adding a media query to our code:
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet); /* Width of the element on tablet and up */
}
}
This works exactly as expected, but only for the content and sidebar, i.e. for the elements that have specified both --width-mobile and --width-tablet. Why?
The media query we created applies to all .column elements, even those that don’t have a --width-tablet variable declared in their scope. What happens if we use a variable that is not declared? The reference to the undeclared variable in a var() function is then considered invalid at computed-value time, i.e. invalid at the time a user agent is trying to compute it in the context of a given declaration.
Ideally, in such a case, we would like the --width: var(--width-tablet); declaration to be ignored and the previous declaration of --width: var(--width-mobile, 0); to be used instead. But this is not how custom properties work! In fact, the invalid --width-tablet variable will still be used in the flex-basis declaration. A property that contains an invalid var() function always computes to its initial value. So, as flex-basis: calc(var(--width) / var(--columns) * 100%); contains an invalid var() function the whole property will compute to auto (the initial value for flex-basis).
What else we can do then? Set a fallback! As we learned before, a var() function containing a reference to the undeclared variable, computes to its fallback value, as long as it’s specified. So, in this case, we can just set a fallback to the --width-tablet variable:
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, 0));
}
}
This will create a chain of fallback values, making the --width property use --width-tablet when available, then --width-mobile if --width-tablet is not declared, and eventually, 0 if neither of the variables is declared. This approach allows us to perform numerous combinations:
.section-1 {
/* Flexible on all resolutions */
}
.section-2 {
/* Full-width on mobile, half of the container's width on tablet and up */
--width-mobile: 12;
--width-tablet: 6;
}
.section-3 {
/* Full-width on all resolutions */
--width-mobile: 12;
}
.section-4 {
/* Flexible on mobile, 25% of the container's width on tablet and up */
--width-tablet: 3;
}
One more thing we can do here is convert the default 0 value to yet another variable so we avoid repetition. It makes the code a bit longer but easier to update:
.column {
--columns: 12; /* Number of columns in the grid system */
--width-default: 0; /* Default width, makes it flexible */
--width: var(--width-mobile, var(--width-default)); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
}
}
Now, we have a fully functional, flexible grid! How about adding some more breakpoints?
Adding more breakpoints
Our grid is already quite powerful but we often need more than one breakpoint. Fortunately, adding more breakpoints to our code couldn’t be easier. All we have to do is to re-use the code we already have and add one variable more:
.column {
--columns: 12; /* Number of columns in the grid system */
--width-default: 0; /* Default width, makes it flexible */
--width: var(--width-mobile, var(--width-default)); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
}
}
@media (min-width: 768px) {
.column {
--width: var(--width-desktop, var(--width-tablet, var(--width-mobile, var(--width-default))));
}
}
One thing that doesn’t look that great in our code is that feedback chains are getting longer and longer with every breakpoint. If we’d like to tackle this issue, we can change our approach to something like this:
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width-tablet: var(--width-mobile);
--width: var(--width-tablet);
}
}
@media (min-width: 768px) {
.column {
--width-desktop: var(--width-tablet);
--width: var(--width-desktop);
}
}
This code is doing exactly the same job but in a bit different way. Instead of creating a full fallback chain for each breakpoint, we set a value of each variable to the variable from the previous breakpoint as a default value.
Why so complicated?
It looks like we’ve done quite a lot of work to complete a relatively simple task. Why? The main answer is: to make the rest of our code simpler and more maintainable. In fact, we could build the same layout by using the techniques described in the previous part of this article:
In a small project, this approach could work perfectly well. For the more complex solutions, I would suggest considering a more scalable solution though.
Why should I bother anyway?
If the presented code is doing a very similar job to what we can accomplish with preprocessors such as Sass, why should we bother at all? Are custom properties any better? The answer, as usually, is: it depends. An advantage of using Sass is better browser support. However, using custom properties has a few perks too:
It’s plain CSS. In other words, it’s a more standardized, dependable solution, independent from any third parties. No compiling, no package versions, no weird issues. It just works (apart from the browsers where it just doesn’t work).
It’s easier to debug. That’s a questionable one, as one may argue that Sass provides feedback through console messages and CSS does not. However, you can’t view and debug preprocessed code directly in a browser, whilst working with CSS variables, all the code is available (and live!) directly in DevTools.
It’s more maintainable. Custom properties allow us to do things simply impossible with any preprocessor. It allows us to make our variables more contextual and, therefore, more maintainable. Plus, they are selectable by JavaScript, something Sass variables are not.
It’s more flexible. Notice, that the grid system we’ve built is extremely flexible. Would you like to use a 12-column grid on one page and a 15-column grid on another? Be my guest—it’s a matter of a single variable. The same code can be used on both pages. A preprocessor would require generating code for two separate grid systems.
It takes less space. As long as the weight of CSS files is usually not the main bottleneck of page load performance, it still goes without saying that we should aim to optimize CSS files when possible. To give a better image of how much can be saved, I made a little experiment. I took the grid system from Bootstrap and rebuilt it from scratch with custom properties. The results are as follows: the basic configuration of the Bootstrap grid generates over 54KB of CSS whilst a similar grid made with custom properties is a mere 3KB. That’s a 94% difference! What is more, adding more columns to the Bootstrap grid makes the file even bigger. With CSS variables, we can use as many columns as we want without affecting the file size at all.
The files can be compressed to minimize the difference a bit. The gzipped Bootstrap grid takes 6.4KB in comparison to 0.9KB for the custom properties grid. This is still an 86% difference!
Performance of CSS variables
Summing up, using CSS custom properties has a lot of advantages. But, if we are making the browser do all the calculations that had been done by preprocessors, are we negatively affecting the performance of our site? It’s true that using custom properties and calc() functions will use more computing power. However, in cases similar to the examples we discussed in this article, the difference will usually be unnoticeable. If you’d like to learn more about this topic, I would recommend reading this excellent article by Lisi Linhart.
Not only grid systems
After all, understanding the ins and outs of custom properties may not be as easy as it seems. It will definitely take time, but it’s worth it. CSS variables can be a huge help when working on reusable components, design systems, theming and customizable solutions. Knowing how to deal with fallback values and undeclared variables may turn out to be very handy then.
Thanks for reading and good luck on your own journey with CSS custom properties!
I have a habit of getting some hosting when I need a new WordPress site. That is, a self-installed, self-hosted WordPress.org site. That’s served me well over the years. I like my control. But along with that control comes a certain level of extra responsibility that sometimes just isn’t worth it.
That’s the case for me now with my little blog Email is Good.
Right from the get-go, I knew I wanted Email is Good to be as absolutely simple as could be. At the moment, I can’t prioritize a fancy custom design or really any specialized functionality at all. All I want is a simple, clean blog in which to publish blog posts. And as powerful and flexible as WordPress is, it’s still extra good at that use case.
I’d like to move it over to WordPress.com, so that I don’t have to deal with hosting, upgrades, backups, security… it’ll just host my simple blog and I can unburden myself of that little spoonful of technical debt.
Their docs for this are there, but a little on the light side, so I’ll document my process here.
Set up the WordPress.com side first
There is a nice clean URL for kicking off a new WordPress.com site:
There isn’t really a one-click just suck everything over in one shot system. Instead, you set up the site on WordPress.com, deal with the domain, and import the content. It might feel a little weird, but this first step is just kinda re-setting up the basics:
Deal with the domain
By “domain”, I mean the URL that you may already own. I own “email-is-good.com” which is what I want to continue to use.
During setup you can buy a domain (or get a free one! They’ll give you a wordpress.com or .blog subdomain), but since I’m moving a site here, I’ll select the option that I already own the domain.
My domain name is already registered on GoDaddy.com. I could just leave the domain name there and map the domain over to WordPress.com. I think that’s generally a smart thing to do, but I wanted to try what seems to be the default which is transferring it over to WordPress.com.
I went through a process of basically re-registering the domain with WordPress.com.
In order to actually transfer the domain, I had to go to GoDaddy and “unlock” the domain as well as request a transfer authorization code.
If you’re transferring a domain, it can take a little while.
The note on the page above tells me it might take a full week to complete. It took me one day less. I got the success email on February 18th instead of 19th.
I did have to “flip the switch”, as the email suggests, to use the WordPress nameservers.
But before I did, I made sure the new WordPress.com site had all the old content!
Exporting Content & Media
There is an export tool baked right into WordPress. Find it under Tools.
You’ll get an .xml file as output. Mine was called:
emailisgood.wordpress.2019-02-12.xml
Importing Content & Media
On the WordPress.com side, there is a big Import option right in the sidebar. In those options, there is a WordPress option to choose.
Drag and drop the .xml file there.
Mine was pretty quick, but I imagine it could take a while. You’ll even get an email when it’s done.
All my content and media made the journey just fine!
Clean Up
I had to do a little cleanup here and there to get the site exactly as it was. My site is so basic, it was hardly any work, but it’s worth knowing you might have to mop up a little. For example, the site already had a contact page, so I had to nuke the one that was imported (which was using a contact form plugin I didn’t need any more anyway) and make sure it was all functional.
Another thing that didn’t make the trip to the new site was the widgets. I had a sidebar with some widgets that I had to re-build, but that was no big deal. I literally copy-pasted the content from them from the old site before I flipped the switch.
So now! I’ve ditched a pile of technical debt. No more worrying about my SSL certifiate. No more having to manually follow up with any hosting company about downtime. Performance is largely in the hands of someone else.
I just have a simple site where I can write write write.
Video!
If it’s helpful for you to watch me talk all this out, I’ve put it on YouTube:
Vue.js has achieved phenomenal adoption growth over the last few years. It has gone from a barely known open-source library to the second most popular front-end framework (behind only React.js).
One of the biggest reasons for its growth is that Vue is a progressive framework — it allows you to adopt bits and pieces at a time. Don’t need a full single page application? Just embed a component. Don’t want to use a build system? Just drop in a script tag, and you’re up and running.
This progressive nature has made it very easy to begin adopting Vue.js piecemeal, without having to do a big architecture rewrite. However, one thing that is often overlooked is that it’s not just easy to embed Vue.js into sites written with other frameworks, it’s also easy to embed other code inside of Vue.js. While Vue likes to control the DOM, it has lots of escape hatches available to allow for non-Vue JavaScript that also touches the DOM.
This article will explore the different types of third-party JavaScript that you might want to use, what situations you might want to use them inside of a Vue project, and then cover the tools and techniques that work best for embedding each type within Vue. We’ll close with some considerations of the drawbacks of these approaches, and what to consider when deciding if to use them.
This article assumes some familiarity with Vue.js, and the concepts of components and directives. If you are looking for an introduction to Vue and these concepts, you might check out Sarah Drasner’s excellent introduction to Vue.js series or the official Vue Guide.
Types Of Third-Party JavaScript
There are three major types of third-party JavaScript that we’ll look at in order of complexity:
The first category of third-party JavaScript is libraries that provide logic in the abstract and have no direct access to the DOM. Tools like moment.js for handling dates or lodash for adding functional programming utilities fall into this category.
These libraries are trivial to integrate into Vue applications, but can be wrapped up in a couple of ways for particularly ergonomic access. These are very commonly used to provide utility functionality, the same as they would in any other type of JavaScript project.
Element Augmentation Libraries
Element augmentation is a time-honored way to add just a bit of functionality to an element. Examples include tasks like lazy-loading images with lozad or adding input masking using Vanilla Masker.
These libraries typically impact a single element at a time, and expect a constrained amount of access to the DOM. They will likely be manipulating that single element, but not adding new elements to the DOM.
These tools typically are tightly scoped in purpose, and relatively straightforward to swap out with other solutions. They’ll often get pulled into a Vue project to avoid re-inventing the wheel.
Components And Component Libraries
These are the big, intensive frameworks and tools like Datatables.net or ZURB Foundation. They create a full-on interactive component, typically with multiple interacting elements.
They are either directly injecting these elements into the DOM or expect a high level of control over the DOM. They were often built with another framework or toolset (both of these examples build their JavaScript on top of jQuery).
These tools provide extensive functionality and can be challenging to replace with a different tool without extensive modifications, so a solution for embedding them within Vue can be key to migrating a large application.
How To Use In Vue
Non-DOM Libraries
Integrating a library that doesn’t touch the DOM into a Vue.js project is relatively trivial. If you’re using JavaScript modules, simply importor require the module as you would in another project. For example:
import moment from 'moment';
Vue.component('my-component', {
//…
methods: {
formatWithMoment(time, formatString) {
return moment(time).format(formatString);
},
});
If using global JavaScript, include the script for the library before your Vue project:
One additional common way to layer on a bit more integration is to wrap up your library or functions from the library using a filter or method to make it easy to access from inside your templates.
Vue Filters
Vue Filters are a pattern that allows you to apply text formatting directly inline in a template. Drawing an example from the documentation, you could create a ‘capitalize’ filter and then apply it in your template as follows:
{{myString | capitalize}}
When importing libraries having to do with formatting, you may want to wrap them up as a filter for ease of use. For example, if we are using moment to format all or many of our dates to relative time, we might create a relativeTime filter.
Element augmentation libraries are slightly more complex to integrate than libraries that don’t touch the DOM — if you’re not careful, Vue and the library can end up at cross purposes, fighting each other for control.
To avoid this, you need to hook the library into Vue’s lifecycle, so it runs after Vue is done manipulating the DOM element, and properly handles updates that Vue instigates.
This could be done in a component, but since these libraries typically touch only a single element at a time, a more flexible approach is to wrap them in a custom directive.
Vue Directives
Vue directives are modifiers that can be used to add behavior to elements in your page. Vue ships with a number of built-in directives that you are likely already comfortable with — things like v-on, v-model, and v-bind. It is also possible to create custom directives that add any sort of behavior to an element — exactly what we’re trying to achieve.
Defining a custom directive is much like defining a component; you create an object with a set of methods corresponding to particular lifecycle hooks, and then add it to Vue either globally by running:
Vue directives have the following hooks available to define behavior. While you can use all of them in a single directive, it is also not uncommon to only need one or two. They are all optional, so use only what you need.
bind(el, binding, vnode)
Called once and only once, when the directive is first bound to an element. This is a good place for one-time setup work, but be cautious, i.e. the element exists, may not yet actually be in the document.
inserted(el, binding, vnode)
Called when the bound element has been inserted into its parent node. This also does not guarantee presence in the document, but does mean if you need to reference the parent you can.
update(el, binding, vnode, oldVnode)
Called whenever the containing component’s VNode has updated. There are no guarantees that other children of the component will have updated, and the value for the directive may or may not have changed. (You can compare binding.value to binding.oldValue to see and optimize away any unnecessary updates.)
componentUpdated(el, binding, vnode, oldVnode)
Similar to update, but called after all children of the containing component have updated. If the behavior of your directive depends on its peers (e.g. v-else), you would use this hook instead of update.
unbind(el, binding, vnode)
Similar to bind, this is called once and only once, when the directive is unbound from an element. This is a good location for any teardown code.
The arguments to these functions are:
el: The element the directive is bound to;
binding: An object containing information about the arguments and value of the directive;
vnode: The virtual node for this element produced by Vue’s compiler;
oldVNode: The previous virtual node, only passed to update and componentUpdated.
Let’s look at an example of doing this type of wrapping using lozad, a lazy-loading library built using the Intersection Observer API. The API for using lozad is simple: use data-src instead of src on images, and then pass a selector or an element to lozad() and call observe on the object that is returned:
const el = document.querySelector('img');
const observer = lozad(el);
observer.observe();
We can do this simply inside of a directive using the bind hook.
We’re not quite done yet though! While this works for an initial load, what happens if the value of the source is dynamic, and Vue changes it? This can be triggered in the pen by clicking the “Swap Sources” button. If we only implement bind, the values for data-src and src are not changed when we want them to be!
To implement this, we need to add an updated hook:
The most complex third-party JavaScript to integrate is that which controls entire regions of the DOM, full-on components and component libraries. These tools expect to be able to create and destroy elements, manipulate them, and more.
For these, the best way to pull them into Vue is to wrap them in a dedicated component, and make extensive use of Vue’s lifecycle hooks to manage initialization, passing data in, and handling events and callbacks.
Our goal is to completely abstract away the details of the third-party library, so that the rest of our Vue code can interact with our wrapping component like a native Vue component.
Component Lifecycle Hooks
To wrap around a more complex component, we’ll need to be familiar with the full complement of lifecycle hooks available to us in a component. Those hooks are:
beforeCreate()
Called before the component is instantiated. Pretty rarely used, but useful if we’re integrating profiling or something similar.
created()
Called after the component is instantiated, but before it is added to the DOM. Useful if we have any one-off setup that doesn’t require the DOM.
beforeMount()
Called just before the component is mounted in the DOM. (Also pretty rarely used.)
mounted()
Called once the component is placed into the DOM. For components and component libraries that assume DOM presence, this is one of our most commonly used hooks.
beforeUpdate()
Called when Vue is about to update the rendered template. Pretty rarely used, but again useful if integrating profiling.
updated()
Called when Vue has finished updating the template. Useful for any re-instantiation that is needed.
beforeDestroy()
Called before Vue tears down a component. A perfect location to call any destruction or deallocation methods on our third-party component
destroyed()
Called after Vue has torn down a component.
Wrapping A Component, One Hook At A Time
Let’s take a look at the popular jquery-multiselect library. There exist many fine multiselect components already written in Vue, but this example gives us a nice combination: complicated enough to be interesting, simple enough to be easy to understand.
The first place to start when implementing a third-party component wrapper is with the mounted hook. Since the third-party component likely expects the DOM to exist before it takes charge of it, this is where you will hook in to initialize it.
For example, to start wrapping jquery-multiselect, we could write:
This is looking pretty good for a start. If there were any teardown we needed to do, we could also add a beforeDestroy hook, but this library does not have any teardown methods that we need to invoke.
Translating Callbacks To Events
The next thing we want to do with this library is add the ability to notify our Vue application when the user selects items. The jquery-multiselect library enables this via callbacks called afterSelect and afterDeselect, but to make this more vue-like, we’ll have those callbacks emit events. We could wrap those callbacks naively as follows:
However, if we insert a logger in the event listeners, we’ll see that this does not provide us a very vue-like interface. After each select or deselect, we receive a list of the values that have changed, but to be more vue-like, we should probably emit a change event with the current list.
We also don’t have a very vue-like way to set values. Instead of this naive approach then, we should look at using these tools to implement something like the v-model approach that Vue provides for native select elements.
Implementing v-model
To implement v-model on a component, we need to enable two things: accepting a value prop that will accept an array and set the appropriate options as selected, and then emit an input event on change that passes the new complete array.
There are four pieces to handle here: initial setup for a particular value, propagate any changes made up to the parent, and handle any changes to value starting outside the component, and finally handle any changes to the content in the slot (the options list).
Let’s approach them one at a time.
Setup With A Value Prop First, we need to teach our component to accept a value prop, and then when we instantiate the multiselect we will tell it which values to select.
Handle Internal Changes To handle changes occurring due to the user interacting with the multiselect, we can go back to the callbacks we explored before — but ‘less naively’ this time. Instead of simply emitting what they send us, we want to turn a new array that takes into account our original value and the change made.
Those callback functions might look a little dense, so let’s break them down a little.
The afterSelect handler concatenates the newly selected value with our existing values, but then just to make sure there are no duplicates, it converts it to a Set (guarantees uniqueness) and then a destructuring to turn it back to an array.
The afterDeselect handler simply filters out any deselected values from the current value list in order to emit a new list.
Handling External Updates To Value The next thing we need to do is to update the selected values in the UI whenever the value prop changes. This involves translating from a declarative change to the props into an imperative change utilizing the functions available on multiselect. The simplest way to do this is to utilize a watcher on our value prop:
watch:
// don't actually use this version. See why below
value() {
$(this.$el).multiselect('select', this.value);
}
}
However, there’s a catch! Because triggering that select will actually result in our onSelect handler, and thus use updating values. If we do this naive watcher, we will end up in an infinite loop./> Luckily,for us, Vue gives us the ability to see the old as well as the new values. We can compare them, and only trigger the select if the value has changed. Array comparisons can get tricky in JavaScript, but for this example, we’ll take advantage of the fact that our arrays are simple (not containing objects) and use JSON stringify to do the comparison. After taking into account that we need to also deselect any that options that have been removed, our final watcher looks like this:
Handling External Updates To Slot We have one last thing that we need to handle: our multiselect is currently utilizing option elements passed in via a slot. If that set of options changes, we need to tell the multiselect to refresh itself, otherwise the new options don’t show up. Luckily, we have both an easy API for this in multiselect (the ‘refresh’ function and an obvious Vue hook to hook into) updated. Handling this last case is as simple as:
Now that we’ve looked at how straightforward it is to utilize third-party JavaScript within Vue, it’s worth discussing drawback of these approaches, and when it appropriate to use them.
Performance Implications
One of the primary drawbacks of utilizing third-party JavaScript that is not written for Vue within Vue is performance — particularly when pulling in components and component libraries or things built using entire additional frameworks. Using this approach can result in a lot of additional JavaScript that needs to be downloaded and parsed by the browser before the user can interact with our application.
For example, by using the multiselect component, we developed above means pulling in not only that component’s code, but all of jQuery as well. That can double the amount of framework related JavaScript our users will have download, just for this one component! Clearly finding a component built natively with Vue.js would be better.
Additionally, when there are large mismatches between the APIs used by third-party libraries and the declarative approach that Vue takes, you may find yourself implementing patterns that result in a lot of extra execution time. Also using the multiselect example, we had to refresh the component (requiring looking at a whole bunch of the DOM) every time a slot changed, while a Vue-native component could utilize Vue’s virtual DOM to be much more efficient in its updates.
When To Use
Utilizing third-party libraries can save you a ton of development time, and often means you’re able to use well-maintained and tested software that you don’t have the expertise to build. The primary drawback is performance, particularly when bringing in large frameworks like jQuery.
For libraries that don’t have those large dependencies, and particularly those that don’t heavily manipulate the DOM, there’s no real reason to favor Vue-specific libraries over more generic ones. Because Vue makes it so easy to pull in other JavaScript, you should go based on your feature and performance needs, simply picking the best tool for the job, without worrying about something Vue-specific.
For more extensive component frameworks, there are three primary cases in which you’d want to pull them in.
Prototyping
In this case, speed of iteration matters far more than user performance; use whatever gets the job done fastest.
Migrating an existing site.
If you’re migrating from an existing site to Vue, being able to wrap whatever framework you’re already using within Vue will give you a graceful migration path so you can gradually pull out the old code piece by piece, without having to do a big bang rewrite.
When the functionality simply isn’t available yet in a Vue component.
If you have a specific and challenging requirement you need to meet, for which a third-party library exists but there isn’t a Vue specific component, by all means consider wrapping the library that does exist.
The first two of these patterns are used all over the open-source ecosystem, so there are a number of different examples you can investigate. Since wrapping an entire complex component or component library tends to be more of a stopgap/migration solution, I haven’t found as many examples of that in the wild, but there are a couple out there, and I’ve used this approach for clients occasionally as requirements have dictated. Here is a quick example of each:
Vue-moment wraps the moment.js library and creates a set of handy Vue filters;
Awesome-mask wraps the vanilla-masker library and creates a directive for masked inputs;
Vue2-foundation wraps up the ZURB Foundation component library inside of Vue components.
Conclusion
The popularity of Vue.js shows no signs of slowing down, with a huge amount of credit being due to the framework’s progressive approach. By enabling incremental adoption, Vue’s progressive nature means that individuals can start using it here and there, a bit at a time, without having to do massive rewrites.
As we’ve looked at here, that progressive nature extends in the other direction as well. Just as you can embed Vue bit by bit in another application, you can embed other libraries bit by bit inside of Vue.
Need some piece of functionality that hasn’t been ported to a Vue component yet? Pull it in, wrap it up, and you’re good to go.
I am no stranger to dogmatic thinking. I was once a very religious missionary, and currently am an open source enthusiast, a web designer, and a gamer. Of all the dogmatic thinkers I’ve encountered in each of those fields, I’m not actually sure which scare me the most. Some are easily identified from a distance, but you never know who compiles their own UNIX-based OS from scratch until it’s too late.
We want to make amazing things, and/or a lot of money, and we often end up fixating rather rigidly on whatever we believe will achieve those goals
No, it’s not a one-to-one comparison, but bear with me here. I’m not saying anyone is likely to be purged in the name of free software, or because they’re on the wrong side of the Warframe vs. Anthem debate. But… it’s not completely outside the realm of possibility. Human beings in general can get a bit intense on occasion.
That’s what (I believe) dogma is, in our modern context: intense and very rigid thinking. Creative people are people of passion and drive. We want to make amazing things, and/or a lot of money, and we often end up fixating rather rigidly on whatever we believe will achieve those goals. Creative though we may be, we are not immune to the universal laws of irony.
The wonderful world of web design is thus a world of goals. We want people to engage, to interact, to stay, to buy, to tell their friends, and to be our friends if it comes to that. And then we want it to be accessible, and usable, but also based on up-to-date frameworks and it just gets confusing sometimes. To stave off confusion, we fall back on what we are pretty sure actually works. We stand our ground on a foundation of facts, and ideas that look a lot like facts, but are actually opinions.
We stand our ground on a foundation of facts, and ideas that look a lot like facts, but are actually opinions
That would be great if this weren’t an industry where the facts change on a daily basis, and everyone seems to have different facts in any case. Hold on too long to any fact or fact-like idea, and you’ll end up just being wrong.
That’s the irony of dogma. We cling to it to protect ourselves from uncertainty and the unknown. Often people cling to dogma in an attempt to shelter their communities from change. The result, of course, is stagnation. Communities, ideas, and industries that don’t evolve will always eventually die. It can take a while, but it’s inevitable.
Dogma kills thought, and halts the processes of mental evolution. Design is pretty much the visual representation of the designer’s thought process, and requires evolution to stay relevant. And so we come back to the title.
Ground Yourself With Principles, not “Facts”
Take browsers (and software in general) as the most blindingly obvious example of this principle: Internet Explorer 6 used to be nearly synonymous with the Internet as a whole. It was what nearly everyone used, and so it was the one browser you had to support. Now it’s Chrome.
As more and more browsers hit the market (there was a small explosion of them in the early-to-mid 00s), some designers and devs started asking questions like, “Well how many of these things are we actually supposed to support?”
They did not like that the answer was, “All of them, sort of.” Cooler heads then pointed out that there were ways to make sure every site you built functioned at some level in every browser. Nowadays we have names for these principles, names like: progressive enhancement, graceful degradation, and “generally not making your whole site depend on something only one browser supports right now”.
The first approach is based on a perceived fact, such as a short list of the “best” browsers to support. The second is based on the idea—the principle—that every site should work on every platform you can reasonably manage, given your resources.
For another example, remember when we stopped asking what resolutions we should be targeting and switched over to responsive design? Yeah, it’s like that. Imagine if we were still endlessly chasing “the ideal resolution”.
What about “big images sell more”? It’s a trend, certainly, but it is not a fact you can always depend on. It might be better to say, “visually arresting design sells more”. In this way, you do not limit yourself to using big images everywhere.
Accept the Fact That Facts Change, and Constantly Double-Check Your Assumptions
With your principles in place as a foundation for your design process, you’re free to follow the facts wherever they might lead you, without fear. However, that freedom comes with a responsibility: you pretty much have to be constantly double checking your information and your assumptions. That doesn’t mean you have to constantly change how you do things, just that you need to keep double checking.
Assumptions, I have found, go hand in hand with dogma as people tend to cling to things that sound true as tightly as they do to anything that actually is true. I’ve done it myself, and it’s always embarrassing when you figure that out later. Check your data, check your facts (there’s a difference, these days), and double-check your assumptions.
The First Changes I’d Make
If you ever find yourself saying anything like, “X framework/CMS/whatever is the best one out there.”, that’s it. Those are the first things I’d check. You may even be right, but you likely won’t be right forever. And even then, whatever you think is “the best thing” may not be “the best thing for the job”.
Check your data, check your facts (there’s a difference, these days), and double-check your assumptions
Then I’d double check any deeply held beliefs you might have about “Users”. Oh, Psychology 101 isn’t going anywhere, but the way we interpret our knowledge of human nature changes over time. Again, check your data, do your research. See what users really do.
Lastly—and I know this isn’t about web design as such—this assumption that logos are better if you drain all personality from them. Plainer is not necessarily better. (Look, I know there were problems with the old Slack logo mark, but this swastika-made-of-phalli thing they have now is not the answer.)