Some simple ways to make content look good

Something non-designers understandably struggle with is how to make things look good. One of those things is long form content that’s well set and readable. Luckily, CSS makes this easy, you just have to know what to change. That’s exactly what we’re going to teach you in this article.

Foundations

We’ve got some semantic HTML here that’s structuring some placeholder content — known as Lorem Ipsum. We’re going to build the look and feel up with CSS, writing very little, with a big impact.

For those who want to build along, here’s all the HTML:

Expand HTML code
<article class="flow">
  <h1>Nulla vitae elit libero, a pharetra augue</h1>
  <p class="lede">Nulla vitae elit libero, a pharetra augue. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Maecenas faucibus mollis interdum. Donec sed odio dui.</p>
  <p>Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Aenean lacinia bibendum nulla sed consectetur.</p>
  <h2>Donec id elit non mi porta gravida at eget metus</h2>
  <p>Aenean eu leo quam. <a href="http://example.com">Pellentesque ornare sem</a> lacinia quam venenatis vestibulum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
  <p>Maecenas sed diam eget risus varius blandit sit amet non magna. Cras mattis consectetur purus sit amet fermentum. Nulla vitae elit libero, a pharetra augue. Donec sed odio dui. Aenean lacinia bibendum nulla sed consectetur. Curabitur blandit tempus porttitor.</p>
  <blockquote>
    <q>Cras justo odio, dapibus ac facilisis in, egestas eget quam. <strong>Sed posuere</strong> consectetur est at lobortis. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</q>
  </blockquote>
  <h3>Sed posuere consectetur est at lobortis</h3>
  <p>Fusce dapibus, <em>tellus ac cursus commodo</em>, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Etiam porta sem malesuada magna mollis euismod. Nulla vitae elit libero, a pharetra augue.</p>
  <h2>Ut fermentum massa justo sit amet risus</h2>
  <ul>
    <li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit</li>
    <li>Aliquam tincidunt mauris eu risus</li>
    <li>Vestibulum auctor dapibus neque</li>
  </ul>
  <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus</p>
</article>

The first thing we’re going to do is apply a light CSS reset. This is a way of levelling the playing field — breaking down some of the styles that all browsers ship with: user agent styles.

I won’t get into the detail of what choices were made in this reset. I wrote about them here instead.

Expand CSS code
/* Box sizing rules */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
ul[role='list'],
ol[role='list'] {
  list-style: none;
}

/* Set core root defaults */
html:focus-within {
  scroll-behavior: smooth;
}

/* Set core body defaults */
body {
  min-height: 100vh;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}

/* A elements that don't have a class get default styles */
a:not([class]) {
  text-decoration-skip-ink: auto;
}

/* Make images easier to work with */
img,
picture {
  max-width: 100%;
  display: block;
}

/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
  font: inherit;
}

/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
  html:focus-within {
   scroll-behavior: auto;
  }
  
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

As you can see, the reset has cut out a lot of the natural spacing, mostly. This is exactly what we want because we’re going to tackle that later in this article.

Core global styles

Now the foundations are in place — semantic HTML and a CSS reset — we can start to build out some visual styles.

We use CUBE CSS here at Set, and one of the key principles of this methodology is to do as much as possible, as high as possible, globally. There’s not a huge amount of CSS to write in this context (because it’s such a powerful language), but we’re still going to stick to that principle.

Just a quick note before we continue, we grabbed Inter from Google fonts.

:root {
  --color-dark: #000000;
  --color-light: #ffffff;
  --color-primary: #1a8fe3;
}

body {
  background: var(--color-light);
  color: var(--color-dark);
  padding: 2em;
  font-family: Georgia, serif;
}

h1,
h2,
h3 {
  font-family: Inter, sans-serif;
  font-weight: 800;
}

ul,
ol {
  padding-inline-start: 1em;
}

blockquote {
  padding-inline-start: 1em;
  border-inline-start: 0.3em solid;
}

As you can see, we’re mainly setting some :root variables/tokens, then styling HTML elements.

Type scale

With more foundations in place, we can start to apply some design foundations. We’ve used type scales in design forever. The main reason is that it’s a great way to get some rhythm and flow going, right off the bat. It also makes decision making easier, along with overall consistency of your design.

Type scales are ratio based, which means each step up the scale is the current step, multiplied by the chosen ratio.

Step 0: 1rem
Step 1: 1.25rem (1 * 1.25)
Step 2: 1.56rem (1.25 * 1.25)
Step 3: 1.95rem (1.56 * 1.25)
Step 4: 2.43rem (1.95 * 1.25)

This is a “Major Third” scale from this excellent tool, which we strongly recommend.

First up, we’ll set these sizes in our variables:

:root {
  --color-dark: #000000;
  --color-light: #ffffff;
  --color-primary: #1a8fe3;
  --size-step-0: 1rem;
  --size-step-1: 1.25rem;
  --size-step-2: 1.56rem;
  --size-step-3: 1.95rem;
  --size-step-4: 2.43rem;
}

Then, apply them to the body, which now looks like this:

body {
  background: var(--color-light);
  color: var(--color-dark);
  padding: 2em;
  font-family: Georgia, serif;
  font-size: var(--size-step-0);
}

Then to the headings, which is a case of stepping down the scale:

h1 {
  font-size: var(--size-step-4);
}

h2 {
  font-size: var(--size-step-3);
}

h3 {
  font-size: var(--size-step-2);
}

Then finally, the blockquote, which now looks like this:

blockquote {
  padding-inline-start: 1em;
  border-inline-start: 0.3em solid;
  font-style: italic;
  font-size: var(--size-step-1);
}

Fluid type

This type scale is already making a great impact on our design, but we can do better. It’d be nicer to have larger type on larger viewports, but maintain this nice Major Third ratio on smaller devices.

Luckily, my personal favourite tool on the web — Utopia — exists, which lets you create fluid type scales. The real magic is that you can create multiple scales for multiple viewports. This is how we achieved large type on large viewports and smaller type on smaller viewports for clients like AHF (massive type in this case), OCEG and Google’s web.dev.

Using this Utopia configuration, we have the same scale for smaller viewports, but a larger “Perfect Fourth” scale on larger viewports.

For our CSS, all we need to do is update the variables!

:root {
  --color-dark: #000000;
  --color-light: #ffffff;
  --color-primary: #1a8fe3;
  --size-step-0: clamp(1rem, calc(0.96rem + 0.22vw), 1.13rem);
  --size-step-1: clamp(1.25rem, calc(1.16rem + 0.43vw), 1.5rem);
  --size-step-2: clamp(1.56rem, calc(1.41rem + 0.76vw), 2rem);
  --size-step-3: clamp(1.95rem, calc(1.71rem + 1.24vw), 2.66rem);
  --size-step-4: clamp(2.44rem, calc(2.05rem + 1.93vw), 3.55rem);
}

Try opening this demo in CodePen and resizing the preview, to see the fluid type in action.

Flow and rhythm

This article is starting to look good! Everything is squashed together vertically still, though, so let’s add some flow and rhythm using my favourite 3 lines of CSS.

.flow > * + * {
  margin-block-start: var(--flow-space, 1em);
}

If you’re interested in how the above CSS works, I wrote about it here, but in short: every direct sibling child element of .flow has margin-block-start added to it. If --flow-space is defined, it uses that, or defaults to 1em, which is relative to the elements font size.

This is the magic, because we’re using a type scale, 1em has a lot of value. It means we get some nice rhythm, just by dropping .flow in our CSS. We can improve it with this little addition too:

:is(h1, h2, h3, blockquote) {
  --flow-space: 1.5em;
}

:is(h1, h2, h3) + * {
  --flow-space: 0.5em;
}

What this does is add more space to the top of headings and block quotes. It also reduces the space between a heading’s direct sibling, so it feels more like a section of content. Again, we’re using em, because it’s relative to the type scale. You can also set space scales with Utopia, which is what we do for client work (also on this site).

Line heights

You might have noticed that the headings look weird. It’s because their line-height (or leading, in design terms), is way too high.

A general rule of thumb is to reduce the line height as the type gets large. Because we’re using Inter as our display font for headings, it has quite shallow ascenders and descenders, which means we can set quite a tight line height:

h1,
h2,
h3 {
  font-family: Inter, sans-serif;
  font-weight: 800;
  line-height: 1.1;
}

Your mileage might vary with line-height and headings, but it’s good to aim for something between 1 and 1.3.

To make the general content read better, we’re going to globally set the line height to 1.7:

body {
  background: var(--color-light);
  color: var(--color-dark);
  padding: 2em;
  font-family: Georgia, serif;
  font-size: var(--size-step-0);
  line-height: 1.7;
}

By setting this on the body, it will be inherited by everything, aside from headings — which have that higher specificity rule. This means we don’t have to keep specifying it on elements and instead, let the browser do the hard work for us. CSS is handy, right?

Line lengths

Sticking with lines, let’s deal with the line lengths. Long lines of text are really hard to read – especially for people with vision and/or cognitive issues. They also don’t look very good visually.

I’d say this is the biggest improvement people in tech (including documentation sites) could do to improve their sites, because they are the most guilty of the crime of long lines.

A good rule of thumb for line lengths of long form content like this is something in the region of 65-75 characters.

Luckily, CSS gives us a very useful unit: the character unit (ch), which is equal to the width of the 0 character, in your chosen font and size. Although specific rigid values are not great for responsive design, a max-width using ch units feels like a great way to responsively set line lengths.

article > * {
  max-width: 65ch;
}

blockquote {
  max-width: 50ch;
}

h1 {
  max-width: 20ch;
}

h2,
h3 {
  max-width: 28ch;
}

The first selector is selecting direct child elements of the article and setting their max-width. Then, just like with line-height, we set shorter widths for larger elements.

Just that bit of CSS has had a massive impact on our page 👇

Colour and contrast

Things are really shaping up now, so it’s all about the attention to detail. First of all, we’ll apply some colour, by tweaking the link styles:

a {
  color: currentColor;
  text-decoration-color: var(--color-primary);
  text-decoration-thickness: 0.3ex;
  text-underline-offset: 0.3ex;
}

This sets the colour of links to be the same as they content they appear in, but applies our primary colour to the underline (always underline your links).

The trick with setting that underline’s thickness and offset is another handy CSS unit: the ex unit. Similar to a ch, this is based on the x height, or the height of a lowercase x character.

The next part is reducing the harshness of the background and text. Right now, it’s black and white, but setting it to off-black and off-white, makes it so much easier to read:

:root {
  --color-dark: #252525;
  --color-light: #efefef;
  --color-primary: #1a8fe3;
  --size-step-0: clamp(1rem, calc(0.96rem + 0.22vw), 1.13rem);
  --size-step-1: clamp(1.25rem, calc(1.16rem + 0.43vw), 1.5rem);
  --size-step-2: clamp(1.56rem, calc(1.41rem + 0.76vw), 2rem);
  --size-step-3: clamp(1.95rem, calc(1.71rem + 1.24vw), 2.66rem);
  --size-step-4: clamp(2.44rem, calc(2.05rem + 1.93vw), 3.55rem);
}

To add a bit more contrast, we’ll style up the lede/intro paragraph:

.lede {
  font-size: var(--size-step-1);
  font-style: italic;
  max-width: 50ch;
}

.lede + * {
  --flow-space: 2em;
}

This makes it bigger and italic (because there is nothing better than italic Georgia). It also reduces the line width (because it’s larger, remember) and adds more --flow-space to elements that follow it.

As you can see, when you build things flexibly and allow specificity and the cascade to do their job, you can make a massive impact with very little CSS!

Text wrap balance

I’ll prefix this part with a warning that at the time of writing, text-wrap: balance is only supported in Chrome Canary with experimental features enabled.

That shouldn’t stop us from using it though, because we’ll use it as a progressive enhancement. When the other browsers support this new property, our page will look better, immediately. It’s also a nice addition, rather than a must have, so again, a progressive enhancement target.

As recommended by the spec writers, this should only be added to smaller chunks of text, so we’re only going to apply it to our headings…

h1,
h2,
h3 {
  font-family: Inter, sans-serif;
  font-weight: 800;
  line-height: 1.1;
  text-wrap: balance;
}

…and our lede:

.lede {
  font-size: var(--size-step-1);
  max-width: 50ch;
  font-style: italic;
  text-wrap: balance;
}

The text-wrap: balance property does exactly what it says on the tin: it balances text for you when it wraps. It’s going to be a great addition to CSS and we strongly recommend that you read this post, by our friend, and web typography expert, Richard Rutter.

A screenshot of our project in Chrome Canary, with more balanced text
For those not using Chrome Canary, here’s a screen shot of this update

Finishing touches

The last finishing touch is to push our article into the middle of the viewport, horizontally. To do that, we’ll adjust the line length rule we set for article earlier:

article {
  max-width: 65ch;
  margin-inline: auto;
}

All we did is remove the > *, which instead of targeting siblings, applies a max-width to the article itself — simplifying and refining what we already had. By setting auto inline margin, the space available is split evenly, pushing the article into the middle of the viewport.

And with that, we are done. All that’s left to do is marvel at our easy to read article that looks great that only took a few lines of CSS to put together!

Here’s a full screen preview of our finished work.


Thanks for reading this Little Design Tip. You can subscribe to these with this RSS feed, or subscribe to all of our posts on the blog with this RSS feed.


Share with your network

Copy this link and send it to your friends 🙂

https://set.studio/some-simple-ways-to-make-content-look-good/

Hey there, we are Set Studio.

We’re a small but nimble, distributed design and development studio

The web is truly global, so our rare combination of highly technical and highly creative designers produce stunning websites that work for everyone.