Typography in Material UI

published 11.6.2020

frontendux design
cssreactmaterial ui

Our UI design team follows Material guidelines, and they do their best to keep up within Typography limitations of Material design system. Nevertheless, they are short of typography styles to communicate UX the way they need to, and so they use additional typography styles. How could we meet UX requirements on UI side, and also keep our code clean?

Material design provides a very nice explanation and reasoning behind its Typography system. Their guideline emphasises the point of not going over board with too many text styles on our webpage. It is a really nice idea and I appreciate the effort to keep designs cleaner and more aligned. However, it can be restrictive - especially on larger projects.

In Material UI library, there is a Typography component to be used for displaying text in different variants. Material UI v4 defines 13 variants, and it is not possible to easily add new variants in a way that would be aligned with Typography component. Based on github issues of Material UI, the plan is to improve this in v5 (with current goal date of Oct 1st 2020). For now though, we need to deal with it ourselves.

On a common websites with few pages of content, or smaller mobile apps, we probably don't need to worry too much and we can get along using Typography component customization. When we work on a larger scale application, or we just need to have more flexibility and customization options, 13 variants might not be good enough.

This is how part of our code could look like, using default typography conventions from Material UI:

<page>
  <Typography variant="h1">Acme online</Typography>
  <Typography variant="h2">Your services provider</Typography>
  <Typography variant="subtitle1">Operating worldwide since 1984</Typography>
  <Typography variant="body1">We have new insights for you, on how to attract more customers.</Typography>
  ...
  <Typography variant="subtitle2">We only had the best experience, and would choose Acme again. John Doe, Woot Inc.</Typography>
</page>

It delivers what it's supposed to. To my taste, it has few disadvantages:

For example, the subtitle2 variant in code above is supposed to be a quote by a customer. We want to have more of those on the page, and we have a clear font style specification provided by our design team. Of course we can theme subtitle2 to make it look right. However, wouldn't it be better to just use quote as a variant value directly? To make it easier to understand what the typography communicates semanticaly? This is not possible with Material UI v4 out of the box.

Another thing is that when we work on a complex UI pages, we don't necessarily want to think on lower level of typography specifics. If we want to use our company tagline somewhere, we don't care whether it's h2 variant, or h3 with some extra props.

One possible improvement to the situation that we've applied on a recent project, is to define specific components to abstract typography details.

<page>
  <Hero>Acme online</Hero>
  <Tagline>Your services provider</Tagline>
  <FactText>Operating worldwide since 1984</FactText>
  <Text>We have new insights for you, on how to attract more customers.</Text>
  ...
  <Quote>We only had the best experience, and would choose Acme again. John Doe, Woot Inc.</Quote>
</page>

The exact naming of these components could be different, to fit whetever conventions the team follows. What's important, is that we've hidden one level of specificity and got onto a higher level of abstraction. When working on overall content and structure of a page, we don't want to get sidetracked with details of how Tagline is defined or styled. And if we want, we always know what component to look for. We don't have to go into source code, where tag line text is defined, to look up what variant of <Typography/> does it use. We can go straight to <Tagline/> and start there.

How do these components look like internally? The nice thing is that they can use <Typography/> under the hood. They are just wrappers. So the benefits of <Typography/> component still apply. And if we get over that magical boundary of 13 variants allowed by Material UI, we can always use our own custom implementation. We have access to theme, so even our custom components can leverage Material UI theming and variables.

Plus, wrappers can still have fall-through properties, and even extend <Typography/> props by our custom features, if needed:

const Text = ({ children, ...restOfProps}) => (
  <Typography variant="body1" {...restOfProps}>
    {children}
  </Typography>
);

const Quote = ({ children }) => (
  <div className={quoteClass}>
    {children}
  </div>
);

When the time comes and Materail UI changes how <Typography/> component works (either changes it's APIs, variant to DOM node mappings, or adds new variants), we would only change these specific components. The rest of our codebase could remain untouched. Or we could migrate to new APIs gradually (by mapping old API props to new ones in the specific components, and clean the code step by step).

How do you deal with additional typography styles in Material UI?