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
+```
+
+
+
+
+ | Name |
+ Value |
+ Comment |
+
+
+ {lineHeightRows}
+
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).
+
+
+
+
+
## Alignment
The Gutenberg editor supports various alignment options for many blocks: