diff --git a/.changeset/fast-mugs-itch.md b/.changeset/fast-mugs-itch.md new file mode 100644 index 000000000..f6ba1dd0f --- /dev/null +++ b/.changeset/fast-mugs-itch.md @@ -0,0 +1,5 @@ +--- +'@cloudfour/patterns': minor +--- + +Add big, small and heading level font size tokens diff --git a/.changeset/olive-berries-watch.md b/.changeset/olive-berries-watch.md new file mode 100644 index 000000000..8fa37461d --- /dev/null +++ b/.changeset/olive-berries-watch.md @@ -0,0 +1,5 @@ +--- +'@cloudfour/patterns': minor +--- + +Add line-height tokens diff --git a/.changeset/two-ghosts-punch.md b/.changeset/two-ghosts-punch.md new file mode 100644 index 000000000..9fe4f3d7f --- /dev/null +++ b/.changeset/two-ghosts-punch.md @@ -0,0 +1,5 @@ +--- +'@cloudfour/patterns': minor +--- + +Add font-size utilities for Gutenberg block editor diff --git a/.changeset/violet-schools-mix.md b/.changeset/violet-schools-mix.md new file mode 100644 index 000000000..576f087c9 --- /dev/null +++ b/.changeset/violet-schools-mix.md @@ -0,0 +1,5 @@ +--- +'@cloudfour/patterns': patch +--- + +Fix issue where Gutenberg quote paragraphs were incorrectly sized diff --git a/.style-dictionary/build.js b/.style-dictionary/build.js index 797f038d2..0c9895a85 100644 --- a/.style-dictionary/build.js +++ b/.style-dictionary/build.js @@ -49,6 +49,20 @@ StyleDictionary.registerTransform({ }, }); +/** + * Custom Transform: Re-join negative number name segments + * Looks for occurrences of `n-{number}` and removes the `-`. Useful for keeping + * negative number modular scale step token names consistent with related class + * names. + */ +StyleDictionary.registerTransform({ + name: 'custom/name/i/kebab-rejoin-n', + type: 'name', + transformer: function (prop) { + return prop.name.replace(/-n-(\d)/g, '-n$1'); + }, +}); + /** * Custom Transform Group: CSS * This is a modified version of the CSS transform group without the time, @@ -57,7 +71,12 @@ StyleDictionary.registerTransform({ */ StyleDictionary.registerTransformGroup({ name: 'custom/transform-group/css', - transforms: ['attribute/cti', 'name/cti/kebab', 'color/css'], + transforms: [ + 'attribute/cti', + 'name/cti/kebab', + 'custom/name/i/kebab-rejoin-n', + 'color/css', + ], }); /** @@ -67,7 +86,12 @@ StyleDictionary.registerTransformGroup({ */ StyleDictionary.registerTransformGroup({ name: 'custom/transform-group/css-category', - transforms: ['attribute/cti', 'custom/name/ti/kebab', 'color/css'], + transforms: [ + 'attribute/cti', + 'custom/name/ti/kebab', + 'custom/name/i/kebab-rejoin-n', + 'color/css', + ], }); /** @@ -77,7 +101,12 @@ StyleDictionary.registerTransformGroup({ */ StyleDictionary.registerTransformGroup({ name: 'custom/transform-group/css-category-type', - transforms: ['attribute/cti', 'custom/name/i/kebab', 'color/css'], + transforms: [ + 'attribute/cti', + 'custom/name/i/kebab', + 'custom/name/i/kebab-rejoin-n', + 'color/css', + ], }); /** diff --git a/.style-dictionary/config.js b/.style-dictionary/config.js index 2c22c5732..9bb80bd16 100644 --- a/.style-dictionary/config.js +++ b/.style-dictionary/config.js @@ -82,6 +82,13 @@ module.exports = { attributes: { category: 'number', type: 'font_weight' }, }, }, + { + destination: '_line-height.scss', + format: 'scss/variables', + filter: { + attributes: { category: 'number', type: 'line_height' }, + }, + }, { destination: '_opacity.scss', format: 'scss/variables', diff --git a/src/base/_defaults.scss b/src/base/_defaults.scss index 0f849fd11..85b1a35e8 100644 --- a/src/base/_defaults.scss +++ b/src/base/_defaults.scss @@ -258,7 +258,7 @@ figure { } figcaption { - font-size: ms.step(-1); + font-size: size.$font-small; font-style: italic; text-align: center; } diff --git a/src/base/_typography.scss b/src/base/_typography.scss index 799e704d5..2dbfb1c6a 100644 --- a/src/base/_typography.scss +++ b/src/base/_typography.scss @@ -1,6 +1,7 @@ @use "_fonts"; @use "../compiled/tokens/scss/breakpoint"; @use "../compiled/tokens/scss/font-family"; +@use "../compiled/tokens/scss/line-height"; @use "../mixins/fluid"; @use "../mixins/headings"; @use "../mixins/ms"; @@ -31,7 +32,7 @@ html { body { font-family: font-family.$sans-fallback; - line-height: ms.step(2, 1); + line-height: line-height.$loose; text-size-adjust: none; /* 1 */ word-wrap: break-word; /* 2 */ diff --git a/src/components/calendar-date/calendar-date.scss b/src/components/calendar-date/calendar-date.scss index 79d3f0aee..fa3463a79 100644 --- a/src/components/calendar-date/calendar-date.scss +++ b/src/components/calendar-date/calendar-date.scss @@ -1,5 +1,6 @@ @use "../../compiled/tokens/scss/color"; @use "../../compiled/tokens/scss/font-weight"; +@use "../../compiled/tokens/scss/line-height"; @use "../../compiled/tokens/scss/size"; @use '../../mixins/ms'; @use '../../mixins/theme'; @@ -25,7 +26,7 @@ $radius: size.$border-radius-medium; flex-direction: column; font-weight: font-weight.$medium; height: $size; - line-height: ms.step(1, 1); + line-height: line-height.$tight; text-align: center; user-select: none; width: $size; @@ -108,7 +109,7 @@ $radius: size.$border-radius-medium; */ .c-calendar-date__note { - font-size: ms.step(-1); + font-size: size.$font-small; overflow: hidden; /* 1 */ padding: 0 size.$spacing-control-text-inset; text-overflow: ellipsis; /* 1 */ diff --git a/src/components/ground-nav/ground-nav.scss b/src/components/ground-nav/ground-nav.scss index 99883a84b..4e88b6dab 100644 --- a/src/components/ground-nav/ground-nav.scss +++ b/src/components/ground-nav/ground-nav.scss @@ -128,7 +128,7 @@ $_ground-nav-border-color: color.$base-gray-light; .c-ground-nav__kudos, .c-ground-nav__legal { - font-size: ms.step(-1); + font-size: size.$font-small; } /** diff --git a/src/components/heading/heading.scss b/src/components/heading/heading.scss index c78a90c58..0ad5bc22c 100644 --- a/src/components/heading/heading.scss +++ b/src/components/heading/heading.scss @@ -48,11 +48,12 @@ $max-width-permalink-shift: math.div($min-width-permalink-shift * 16 - 1, 16); * We start at `-2` so template designers have three sizes larger than a default * `

` to apply thoughtfully within templates. * - * We end at 3 because that is the maximum depth we support with distinct - * visual differences. + * Even though distinct sizes end at level 3, we output modifier classes for all + * 6 levels to encourage semantic sizing that will be future-friendly if that + * ever changes. */ -@for $level from -2 through headings.$max-level { +@for $level from -2 through 6 { $level-segment: ms.step-class-segment($level); .c-heading--level-#{$level-segment} { diff --git a/src/components/message/message.scss b/src/components/message/message.scss index bf0d90574..25500ca3d 100644 --- a/src/components/message/message.scss +++ b/src/components/message/message.scss @@ -1,6 +1,7 @@ @use "../../compiled/tokens/scss/brightness"; @use "../../compiled/tokens/scss/color"; @use "../../compiled/tokens/scss/ease"; +@use "../../compiled/tokens/scss/line-height"; @use "../../compiled/tokens/scss/transition"; @use "../../compiled/tokens/scss/scale"; @use "../../compiled/tokens/scss/size"; @@ -18,7 +19,7 @@ color: color.$text-dark; display: flex; /* 1 */ justify-content: space-between; /* 1 */ - line-height: ms.step(1, 1); /* 2 */ + line-height: line-height.$tight; /* 2 */ } .c-message__content { @@ -42,7 +43,7 @@ color: inherit; /* 1 */ cursor: pointer; display: flex; - font-size: ms.step(1); /* 2 */ + font-size: size.$font-big; /* 2 */ margin-left: ms.step(1); /* 3 */ padding: 0 ms.step(1); diff --git a/src/components/pagination/pagination.scss b/src/components/pagination/pagination.scss index 54ddc3288..44e1e56a7 100644 --- a/src/components/pagination/pagination.scss +++ b/src/components/pagination/pagination.scss @@ -3,6 +3,7 @@ @use "../../compiled/tokens/scss/color"; @use "../../compiled/tokens/scss/ease"; @use "../../compiled/tokens/scss/font-weight"; +@use "../../compiled/tokens/scss/line-height"; @use "../../compiled/tokens/scss/scale"; @use "../../compiled/tokens/scss/size"; @use "../../compiled/tokens/scss/transition"; @@ -233,7 +234,7 @@ $indicator-width-current: $indicator-height-current; font-weight: font-weight.$medium; height: ($action-size - $action-pad * 2); /* 1 */ justify-content: center; - line-height: ms.step(0, 1); /* 2 */ + line-height: line-height.$tighter; /* 2 */ max-width: ($action-size - $action-pad * 2); /* 1 */ position: relative; /* 3 */ width: 100%; /* 1 */ diff --git a/src/components/sky-nav/sky-nav.scss b/src/components/sky-nav/sky-nav.scss index 35229f553..6a8126dfb 100644 --- a/src/components/sky-nav/sky-nav.scss +++ b/src/components/sky-nav/sky-nav.scss @@ -3,6 +3,7 @@ @use "../../compiled/tokens/scss/breakpoint"; @use "../../compiled/tokens/scss/color"; @use "../../compiled/tokens/scss/ease"; +@use "../../compiled/tokens/scss/line-height"; @use "../../compiled/tokens/scss/opacity"; @use "../../compiled/tokens/scss/scale"; @use "../../compiled/tokens/scss/size"; @@ -378,9 +379,9 @@ $_masthead-height-sm: ms.step(7); .c-sky-nav__menu-action { align-items: center; /* 1 */ display: flex; /* 2 */ - font-size: ms.step(1); + font-size: size.$font-big; justify-content: center; /* 1 */ - line-height: ms.step(1, 1); + line-height: line-height.$tight; text-align: center; /* 1 */ text-decoration: none; transition: opacity transition.$quick ease.$out; diff --git a/src/mixins/_headings.scss b/src/mixins/_headings.scss index 4fb70d862..1f4bc2788 100644 --- a/src/mixins/_headings.scss +++ b/src/mixins/_headings.scss @@ -1,39 +1,49 @@ @use "../compiled/tokens/scss/breakpoint"; @use "../compiled/tokens/scss/font-weight"; +@use "../compiled/tokens/scss/line-height"; +@use "../compiled/tokens/scss/size"; @use 'fluid'; @use 'ms'; +@use 'sass:map'; +@use 'sass:meta'; -$max-level: 3; -$fluid-steps: 1; +$size-tokens: meta.module-variables('size'); -/** - * Mixin for applying heading styles at a particular level. Accepts an argument - * so it may be easily baked into "for" loops. The size jumps and responsive - * behavior are determined mathematically, so the level may be lower than 1 for - * constructing larger heading classes. - */ - -@mixin level($level) { - @if $level <= $max-level { - $min-step: $level * -1 + $max-level; - $max-step: $min-step + $fluid-steps; +/// Mixin for applying heading styles at a particular level. Accepts an argument +/// so it may be easily baked into "for" loops. +/// @param {number} $level +@mixin level($level, $include-weight: true) { + // Build token name to check for + $token-name-root: 'font-heading-' + ms.step-class-segment($level); + // Get minimum size token + $min-size: map.get($size-tokens, $token-name-root + '-min'); + // Get maximum size token + $max-size: map.get($size-tokens, $token-name-root + '-max'); + // If tokens were found... + @if $min-size and $max-size { + // Output a fluid font size @include fluid.font-size( breakpoint.$xs, breakpoint.$l, - ms.step($min-step), - ms.step($max-step) + $min-size, + $max-size ); + // Line heights above standard heading levels should be tighter @if $level < 1 { - line-height: ms.step(0, 1); + line-height: line-height.$tighter; } @else { - line-height: ms.step(1, 1); + line-height: line-height.$tight; } } @else { + // Otherwise, assume this is a lower heading level font-size: inherit; line-height: inherit; } - font-weight: font-weight.$bold; + @if $include-weight { + // Consistently bold headings to differentiate from normal copy + font-weight: font-weight.$bold; + } } diff --git a/src/mixins/_table.scss b/src/mixins/_table.scss index 801324d53..d2e1119f3 100644 --- a/src/mixins/_table.scss +++ b/src/mixins/_table.scss @@ -58,7 +58,7 @@ $table-border-thick: size.$edge-medium solid $table-border-color; @mixin t-caption { caption-side: bottom; color: var(--theme-color-table-caption); - font-size: ms.step(-1); + font-size: size.$font-small; font-style: italic; padding: ms.step(-1) 0; } diff --git a/src/tokens/line-height.stories.mdx b/src/tokens/line-height.stories.mdx new file mode 100644 index 000000000..efe09eca6 --- /dev/null +++ b/src/tokens/line-height.stories.mdx @@ -0,0 +1,40 @@ +import { Meta } from '@storybook/addon-docs/blocks'; +import tokens from '../compiled/tokens/js/tokens'; +const lineHeightRows = Object.entries(tokens.number.line_height).map( + ([key, { name, value, comment, attributes }]) => ( + + + line-height.${attributes.item} + + + {value} + + {comment} + + ) +); + + + +# Line Height + +```scss +@use "../../compiled/tokens/scss/line-height"; +$example: line-height.$loose; // => 1.5625 +``` + +```javascript +import tokens from '../../compiled/tokens/js/tokens'; +console.log(tokens.number.line_height.loose.value); // => 1.5625 +``` + + + + + + + + + + {lineHeightRows} +
NameValueComment
diff --git a/src/tokens/number/line-height.js b/src/tokens/number/line-height.js new file mode 100644 index 000000000..50659d0c2 --- /dev/null +++ b/src/tokens/number/line-height.js @@ -0,0 +1,21 @@ +const { modularScale } = require('../../scripts/modular-scale'); + +module.exports = { + number: { + line_height: { + loose: { + value: modularScale(2), + comment: 'For multiline copy.', + }, + tight: { + value: modularScale(1), + comment: 'For headings and single-line text.', + }, + tighter: { + value: modularScale(0), + comment: + 'For very large headings or UI elements with built-in height or padding.', + }, + }, + }, +}; diff --git a/src/tokens/size/font.js b/src/tokens/size/font.js new file mode 100644 index 000000000..471bbb616 --- /dev/null +++ b/src/tokens/size/font.js @@ -0,0 +1,18 @@ +const { modularEm } = require('../../scripts/modular-scale'); + +module.exports = { + size: { + font: { + big: { + value: modularEm(1), + comment: + 'Slightly larger than normal. Good for short introductory sections or important actions.', + }, + small: { + value: modularEm(-1), + comment: + 'Slightly smaller than usual. Good for badges, captions or other space-constrained bits of copy.', + }, + }, + }, +}; diff --git a/src/tokens/size/heading.js b/src/tokens/size/heading.js new file mode 100644 index 000000000..f64604625 --- /dev/null +++ b/src/tokens/size/heading.js @@ -0,0 +1,38 @@ +const { modularEm } = require('../../scripts/modular-scale'); + +// Minimum heading level to output tokens for (`-2` means three above `h1`) +const minLevel = -2; +// Maximum heading level to output tokens for (`3` means `h3`) +const maxLevel = 3; +// Modular scale steps to increase from minimum to maximum values +const fluidSteps = 1; + +// Store heading token groups here +const headingTokens = {}; + +// Generate heading tokens +for (let level = minLevel; level <= maxLevel; level++) { + // Replace `-` with `n` in token names to avoid awkward case across languages + const levelSuffix = `${level}`.replace('-', 'n'); + // Determine minimum size based on current level + const minStep = level * -1 + maxLevel; + // Determine maximum size based on minimum size + const maxStep = minStep + fluidSteps; + // Store `max` and `min` tokens + headingTokens[`heading_${levelSuffix}`] = { + max: { + value: modularEm(maxStep), + comment: `Maximum fluid size for heading level ${level}.`, + }, + min: { + value: modularEm(minStep), + comment: `Minimum fluid size for heading level ${level}.`, + }, + }; +} + +module.exports = { + size: { + font: headingTokens, + }, +}; diff --git a/src/vendor/wordpress/demo/font-size.twig b/src/vendor/wordpress/demo/font-size.twig new file mode 100644 index 000000000..9f69fa02d --- /dev/null +++ b/src/vendor/wordpress/demo/font-size.twig @@ -0,0 +1,7 @@ +

+ We help forward-thinking teams craft accessible design systems and progressive web apps. +

diff --git a/src/vendor/wordpress/styles/_core-blocks.scss b/src/vendor/wordpress/styles/_core-blocks.scss index 90c487d64..7f2436f0a 100644 --- a/src/vendor/wordpress/styles/_core-blocks.scss +++ b/src/vendor/wordpress/styles/_core-blocks.scss @@ -1,5 +1,6 @@ @use "sass:math"; @use "../../../compiled/tokens/scss/breakpoint"; +@use "../../../compiled/tokens/scss/line-height"; @use "../../../compiled/tokens/scss/size"; @use "../../../mixins/button"; @use "../../../mixins/table"; @@ -174,11 +175,13 @@ $wp-button-gap: size.$spacing-gap-button-group-default; */ /** - * Makes the line-height more consistent with existing quote styles. + * Makes the line-height more consistent with existing typographic styles. */ -.wp-block-quote, +.wp-block-quote.is-large, .wp-block-pullquote { - line-height: size.$height-control-multiline; + p { + line-height: line-height.$tight; + } } .wp-block-pullquote { diff --git a/src/vendor/wordpress/styles/_utilities.scss b/src/vendor/wordpress/styles/_utilities.scss index ee0e09135..68daaea32 100644 --- a/src/vendor/wordpress/styles/_utilities.scss +++ b/src/vendor/wordpress/styles/_utilities.scss @@ -2,6 +2,8 @@ @use "../../../compiled/tokens/scss/breakpoint"; @use "../../../compiled/tokens/scss/color-base"; @use "../../../compiled/tokens/scss/size"; +@use '../../../mixins/headings'; +@use '../../../mixins/ms'; // These utility classes may apply to anything created within the Gutenberg // editor (but not necessarily Gutenberg blocks). @@ -55,3 +57,22 @@ $color-map: meta.module-variables('color-base'); color: $value; } } + +/// Utilities for Block Font Sizes +/// @link https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/#block-font-sizes + +.has-big-font-size { + font-size: size.$font-big; +} + +.has-small-font-size { + font-size: size.$font-small; +} + +@for $level from -2 through 3 { + $level-segment: ms.step-class-segment($level); + + .has-heading-#{$level-segment}-font-size { + @include headings.level($level, false); + } +} diff --git a/src/vendor/wordpress/utilities.stories.mdx b/src/vendor/wordpress/utilities.stories.mdx index 6d0bee25a..bba2c3298 100644 --- a/src/vendor/wordpress/utilities.stories.mdx +++ b/src/vendor/wordpress/utilities.stories.mdx @@ -2,6 +2,7 @@ import { Story, Canvas, Meta, ArgsTable } from '@storybook/addon-docs/blocks'; import { kebabCase } from 'lodash'; import tokens from '../../compiled/tokens/js/tokens.js'; import colorDemo from './demo/color.twig'; +import fontSizeDemo from './demo/font-size.twig'; const baseColorTokenKeys = Object.keys(tokens.color.base).map(kebabCase); const colorControlConfig = { type: { name: 'string' }, @@ -10,6 +11,23 @@ const colorControlConfig = { options: ['', ...baseColorTokenKeys], }, }; +const fontSizeControlConfig = { + type: { name: 'string' }, + control: { + type: 'select', + options: [ + '', + 'big', + 'small', + 'heading-n2', + 'heading-n1', + 'heading-0', + 'heading-1', + 'heading-2', + 'heading-3', + ], + }, +}; @@ -43,6 +61,26 @@ When a background color is chosen, rounded corners and padding may be added (dep +## Font Size + +We include `has-{font-size}-font-size` classes for our `big`, `small` and heading level [size tokens](/docs/design-tokens-size--page) to support [the `editor-font-sizes` feature](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/#block-font-sizes). + + + + {(args) => fontSizeDemo(args)} + + + + + ## Alignment The Gutenberg editor supports various alignment options for many blocks: