Technology

An abstraction called text styles – How to save yourself from a typographic mess

May 22, 2019

Read time 9 min

Having worked on different mid-sized websites, I’ve noticed that a large chunk of time developers use writing styles is spent on tweaking text representation. Styling text tends to be quite verbose in CSS, and it’s been a common practice to tie styles to components for the longest time. Eventually, I tired of being slowed down by typographic styles, and started cooking up an idea about abstracting the styling of text out of the components. I was able to speed up development and enhance the readability of style files, and am here to tell you how to do that, too.

Writing and maintaining CSS in large scale is hard. Good abstractions are needed on the styling layer to avoid a situation where lots of styles are written multiple times, by multiple developers, to multiple places. It’s not uncommon to see developers flat out copy and paste CSS from one file to another in order to replicate a certain look. In many projects, keeping styles DRY isn’t as apparent as keeping your logic DRY.

I suggest we look at this from a component perspective. A heading, with its styled variants from h1 to h6, can be seen as a component. Let’s assume that we can use the heading component anywhere by simply importing it. This way, we get a heading with the correct styles in place, and the typographic hierarchy of headings remains consistent across the application. Sweet.

The problem is that we developers get lazy, and we’ll soon start seeing a h4 being used as the heading of a sidebar navigation. Why? Because it happens to “look closest to what’s in the design”. We’re reusing styles (which is good), but it is being done at the expense of the semantics of our HTML structure (which is bad).

In graphic design, it is quite common to combine contradicting properties to create interesting visual effects. Think big but faded away, small but in bright detail. These kind of effects are quite often applied to typographic elements but, again, don’t visually tell much about the semantics of the content they hold. If I would, for example, like to use the text style of the h1 heading in creating another component, that wouldn’t really work – the text style is coupled with the heading component and its semantics.

Pulling typographic styles out of components

I decided to try solving these challenges by decoupling text-related styles from components, and eventually came to make the abstraction I simply call text styles. It consists purely of styles that can be applied to simple text-holding elements like headings, paragraphs, quotes, and the list goes on. These styles sit in a separate file, our text style inventory. This way, we have a single place where all different typography styles are clearly visible. Also, these styles aren’t attached to any semantic elements, not even if their naming refers to semantics. Having all typographic styles in one place yields multiple benefits.


// A naive example of a text styles file.
.txt-style--body-copy {
  @include roboto-regular;
  font-size: 16px;
}

.txt-style--heading {
  @include roboto-bold;
  font-size: 20px;
}

.txt-style--quote {
  font-weight: bold;
  font-style: italic;
  quotes: "“" "”" "‘" "’";
  
  :before {
    content: open-quote;
  }

  :after {
    content: close-quote;
  }
}

First up: when all styles live in one place, it is easy to keep track of what already exists. When writing new components, we can simply pick a text style from our inventory, and apply it to where it’s needed. Having all text styles in one place – and reusing them across the codebase – ultimately results in a smaller amount of CSS, less bytes over the wire. In theory, this approach might give you a small performance benefit.

Another benefit is that the style files for components get smaller, more readable, and more focused on layout and positioning over the visual intricacies of text. Predefined text styles also make development faster, as getting a text style in place is just an extend away (at least if you’re using SASS to implement this approach).

Personally, I like the speed gain of this abstraction so much so that I started writing out text styles before any other aspects of the whole layout. In a way, typography creates the base for the rest of the styles of the site. Creating components later in the process is quicker and we get to focus more on the layout and positioning aspects.

Very little in this world is perfect, and this approach also has its flaws. Quite a few adjustments are required to text styles as the complete layout of the site starts to come together. This is why I warmly recommend working closely with the designers in your team from the get-go. Sharing an understanding of this concept has great potential for a clearer code base and a more logical user interface.    

After using the text styles approach for a while, I noticed that the inventory of text styles usually grows in size over time. Designs usually go through multiple iterations during development, and it is common to find some unaccounted for edge cases. They usually result in more text styles, and this is how it should be.

However, a substantial amount of slightly different text styles is a sign of a lazy design, or a lack of understanding of the medium. Having text styles spread out the codebase not only makes it hard to see how many different representations for text you’ve created, but also makes it harder to challenge the designs in adding unnecessary complexity. Personally, I think that designs can and should be challenged if they don’t have a clear typographic hierarchy in place. More systematic design leads to a more systematic codebase and a better user experience. Good code can’t fix a broken design, and the same applies the other way around. So designers and developers, please work together ❤️.

The concept of responsiveness

Now that we’ve covered text styles 101, let’s talk about responsive design in general. As most developers already know, if a site needs to support mobile devices, all styles should be written mobile first to ensure readability and performance. So, start from mobile, and build towards desktop.

It is important to note that mobile first thinking isn’t only something that developers do. Mobile first is a lens through which the whole project should be looked at. This line of thinking should commence latest at the design phase and, preferably, even before.

Most graphic designers have heard the term ‘mobile first’, but not everyone has actually used it in practice or know the reasoning behind this approach. Whether this is due to an outdated curriculum at school, or because designers often feel more comfortable starting from desktop views, or because clients still expect to see desktop renderings of layouts (even if this doesn’t make sense from a business perspective), I can never be sure.

Again, I’m advocating for tight cooperation. Designers and developers who work in tightly integrated, cross-disciplinary teams, tend to have learned the concept best. Best outcomes occur when the whole team is involved in the project from the beginning, and designers feel comfortable in challenging developers and vice versa. Sharing an understanding of the concept of responsive design is a requirement for designing a functional typographic hierarchy, and thus for creating an effective text styles abstraction.

Responsiveness and text styles

Now, let’s focus on how to adjust text styles responsively. In most cases, text styles need adjustments between different viewports. A well-designed typographic hierarchy takes into account all the ‘main viewport sizes’, and communicates the changes in the specimens across the hierarchy to the developers. Common adjustments between different viewports are, for example, changes in font size, letter spacing, and line height.

Responsive site designs are usually created for mobile, tablet, and desktop versions of the site. Developers are quite well off by creating the text styles mobile first, and then applying the required adjustments for each breakpoint. This works well with the abstraction as adjustments can live within the text styles. Here is an (oversimplified) example:


// Continuing on the previous example by adding some responsiveness.
.txt-style--body-copy {
  @include roboto-regular;
  font-size: 16px;

  @include media-query("desk") {
    font-size: 18px;
  }
}

.txt-style--heading {
  @include roboto-bold;
  font-size: 20px;
  
  @include media-query("lap") {
    font-size: 26px;
  } 

  @include media-query("desk") {
    font-size: 30px;
    letter-spacing: 4px;
  }
}

.txt-style--quote {
  font-weight: bold;
  font-style: italic;
  quotes: "“" "”" "‘" "’";
  
  :before {
    content: open-quote;
  }

  :after {
    content: close-quote;
  }
}

In this example, the text styles are responsive and built mobile first. In other terms, the different aspects of typography are optimised for certain breakpoints. What about the screens that sit between the main viewport sizes, the ones you’ve selected and optimised your design for? I assume there are quite a few of those. The easiest way to account for those screens is to combine responsive and fluid strategies. Adapt the site responsively with breakpoints and use viewport relative units where applicable so the user interface can scale fluidly in response to the viewport size between the breakpoints.

Some aspects of sites are easier to make respond responsively and fluidly to the size of the viewport. To me, typography in all of its delicateness wasn’t one of them. Using relative units with typography has been possible for a long time but having delicate control over it is hard and quite laborious.

Thankfully, there is a way to achieve both the fluidity and the control, without abandoning our new text styles abstraction. Using polyfluid sizing relying in viewport units and linear interpolation, we can create text styles that match the exact sizes defined for selected breakpoints, and are linearly scaled between these breakpoints. This is an example of how the code could look like (in SCSS again). Check out a simple demo mimicking the text styles approach using polyfluid sizing.


// Continuing on the previous example by adding some fluidity addition to the responsiveness.
.txt-style--body-copy {
  @include roboto-regular;
  @include poly-fluid-sizing(
    "font-size", (
      320px: 16px,
      1024px: 18px
    )
  );
}

.txt-style--heading {
  @include roboto-bold;
  @include poly-fluid-sizing(
    "font-size", (
      320px: 20px,
      720px: 26px,
      1024px: 30px
    )
  );
  @include poly-fluid-sizing(
    "letter-spacing", (
      720px: 0px,
      1024px: 4px
    )
  );
}

.txt-style--quote {
  font-weight: bold;
  font-style: italic;
  quotes: "“" "”" "‘" "’";
  
  :before {
    content: open-quote;
  }

  :after {
    content: close-quote;
  }
}

In these examples, we’re using SASS as a preprocessor to keep things readable and hand-written code at a minimum what comes to applying font families, media queries and polyfluid sizing. However, you could use any tool of choice to implement a similar system.

Give the text styles abstraction and polyfluid sizing a go, and see how it works for you. I’d love to hear back from you whether you’re a dev curious designer or a design curious dev. Happy abstracting!

Joonas is a principal consultant at Reaktor Amsterdam. Check out our open positions and come do cool things with him

Never miss a post