Usually in my code there are few distinct fonts I'm using with some variants like:
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:
[font-family, font-style, font-weight]
combinationSecond approach
Use the same technic, but instead of defining styled-component
s 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:
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
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.
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).
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:
apply
function instead of classic "styled" wayCons:
styled.tag
to apply('tag', baseStyles)
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With