Fonts and icons
Google Sans Flex, the variable typeface of M3 Expressive, ships with the tokens in three profiles; icons are Material Symbols ligatures you can subset to the glyphs you use.
Google Sans Flex
The type scale is set in Google Sans Flex, self-hosted from @kyralabs/m3e-tokens
(DECISIONS D1, D11). Pick one profile: each registers the same
"Google Sans Flex" family, so they replace each other instead of adding up.
Browsers download only the chunks a page's text needs.
| Entry | Variable axes | Latin chunk | All chunks |
|---|---|---|---|
fonts.css |
wght, opsz (default) | 138.8 KiB | 328.5 KiB |
fonts-rond.css |
wght, opsz, ROND | 205.2 KiB | 468.1 KiB |
fonts-full.css |
wght, opsz, ROND, wdth, slnt | 1020.8 KiB | 2026.8 KiB |
@kyralabs/m3exp-web/css/index.css includes fonts.css. The type scale
uses weight only (emphasized styles are heavier) and drives optical size from the font size
with font-optical-sizing: auto; axes a profile drops stay at their defaults, so
every profile renders the type scale identically.
Use another profile
Import the profile with the token and theme stylesheets and the component classes, in place of
css/index.css:
@import '@kyralabs/m3e-tokens/fonts-rond.css'; /* instead of fonts.css */
@import '@kyralabs/m3e-tokens/tokens.css';
@import '@kyralabs/m3e-tokens/theme.css';
@import '@kyralabs/m3e-css/index.css';
Roundness (ROND, 0 to 100) has no CSS property of its own; set it with
font-variation-settings. That property replaces any value it inherits instead of
merging with it, so repeat the other axes you set in the same declaration. No Material source
gives roundness values for the type scale, so the tokens never set it.
.playful {
font-variation-settings: 'ROND' 100;
}
The fonts ship with their licenses beside them in @kyralabs/m3e-tokens/fonts/:
Google Sans Flex under the SIL Open Font License 1.1, Material Symbols under Apache 2.0.
"Google Sans" is a trademark of Google LLC.
Material Symbols
Icons are Material Symbols ligatures (DECISIONS D5): the icon's name, set in the icon font. The default style is Rounded (D12); Outlined and Sharp ship as their own stylesheets. Load one:
@import '@kyralabs/m3e-tokens/material-symbols.css'; /* Rounded */
/* or material-symbols-outlined.css, material-symbols-sharp.css */
Every layer renders the same .m3e-icon span:
<span class="m3e-icon" aria-hidden="true">favorite</span>
<span class="m3e-icon" aria-hidden="true" data-fill>favorite</span>
<m3e-icon name="favorite" fill weight="500"></m3e-icon>
import { Icon } from '@kyralabs/m3exp-web/react';
<Icon name="favorite" fill weight={500} grade={-25} opticalSize={20} />
<Icon name="info" label="Information" /> {/* role="img" with a name */}
Icons are decorative by default (aria-hidden); give an icon that stands alone a
label, or name the control around it. Selected states fill their icon
(FILL 1). The axes come from tokens and can be set per icon with
--m3e-comp-icon-fill, --m3e-comp-icon-weight,
--m3e-comp-icon-grade, --m3e-comp-icon-optical-size, and the size
with --m3e-comp-icon-size.
Ship only your icons
The full variable fonts weigh several megabytes (Rounded: about 5 MB). The
m3e-icons CLI subsets one to the names you list and writes a drop-in
@font-face stylesheet that points .m3e-icon at it:
npm install --save-dev @kyralabs/m3e-icons
npx m3e-icons subset --names home,search,menu --out public/icons.woff2 --css public/icons.css
Load icons.css instead of material-symbols.css. Include the icons
components draw on their own, such as the date picker's arrows or the search bar's back button.
Options: --names-file, --style rounded|outlined|sharp, and
--fill, --wght, --grad, --opsz to pin an
axis or narrow its range for a smaller file. Build scripts can call the exported
subsetIcons() instead.