community-group
community-group copied to clipboard
Type: font family
A naive approach like the one below may be appropriate for the first stage of the specification, but this may be more complicated than it seems due to platform/OS/browser restrictions.
Please share feedback, ideas and concerns in the issue below so we can assess the best course of action.
Font name
Represents a font name or an array of font names (ordered from most to least preferred). The type property must be set to the string “font”. The value must either be a string value containing a single font name or an array of strings, each being a single font name. For example:
{
"Primary font": {
"value": "Comic Sans MS",
"type": "font"
},
"Body font": {
"value": ["Helvetica", "Arial"],
"type": "font"
}
}
Just sharing my current setup. We use tokens for both Figma and Web platforms. Figma requires the "font name" to be specified so it can also use the loadFontAsync()
function, so we store both the name and the CSS string for font-family
.
I'd imagine this could be more explicit by maybe including vendor
in there, so it's clearer what it's used for, but putting it out there for the discussion.
type FontFamilyData = {
fontName: string; // For Figma
fontFamily: string; // For Web
};
interface FontFamilyToken extends BaseToken {
name: string;
description: string;
data: FontFamilyData;
}
const FontFamilies: { [x: string]: FontFamilyToken } = {
sans: {
name: "sans",
description: "Sans-serif font used for all UI text.",
data: {
fontName: "SF Pro Text",
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
},
},
sansDisplay: {
name: "sans-display",
description: "Display variant of sans-serif font.",
data: {
fontName: "SF Pro Display",
fontFamily:
'-apple-system-headline, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif',
},
},
};
export default FontFamilies;
Reviewed by the spec editors on 2021-10-19. No further changes to the spec decided based on current discussion. Leaving this thread open for further discussion.
I am very interested to see how this evolves. Are styles and weights on the same level of as Font Family or are they descendants? As the file format of fonts couples them together (and to a certain extent brand manuals, "we only use Helvetica Bold") but from a token perspective they can be independent due to how they get composed in the front-end.
Some challenges I have had in the past with Sketch: As one may set their weights and styles at a token level outside of a particular typeface as "bold" but some typefaces have different values for bold e.g. 700/800. Or in some cases Thin vs Light being the same numerical value and falling back to Thin. Or the family, weight and style being one and the same e.g. A bold italic font having its weight set at "normal" and style to"regular".
Just posting a small update since things have moved on a bit since this issue was opened. The latest draft spec has:
- Added a
fontWeight
type - Renamed
font
tofontFamily
- Added a
typography
composite type (and we are collecting feedback on that chapter here: #102)
Looking at @kaelig's OP, I think the original issue is still unresolved. While updates to the spec draft have addressed things like font weight and typography styles, I think the spec needs to still clarify the following for the fontFamily
type:
- What do the font names listed as the value(s) of a
fontFamily
actually represent? (and how do they map to actual font files or pre-installed fonts on a user's system?) - Should our spec define a set of generic font families (like CSS has) that can be used? What would that mean for platforms and design tools that don't have that concept?
- What is the expected behaviour in a tool or platform that does not support CSS-like font stacks, when it encounters a
fontFamily
token with an array value?
FWIW, my suggestions for each are as follows:
1) Meaning of fontFamily
values
Fonts are typically made up of several font faces, which are different combinations of weight (bold, light, regular, etc.) and/or style (italic or normal). In many cases, there are individual font files for each of the faces, though in some cases there may be a single variable font file that covers all of those faces.
In CSS, a "font family" is a name assigned to a group of related font faces. When styling text in CSS, the font family is combined with font style & weight properties and the browser / OS then figures out how to display that (which will often involve mapping the font face back to the corresponding font file and loading that). In CSS, authors can define that mapping themselves via @font-face
rules. In the absence of a @font-face
for the specified font family, browsers will instead look at the fonts installed on the user's system.
Based a quick read of their docs, it looks like Android has a similar concept of font families. Developers can create font family resources in a similar fashion to @font-face
rules in CSS.
Design tools like Figma & Sketch also seem to have a similar concept (thought they don't explicitly call it font families). When styling text, designers typically choose a "font" from a dropdown. However, the listed options are really font families as they will be things like "Times New Roman" and not "Times New Roman Bold, Times New Roman Bold Italic", etc. Once a font has been selected, other controls are used to pick the weight and style.
This gives me some confidence that font families is the right concept to be expressing as a token, rather than font faces.
How tools & platforms actually map a font family + weight + style back to a downloaded / bundled / pre-installed font file does seem to vary quite a bit. However, it looks like it is possible for teams to set things up so that the font family a designer picks from the dropdown in their design tool, matches the font family a developer writes into their code. So, using such names as the value of a token to be shared by all of them seems reasonable to me.
As far as our spec is concerned, I think we just need to do the following:
- Clarify that values of
fontFamily
tokens are names of font families and not names of individual faces within a family - Add some examples (like mine above) to illustrate how this is represented in different tools and platforms
- Clarify that the definition of the font family (e.g. CSS
@font-face
rules, Android fot family resources, etc. or how a pre-installed system font is picked given a font family name) is out of scope for this spec. (Maybe add a suggestion that teams should aim to set things up across design & dev that the font family names match everywhere though)
2) Generic font families
I'm aware of some design systems that intentionally use system font stacks (for example booking.com), so I think this is a concept folks should be able to express in a design token. I.e. you're saying something like "use whatever the user's default monospace font is" rather than "use Courier New, regardless of what system the user has". Those a very different design decisions.
My suggestion would be to just align with the set of generic font families that CSS has. Perhaps we can add some examples to illustrate how those might be interpreted on non-CSS platforms. E.g. an iOS implementation of a font family design token whose value is a sans-serif
might simply be to (explicitly) set the font to be "San Francisco".
As far as I know, current design tools do not have this concept. Designers always need to pick an explicit font family. If design tools want to keep it that way, I think there are ways they can work around this when importing/exporting DTCG files though. For example:
- When exporting
fontFamily
tokens that were created inside the design tool, only ever export explicit font family names. - When importing
fontFamily
tokens from elsewhere that happen to use a generic font family value, map that to one of the available explict fonts wherever the font family value is displayed or altered by the user.- However, if subsequently exporting tokens it should preserve the original generic font family value (assuming the user never altered the token value). This way the design decision to use a generic font family does not get lost.
- Optionally, a design tool may append data to the token
s
$extensions` property to specify what explicit font was displayed in place of the generic one. That might be useful if tokens are later used by designers in other tools or on other OSes. Assuming they support the same extension, they could then display the same font even if they might otherwise map the generic name to something else.
3) Font stack behaviour
I believe the behaviour with font stacks should be similar to 2). Platforms and tools that do not have this notion should do the following:
- When reading a
fontFamily
token with an array value, simply take the first item in that array and ignore the rest - When editing a token, replace the array value with the new, single font family value the user specified
- Ideally, they may display a warning message to the user to alert them to the fact that they are replacing a font stack with a single font.
My suggestion would be to just align with the set of generic font families that CSS has.
How is the difference made between "sans-serif"
(custom font family name) and sans-serif
(generic font family)?
When exposing these values to CSS we need to be able to generate either an ident
or a string
token.
Hi @kaelig , I think we need to separate the fontWeight
vs fontStyle
,
where the fontWeight we can keep this value as a number (ex: 400
/500
/600
)
then each fontWeight has a different fontStyle (ex: italic
/normal
).