Skip to content

Commit b2fd255

Browse files
authored
feat(font tokens): add font family fallbacks (#8389)
**Related Issue:** #7175 ## Summary Add font family fallbacks to tokens and update the transformer to know how to interpret those for Styles
1 parent afbcb09 commit b2fd255

File tree

6 files changed

+149
-23
lines changed

6 files changed

+149
-23
lines changed

packages/calcite-design-tokens/src/core/font.json

+35
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
"core": {
33
"font": {
44
"family": {
5+
"andaleMono": {
6+
"value": "Andale Mono",
7+
"type": "fontFamilies",
8+
"description": "Fallback for Monaco"
9+
},
510
"avenirNextPro": {
611
"value": "Avenir Next LT Pro",
712
"type": "fontFamilies"
@@ -10,9 +15,39 @@
1015
"value": "Avenir Next World",
1116
"type": "fontFamilies"
1217
},
18+
"avenir": {
19+
"value": "Avenir",
20+
"type": "fontFamilies",
21+
"description": "Fallback for Avenir Next"
22+
},
23+
"consolas": {
24+
"value": "Consolas",
25+
"type": "fontFamilies",
26+
"description": "Fallback for Monaco"
27+
},
28+
"helveticaNeue": {
29+
"value": "Helvetica Neue",
30+
"type": "fontFamilies",
31+
"description": "Fallback for Avenir Next"
32+
},
33+
"lucidaConsole": {
34+
"value": "Lucida Console",
35+
"type": "fontFamilies",
36+
"description": "Fallback for Monaco"
37+
},
1338
"monaco": {
1439
"value": "Monaco",
1540
"type": "fontFamilies"
41+
},
42+
"monospace": {
43+
"value": "monospace",
44+
"type": "fontFamilies",
45+
"description": "Fallback for Monaco"
46+
},
47+
"sansSerif": {
48+
"value": "sans-serif",
49+
"type": "fontFamilies",
50+
"description": "Fallback for Avenir Next"
1651
}
1752
},
1853
"style": {

packages/calcite-design-tokens/src/semantic/font.json

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,26 @@
33
"font": {
44
"family": {
55
"default": {
6-
"value": "{core.font.family.avenirNextPro}",
7-
"type": "fontFamilies"
6+
"value": [
7+
"{core.font.family.avenirNextWorld}",
8+
"{core.font.family.avenirNextPro}",
9+
"{core.font.family.avenir}",
10+
"{core.font.family.helveticaNeue}",
11+
"{core.font.family.sansSerif}"
12+
],
13+
"type": "fontFamilies",
14+
"description": "Primary font with fallbacks"
815
},
916
"code": {
10-
"value": "{core.font.family.monaco}",
11-
"type": "fontFamilies"
17+
"value": [
18+
"{core.font.family.monaco}",
19+
"{core.font.family.consolas}",
20+
"{core.font.family.andaleMono}",
21+
"{core.font.family.lucidaConsole}",
22+
"{core.font.family.monospace}"
23+
],
24+
"type": "fontFamilies",
25+
"description": "Font family for code with fallbacks"
1226
}
1327
},
1428
"weight": {

packages/calcite-design-tokens/support/tests/__snapshots__/index.spec.ts.snap

+55-14
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,16 @@ exports[`generated tokens CSS core should match 1`] = `
8686
--calcite-font-weight-thin: 100;
8787
--calcite-font-weight-ultralight: 200; /* only for Avenir Next World (secondary font family) */
8888
--calcite-font-style-italic: italic; /* used in ratings */
89+
--calcite-font-family-sans-serif: sans-serif; /* Fallback for Avenir Next */
90+
--calcite-font-family-monospace: monospace; /* Fallback for Monaco */
8991
--calcite-font-family-monaco: Monaco;
92+
--calcite-font-family-lucida-console: "Lucida Console"; /* Fallback for Monaco */
93+
--calcite-font-family-helvetica-neue: "Helvetica Neue"; /* Fallback for Avenir Next */
94+
--calcite-font-family-consolas: Consolas; /* Fallback for Monaco */
95+
--calcite-font-family-avenir: Avenir; /* Fallback for Avenir Next */
9096
--calcite-font-family-avenir-next-world: "Avenir Next World";
9197
--calcite-font-family-avenir-next-pro: "Avenir Next LT Pro";
98+
--calcite-font-family-andale-mono: "Andale Mono"; /* Fallback for Monaco */
9299
--calcite-container-size-1440: 1440px;
93100
--calcite-container-size-1152: 1152px;
94101
--calcite-container-size-854: 854px;
@@ -438,8 +445,10 @@ exports[`generated tokens CSS global should match 1`] = `
438445
--calcite-font-weight-regular: 400;
439446
--calcite-font-weight-normal: 400; /* For backwards compatibility only. This token will be removed from the published tokens in the next major in favor of the more descriptive word "regular" */
440447
--calcite-font-weight-light: 300; /* For Avenir Next World (secondary font family) */
441-
--calcite-font-family-code: Monaco;
442-
--calcite-font-family: "Avenir Next LT Pro";
448+
--calcite-font-family-code: Monaco Consolas "Andale Mono" "Lucida Console"
449+
monospace; /* Font family for code with fallbacks */
450+
--calcite-font-family: "Avenir Next World" "Avenir Next LT Pro" Avenir
451+
"Helvetica Neue" sans-serif; /* Primary font with fallbacks */
443452
--calcite-container-size-content-fixed: 1440px; /* only for lg breakpoint fixed grid width */
444453
--calcite-container-size-content-fluid: 100%; /* for fluid grid widths */
445454
--calcite-container-size-gutter: 16px;
@@ -727,9 +736,16 @@ export const calciteContainerSize768 = "768px";
727736
export const calciteContainerSize854 = "854px";
728737
export const calciteContainerSize1152 = "1152px";
729738
export const calciteContainerSize1440 = "1440px";
739+
export const calciteFontFamilyAndaleMono = "Andale Mono"; // Fallback for Monaco
730740
export const calciteFontFamilyAvenirNextPro = "Avenir Next LT Pro";
731741
export const calciteFontFamilyAvenirNextWorld = "Avenir Next World";
742+
export const calciteFontFamilyAvenir = "Avenir"; // Fallback for Avenir Next
743+
export const calciteFontFamilyConsolas = "Consolas"; // Fallback for Monaco
744+
export const calciteFontFamilyHelveticaNeue = "Helvetica Neue"; // Fallback for Avenir Next
745+
export const calciteFontFamilyLucidaConsole = "Lucida Console"; // Fallback for Monaco
732746
export const calciteFontFamilyMonaco = "Monaco";
747+
export const calciteFontFamilyMonospace = "monospace"; // Fallback for Monaco
748+
export const calciteFontFamilySansSerif = "sans-serif"; // Fallback for Avenir Next
733749
export const calciteFontStyleItalic = "italic"; // used in ratings
734750
export const calciteFontWeightUltralight = "200"; // only for Avenir Next World (secondary font family)
735751
export const calciteFontWeightThin = "100";
@@ -888,8 +904,8 @@ export const calciteContainerSizeMargin = "24px";
888904
export const calciteContainerSizeGutter = "16px";
889905
export const calciteContainerSizeContentFluid = "100%"; // for fluid grid widths
890906
export const calciteContainerSizeContentFixed = "1440px"; // only for lg breakpoint fixed grid width
891-
export const calciteFontFamily = "Avenir Next LT Pro";
892-
export const calciteFontFamilyCode = "Monaco";
907+
export const calciteFontFamily = ["Avenir Next World","Avenir Next LT Pro","Avenir","Helvetica Neue","sans-serif"]; // Primary font with fallbacks
908+
export const calciteFontFamilyCode = ["Monaco","Consolas","Andale Mono","Lucida Console","monospace"]; // Font family for code with fallbacks
893909
export const calciteFontWeightLight = "300"; // For Avenir Next World (secondary font family)
894910
export const calciteFontWeightNormal = "400"; // For backwards compatibility only. This token will be removed from the published tokens in the next major in favor of the more descriptive word "regular"
895911
export const calciteFontWeightRegular = "400";
@@ -954,15 +970,15 @@ export const calciteSpacingLg = "14px";
954970
export const calciteSpacingXl = "16px";
955971
export const calciteSpacingXxl = "20px";
956972
export const calciteSpacingXxxl = "32px";
957-
export const calciteTypography = {"fontFamily":"Avenir Next LT Pro","fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
973+
export const calciteTypography = {"fontFamily":["Avenir Next World","Avenir Next LT Pro","Avenir","Helvetica Neue","sans-serif"],"fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
958974
export const calciteTypographyLightMinus3h = {"fontSize":"10px","fontWeight":"300","lineHeight":"12px"};
959975
export const calciteTypographyLightMinus2h = {"fontSize":"12px","fontWeight":"300"};
960976
export const calciteTypographyLightMinus1h = {"fontWeight":"300"};
961977
export const calciteTypographyLight0h = {"fontSize":"16px","fontWeight":"300","lineHeight":"20px"};
962978
export const calciteTypographyLight1h = {"fontSize":"18px","fontWeight":"300","lineHeight":"24px"};
963979
export const calciteTypographyRegularMinus3h = {"lineHeight":"12px","fontSize":"10px"};
964980
export const calciteTypographyRegularMinus2h = {"fontSize":"12px"};
965-
export const calciteTypographyRegularMinus1h = {"fontFamily":"Avenir Next LT Pro","fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
981+
export const calciteTypographyRegularMinus1h = {"fontFamily":["Avenir Next World","Avenir Next LT Pro","Avenir","Helvetica Neue","sans-serif"],"fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
966982
export const calciteTypographyRegular0h = {"lineHeight":"20px","fontSize":"16px"};
967983
export const calciteTypographyRegular1h = {"lineHeight":"24px","fontSize":"18px"};
968984
export const calciteTypographyMediumMinus3h = {"fontWeight":"500","lineHeight":"12px","fontSize":"10px"};
@@ -1007,7 +1023,7 @@ export const calciteTypographyHierarchyHeading3 = {"fontWeight":"500","lineHeigh
10071023
export const calciteTypographyHierarchyHeading4 = {"fontWeight":"500","lineHeight":"137.5%","fontSize":"16px"};
10081024
export const calciteTypographyHierarchyHeading5 = {"fontWeight":"500","lineHeight":"137.5%"};
10091025
export const calciteTypographyHierarchyBodySnug = {"lineHeight":"137.5%"};
1010-
export const calciteTypographyHierarchyBody = {"fontFamily":"Avenir Next LT Pro","fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
1026+
export const calciteTypographyHierarchyBody = {"fontFamily":["Avenir Next World","Avenir Next LT Pro","Avenir","Helvetica Neue","sans-serif"],"fontSize":"14px","fontWeight":"400","letterSpacing":"0","lineHeight":"16px","paragraphSpacing":"4px","textCase":"none","textDecoration":"none"};
10111027
export const calciteTypographyHierarchyOverline = {"lineHeight":"12px","textCase":"uppercase","fontWeight":"700"};
10121028
export const calciteTypographyHierarchyCaption = {"lineHeight":"137.5%","fontSize":"12px"};
10131029
export const calciteZIndexDeep = "-999999";
@@ -1105,8 +1121,10 @@ export const calciteContainerSizeGutter : string;
11051121
export const calciteContainerSizeContentFluid : string;
11061122
/** only for lg breakpoint fixed grid width */
11071123
export const calciteContainerSizeContentFixed : string;
1108-
export const calciteFontFamily : string;
1109-
export const calciteFontFamilyCode : string;
1124+
/** Primary font with fallbacks */
1125+
export const calciteFontFamily : string[];
1126+
/** Font family for code with fallbacks */
1127+
export const calciteFontFamilyCode : string[];
11101128
/** For Avenir Next World (secondary font family) */
11111129
export const calciteFontWeightLight : string;
11121130
/** For backwards compatibility only. This token will be removed from the published tokens in the next major in favor of the more descriptive word "regular" */
@@ -1180,15 +1198,15 @@ export const calciteSpacingLg : string;
11801198
export const calciteSpacingXl : string;
11811199
export const calciteSpacingXxl : string;
11821200
export const calciteSpacingXxxl : string;
1183-
export const calciteTypography : { fontFamily: string, fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
1201+
export const calciteTypography : { fontFamily: string[], fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
11841202
export const calciteTypographyLightMinus3h : { fontSize: string, fontWeight: string, lineHeight: string };
11851203
export const calciteTypographyLightMinus2h : { fontSize: string, fontWeight: string };
11861204
export const calciteTypographyLightMinus1h : { fontWeight: string };
11871205
export const calciteTypographyLight0h : { fontSize: string, fontWeight: string, lineHeight: string };
11881206
export const calciteTypographyLight1h : { fontSize: string, fontWeight: string, lineHeight: string };
11891207
export const calciteTypographyRegularMinus3h : { lineHeight: string, fontSize: string };
11901208
export const calciteTypographyRegularMinus2h : { fontSize: string };
1191-
export const calciteTypographyRegularMinus1h : { fontFamily: string, fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
1209+
export const calciteTypographyRegularMinus1h : { fontFamily: string[], fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
11921210
export const calciteTypographyRegular0h : { lineHeight: string, fontSize: string };
11931211
export const calciteTypographyRegular1h : { lineHeight: string, fontSize: string };
11941212
export const calciteTypographyMediumMinus3h : { fontWeight: string, lineHeight: string, fontSize: string };
@@ -1233,7 +1251,7 @@ export const calciteTypographyHierarchyHeading3 : { fontWeight: string, lineHeig
12331251
export const calciteTypographyHierarchyHeading4 : { fontWeight: string, lineHeight: string, fontSize: string };
12341252
export const calciteTypographyHierarchyHeading5 : { fontWeight: string, lineHeight: string };
12351253
export const calciteTypographyHierarchyBodySnug : { lineHeight: string };
1236-
export const calciteTypographyHierarchyBody : { fontFamily: string, fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
1254+
export const calciteTypographyHierarchyBody : { fontFamily: string[], fontSize: string, fontWeight: string, letterSpacing: string, lineHeight: string, paragraphSpacing: string, textCase: string, textDecoration: string };
12371255
export const calciteTypographyHierarchyOverline : { lineHeight: string, textCase: string, fontWeight: string };
12381256
export const calciteTypographyHierarchyCaption : { lineHeight: string, fontSize: string };
12391257
export const calciteZIndexDeep : string;
@@ -1466,9 +1484,23 @@ export const calciteContainerSize768 : string;
14661484
export const calciteContainerSize854 : string;
14671485
export const calciteContainerSize1152 : string;
14681486
export const calciteContainerSize1440 : string;
1487+
/** Fallback for Monaco */
1488+
export const calciteFontFamilyAndaleMono : string;
14691489
export const calciteFontFamilyAvenirNextPro : string;
14701490
export const calciteFontFamilyAvenirNextWorld : string;
1491+
/** Fallback for Avenir Next */
1492+
export const calciteFontFamilyAvenir : string;
1493+
/** Fallback for Monaco */
1494+
export const calciteFontFamilyConsolas : string;
1495+
/** Fallback for Avenir Next */
1496+
export const calciteFontFamilyHelveticaNeue : string;
1497+
/** Fallback for Monaco */
1498+
export const calciteFontFamilyLucidaConsole : string;
14711499
export const calciteFontFamilyMonaco : string;
1500+
/** Fallback for Monaco */
1501+
export const calciteFontFamilyMonospace : string;
1502+
/** Fallback for Avenir Next */
1503+
export const calciteFontFamilySansSerif : string;
14721504
/** used in ratings */
14731505
export const calciteFontStyleItalic : string;
14741506
/** only for Avenir Next World (secondary font family) */
@@ -1649,9 +1681,16 @@ $calcite-font-weight-light: 300; // only for Avenir Next World (secondary font f
16491681
$calcite-font-weight-thin: 100;
16501682
$calcite-font-weight-ultralight: 200; // only for Avenir Next World (secondary font family)
16511683
$calcite-font-style-italic: italic; // used in ratings
1684+
$calcite-font-family-sans-serif: sans-serif; // Fallback for Avenir Next
1685+
$calcite-font-family-monospace: monospace; // Fallback for Monaco
16521686
$calcite-font-family-monaco: Monaco;
1687+
$calcite-font-family-lucida-console: "Lucida Console"; // Fallback for Monaco
1688+
$calcite-font-family-helvetica-neue: "Helvetica Neue"; // Fallback for Avenir Next
1689+
$calcite-font-family-consolas: Consolas; // Fallback for Monaco
1690+
$calcite-font-family-avenir: Avenir; // Fallback for Avenir Next
16531691
$calcite-font-family-avenir-next-world: "Avenir Next World";
16541692
$calcite-font-family-avenir-next-pro: "Avenir Next LT Pro";
1693+
$calcite-font-family-andale-mono: "Andale Mono"; // Fallback for Monaco
16551694
$calcite-container-size-1440: 1440px;
16561695
$calcite-container-size-1152: 1152px;
16571696
$calcite-container-size-854: 854px;
@@ -1994,8 +2033,10 @@ $calcite-font-weight-medium: 500;
19942033
$calcite-font-weight-regular: 400;
19952034
$calcite-font-weight-normal: 400; // For backwards compatibility only. This token will be removed from the published tokens in the next major in favor of the more descriptive word "regular"
19962035
$calcite-font-weight-light: 300; // For Avenir Next World (secondary font family)
1997-
$calcite-font-family-code: Monaco;
1998-
$calcite-font-family: "Avenir Next LT Pro";
2036+
$calcite-font-family-code: Monaco Consolas "Andale Mono" "Lucida Console"
2037+
monospace; // Font family for code with fallbacks
2038+
$calcite-font-family: "Avenir Next World" "Avenir Next LT Pro" Avenir
2039+
"Helvetica Neue" sans-serif; // Primary font with fallbacks
19992040
$calcite-container-size-content-fixed: 1440px; // only for lg breakpoint fixed grid width
20002041
$calcite-container-size-content-fluid: 100%; // for fluid grid widths
20012042
$calcite-container-size-gutter: 16px;

packages/calcite-design-tokens/support/token-transformer/registerCalciteTransformers.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { registerValueRGBA } from "./styleDictionary/transformer/value/valueRGBA
1818
import { registerNameSpacePath } from "./styleDictionary/transformer/name/nameSpacePath.js";
1919
import { registerFormatterDocs } from "./styleDictionary/formatter/docs.js";
2020
import { registerValueToREM } from "./styleDictionary/transformer/value/valueToREM.js";
21+
import { registerValueFontFamilyWithFallbacks } from "./styleDictionary/transformer/value/valueFontFamilyFallbacks.js";
2122

2223
export async function registerCalciteTransformers(sd: StyleDictionary): Promise<void> {
2324
// Here we are registering the Transforms provided by Token Studio however,
@@ -27,21 +28,22 @@ export async function registerCalciteTransformers(sd: StyleDictionary): Promise<
2728
await registerTransforms(sd, {
2829
expand: false,
2930
});
30-
registerValueRGBA(sd);
31-
registerValueEvaluateMath(sd);
3231
registerAttributePlatformNames(sd);
3332
registerCustomJSONParser(sd);
3433
registerFilterSource(sd);
3534
registerFormatterCss(sd);
36-
registerFormatterScss(sd);
35+
registerFormatterDocs(sd);
3736
registerFormatterJs(sd);
37+
registerFormatterScss(sd);
3838
registerNameCamelCase(sd);
3939
registerNameJoinPath(sd);
4040
registerNameKebabCase(sd);
41+
registerNameSpacePath(sd);
4142
registerValueAlignFontWeightAndStyles(sd);
4243
registerValueAssetToken(sd);
44+
registerValueEvaluateMath(sd);
45+
registerValueFontFamilyWithFallbacks(sd);
46+
registerValueRGBA(sd);
4347
registerValueStringWrapper(sd);
44-
registerNameSpacePath(sd);
45-
registerFormatterDocs(sd);
4648
registerValueToREM(sd);
4749
}

packages/calcite-design-tokens/support/token-transformer/styleDictionary/transformer/utils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { valueEvaluateMath } from "./value/valueCheckEvaluateMath.js";
1515
import { CalciteValueRGBA } from "./value/valueRGBA.js";
1616
import { nameSpacePath } from "./name/nameSpacePath.js";
1717
import { CalciteValueToREM } from "./value/valueToREM.js";
18+
import { valueFontFamilyFallbacks } from "./value/valueFontFamilyFallbacks.js";
1819

1920
export type TransformerTypeUnion = `${TransformerTypeEnum}`;
2021

@@ -51,6 +52,7 @@ export const styles = [
5152
valueAssetToken,
5253
valueStringWrapper,
5354
CalciteValueToREM,
55+
valueFontFamilyFallbacks,
5456
nameKebabCase,
5557
];
5658

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Core as StyleDictionary } from "style-dictionary";
2+
import { Matcher } from "style-dictionary/types/Matcher.js";
3+
import { TransformedToken } from "style-dictionary/types/TransformedToken.js";
4+
import { TokenTypes } from "@tokens-studio/types";
5+
6+
import { CalledTransformerFunction, TransformerConfig } from "../utils.js";
7+
8+
export const matcher: Matcher = (token: TransformedToken) => {
9+
return token.type === TokenTypes.FONT_FAMILIES && Array.isArray(token.value);
10+
};
11+
12+
type FontFamilyFallbackToken = TransformedToken & { value: string[] };
13+
14+
export const transformValuesFontFamilyWithFallbacks: CalledTransformerFunction<string> = (
15+
token: FontFamilyFallbackToken
16+
) => {
17+
return token.value.join(" ");
18+
};
19+
20+
export const registerValueFontFamilyWithFallbacks = (sd: StyleDictionary): void => {
21+
const transformerConfig: TransformerConfig = {
22+
name: valueFontFamilyFallbacks,
23+
transformer: transformValuesFontFamilyWithFallbacks,
24+
type: "value",
25+
matcher,
26+
transitive: true,
27+
};
28+
29+
sd.registerTransform(transformerConfig);
30+
};
31+
32+
export const valueFontFamilyFallbacks = "value/calcite/font-family";

0 commit comments

Comments
 (0)