design-tokens / community-group

This is the official DTCG repository for the design tokens specification.
https://tr.designtokens.org
Other
1.49k stars 60 forks source link

Type: font family #53

Open kaelig opened 2 years ago

kaelig commented 2 years ago

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"
  }
}
lucastobrazil commented 2 years ago

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;
kevinmpowell commented 2 years ago

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.

tbrasington commented 2 years ago

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".

c1rrus commented 2 years ago

Just posting a small update since things have moved on a bit since this issue was opened. The latest draft spec has:

c1rrus commented 2 years ago

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:

  1. 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?)
  2. 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?
  3. 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:

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:

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:

romainmenke commented 2 years ago

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.

mnrendra commented 1 year ago

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).