Typography in Design Systems (rebound)

About a week ago, I read Dan Mall's article, Typography in Design Systems, and I've been meaning to write a quick rebound post to expand on the section about the system in code with something I've been doing in my own work.

In the original article, Dan shows how he uses Sass mixins to create a core set of sizes to use throughout the project, creating a consistent set of rules for type sizes and also makes it really simple to maintain. Now, I don't know if what I'm about to talk about is something Dan already does but omitted from the examples for simplicity, but I also place media query rules in the mixins also to create a consistent set of sizes for the core types of screen size.

I just want to take a step back to explain how I got here and why I started developing like this. When I was at an agency our client began sending designs through for desktop, tablet and mobile. In the very early days, there were a few inconsistencies. For example, one desktop design would have a heading set at something like 32px, which then resized to 24px on tablet and 18px on mobile. Another design would have a heading at 32px on desktop, 28px on tablet and 22px on mobile. It was a challenge to manage in code and it was also a challenge to manage at the design phase too. I worked with the designer to identify the issues and we agreed on a core set of sizes. I know Dan isn't a fan of the xs, s, m, l, xl, xxl naming system but we only had about 5 or 6 core sizes so we ran with that (but Dan does make a good case for scalability which I hadn't considered at the time and I'll get to shortly).

Once we'd nailed down the sizes we needed and how they display at the three target scenarios, it was just a case of generating a _typography.scss file in the codebase. I was also making use of Bulma to handle core layout so to ensure I was working consistently, I also utilised Bulma's breakpoint rules, as you'll see in the following example:

@mixin xxlarge {
    font-size: 35px;

    @include tablet {
        font-size: 40px;
    }

    @include desktop {
        font-size: 48px;
    }
}

@mixin xlarge {
    font-size: 22px;

    @include tablet {
        font-size: 28px;
    }

    @include desktop {
        font-size: 35px;
    }
}

@mixin large {
    font-size: 18px;

    @include tablet {
        font-size: 24px;
    }
}

...

For the purpose of simplifying the example, I removed line-height settings and things but you could, of course, add these in.

So now I've got a set of mixins available to use throughout my Sass files. One thing I also did was create a set of global classes which are then available for those occasions when you only need to just resize a bit of text but don't want to create a brand new class for the job.

.is-xxlarge {
    @include xxlarge;
}

.is-xlarge {
    @include xlarge;
}

.is-large {
    @include large;
}

...

Issues with scalability

As mentioned above, the original article makes a case for staying away from xs, s, m etc, and with good reason. I recently had the problem of needing to introduce a new size which would sit between large and xlarge . It's a case of bumping things about a bit: upping the xlarge mixin name to xxlarge, updating the global class too, then doing a find and replace in the codebase. This then creates the room for your new size which will now take the xlarge name. While fairly easy to manage, it is a bit clunky. When working with a small team, it's easy to explain the change and get people on board but for larger scale projects, I can definitely see the problems or confusion that could occur with this.

Evolution...

Others will no doubt have been employing something similar (or better) in their own code. What I've employed on projects for the last year or so might not work for others, it may not work for me long-term depending on what projects I end up working on. I guess this just reminds me that there isn't necessarily a right or wrong way of doing things, just different ways. Ways which are constantly evolving.

For my part, as I move to a new company (at the time of writing, starting tomorrow) with a brand new codebase, I will carry this approach with me but take the opportunity to really focus on the best way for me to deploy this technique in a way that allows the code to be easily maintainable with minimal impact on the output for the end user. I'll see where this evolution takes me and if I arrive anywhere worthwhile, I'll do a follow-up post when I get there.

If you've used this sort of method or have an even better way, feel free to tweet me and let me know!