Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scalable way of handling font definitions in React.js application

Usually in my code there are few distinct fonts I'm using with some variants like:

  • Arial, font-weight: 400, font-style: normal
  • Arial, font-weight: 400, font-style: italic
  • Arial, font-weight: 700, font-style: normal
  • Helvetica, font-weight: 400, font-style: normal
  • Helvetica, font-weight: 400, font-style: italic

I'm using CSS-IN-JS library styled-components for that, so not using some styles.css. Sometimes designer comes to me and asks to change e.g. Arial to Comic Sans for font-style: italic; and font-weight: 400;

This can't be code by simple replacing across the project codebase as there are other variants of this font.

I want to minimise the amount of work needed to incorporate that change, thus need to isolate those font objects in one place.

QUESTION :

I've devised 3 approaches for this and 3rd one seems to be the best one, but please advice from your experience. Maybe there are some extra cons or pros to each of those specified below?

APPROACHES :

First approach

I thought about extracting those text definitions into separate style-components, like:

const ArialGeneral = styled.span`
  font-family: Arial;
  font-style: normal;
  font-weight: 400;
`

const ArialNormal = styled(ArialGeneral)``
const ArialNormalItalic = styled(ArialGeneral)`
  font-style: italic;
`

And then wrapping all text occurrences with relevant style.

...
<HeroText>
  <ArialNormal>
    Welcome to our system!
  </ArialNormal>
</HeroText>
...

Cons of this are:

  • Extra JSX tags

  • Maybe some computational cost to re-render those extra components along with CSS computations by browser

Pros:

  • I will have 1 place to govern all occurrences of any given [font-family, font-style, font-weight] combination

Second approach

Use the same technic, but instead of defining styled-components use global styles.css with basically same definitions in form of classes e.g.:

.font-arial-normal {
  font-family: Arial;
  font-style: normal;
  font-weight: 400;
}

This will require to decorate text elements with some classes e.g.:

...
<HeroText className="font-arial-normal">
  Welcome to our system!
</HeroText>
...

Cons:

  • Using two formats CSS-in-JS and styles.css

  • Addition of classNames to JSX

Pros:

  • In contrast with variant 1, will not require resources for additional JSX compilation

  • Same benefit as in variant 1

Third approach

I've see in this article WebType Best practices for using font-weights and also in examples for web-fonts packages I'm downloading that each combination of [font-family, font-style, font-weight] is defined as a separate font e.g. like in this example from mentioned resource (Assuming those are all font-style: normal;):

{ font-family: "Interstate Light"; font-weight: normal; }
{ font-family: "Interstate Medium"; font-weight: normal; }
{ font-family: "Interstate Regular"; font-weight: normal; }
{ font-family: "Interstate Semibold"; font-weight: bold; }
{ font-family: "Interstate Bold"; font-weight: bold; }
{ font-family: "Interstate Extrabold"; font-weight: bold; }
{ font-family: "Interstate Black"; font-weight: bold; }

Cons:

  • Requires more initial work (but those webfont packages already provide the needed css)

Pros:

  • We are not using anything more then fonts.css where we define fonts (I use this in all variants) thus eliminating the using two formats CSS-in-JS and styles.css issues from second method

  • Can be used in JS-IN-CSS as I'm using it now and codebase-wide replace will change only those fonts (what I'm trying to achieve), so the same benefit as in variant 1

like image 811
zmii Avatar asked Jan 23 '18 08:01

zmii


People also ask

Is react scalable?

React is a perfect choice if you expect project maintainability and scalability. It's all thanks to powerful tools and its ecosystem that let teams organize applications in the correct way. A big benefit is that you can start small and add additional, more powerful tools when you need them.


3 Answers

I think the biggest thing you need answer is where the font variants will take place inside your app. Are they global? Do you only need them for certain tags (i.e. h1, h2, etc)? Do you need specific overrides specific to few components?

In my experience, generally you only declare 1 or 2 fonts per app, and these generally tend to be app-wide. Its rare that I see these fonts used very specifically (i.e. 1 or 2 elements with a specific class or ID), and rather tend to be placed on body.

First approach

Assuming that are only using a single font family (i.e. "Arial", "open sans", etc), the first approach will be very hard to scale. As you mentioned, not only does it create a lot of noise, but if you name the component after the font name, it will quickly get out of date the moment you switch font families. This will require quite a few edits to your application. I don't recommend this approach unless you enjoy editing many many files when it comes time to update.

Second approach

This approach is a little bit better since you have now cut down on the HTML noise, but you have gained very little here. You still suffer from the same problem as the first approach, just in a slightly different form. Changing fonts likely means renaming the class selector, which also means updating any component that uses that class name.

Third approach

This is the closet approach to solving your needs in my opinion, though it does seem you want to keep everything inside of styled-components as much as possible. This being the case, I would tweak this approach slightly.

Alternate approach

I believe the @font-face approach paired with styled-component's injectGlobal API is going to get you the most mileage (they even mention @font-face in the docs!)

Swapping out font-families means updating your @font-family definition and updating your font-family declaration all in one go. Better yet, you avoid .css files and keep everything within styled-components!

Again, assuming your goal is to target font app-wide, with maybe a variant on a tag or two (h2, h2, etc), this allows you to make all of your edits in 1 place and I believe best accomplishes your goal.

Take a look at the @font-face documentation to get a feel for declaring your font styles in case you aren't familiar with it. I know there are plenty of resources out there to help guide you declaring @font-face as well (assuming you aren't using something like Google fonts already).

like image 123
jerelmiller Avatar answered Oct 14 '22 04:10

jerelmiller


Maybe you can use css and .extend to solve this problem?

DEMO

import styled, { css } from 'styled-components';

const apply = (tag, styles) => {
  return styled[tag]`${styles}`.extend
};

// BASE STYLES

const arial400 = css`
  font-family: Arial;
  font-weight: 400;
`;

const arial400italic = css`
  ${arial400};
  font-style: italic;
`;

const helveticaNormal = css`
  font-family: Helvetica;
  font-weight: normal;
`;

const helveticaItalicBold = css`
  ${helveticaNormal};
  font-style: italic;
  font-weight: bold;
`;

const HeroText = apply('h2', arial400italic)`
  color: red;
`;

const FancyButton = apply('button', helveticaItalicBold) `
  color: white;
  background: indigo;
  padding: 10px;
`;

...

<div>
  <HeroText>Welcome to our system!</HeroText>
  <FancyButton>CLICK ME</FancyButton>
</div>

...

Another approach?

Important part of this approach is a apply function, that create the new styled-component with a basic styles.

First, you need to describe the basic styles. Second, using the apply function, you simply extend the base style with custom styles which need for specific case (if that needed).

Pros:

  • using weird apply function instead of classic "styled" way

Cons:

  • no require extra JSX tags
  • one place for global styles, which can easily extend each other
  • because global styles still be a "styled components" that means you can use props
  • easy to extend existing styles, all you need just to replace styled.tag to apply('tag', baseStyles)
like image 26
artanik Avatar answered Oct 14 '22 03:10

artanik


I don't entirely agree with the pros and cons stated above. Most of these are related to CSS vs CSS-IN-JS. Having CSS is perfectly fine in styled-components. My thumb rule is to use CSS for generic classes and style-components for theming and variables. Rest use whatever you feel is less awkward and maintainable.

Coming to your specific problem of maintaining fonts and font-weight changes.

I have faced similar issues few times.

Platforms with themes support, design iteration or even at hackathons.

You mostly find yourself changing these with same design language at top level. i.e changing your primary font and weight in the app entirely.

so, Have your fonts defined with @font-face with primary, secondary, tertiary etc instead of specific font-name as you would be have to change just the definition instead of the classnames everywhere.

   @font-face {
     font-family: "primary-default";
     font-weight:  regular
     src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
          url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
   }

If you would have to change primary-default to have a different font-family and weight you can modify these definitions accordingly.

If it is just a specific changes or quick overrides then use helper classes

 .primary-fontStyle {
   font-family: "primary-default";
   font-weight:  regular
 }
like image 29
Sudheer Avatar answered Oct 14 '22 05:10

Sudheer