Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fluid typography: use logarithmic scale factor to calculate a min font size #49707

Merged
merged 10 commits into from
Apr 20, 2023
24 changes: 17 additions & 7 deletions lib/block-supports/typography.php
Original file line number Diff line number Diff line change
Expand Up @@ -481,12 +481,13 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty
$fluid_settings = isset( $typography_settings['fluid'] ) && is_array( $typography_settings['fluid'] ) ? $typography_settings['fluid'] : array();

// Defaults.
$default_maximum_viewport_width = '1600px';
$default_minimum_viewport_width = '768px';
$default_minimum_font_size_factor = 0.75;
$default_scale_factor = 1;
$has_min_font_size = isset( $fluid_settings['minFontSize'] ) && ! empty( gutenberg_get_typography_value_and_unit( $fluid_settings['minFontSize'] ) );
$default_minimum_font_size_limit = $has_min_font_size ? $fluid_settings['minFontSize'] : '14px';
$default_maximum_viewport_width = '1600px';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be more useful referencing the layout.wideSize value from theme.json, so that fonts stops growing when wide width elements are maxed out—as the wideSize size is used as the page width in many cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice how the layout keeps shifting (as the fontSize is still growing), even when the max size is reached:

CleanShot.2023-04-13.at.15.37.49.mp4

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I agree it's definitely something we can and probably should do. It was slated for the original incarnation of fluid font sizes, but I think it fell off radar since we wanted to test the fallback value. From memory it tested pretty well.

I think a manageable approach would be to throw up a new PR using this PR as the base.

👍

$default_minimum_viewport_width = '320px';
$default_minimum_font_size_factor_max = 0.75;
$default_minimum_font_size_factor_min = 0.25;
$default_scale_factor = 1;
$has_min_font_size = isset( $fluid_settings['minFontSize'] ) && ! empty( gutenberg_get_typography_value_and_unit( $fluid_settings['minFontSize'] ) );
$default_minimum_font_size_limit = $has_min_font_size ? $fluid_settings['minFontSize'] : '14px';

// Font sizes.
$fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null;
Expand Down Expand Up @@ -538,7 +539,16 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty
* the given font size multiplied by the min font size scale factor.
*/
if ( ! $minimum_font_size_raw ) {
$calculated_minimum_font_size = round( $preferred_size['value'] * $default_minimum_font_size_factor, 3 );
$preferred_font_size_in_px = 'px' === $preferred_size['unit'] ? $preferred_size['value'] : $preferred_size['value'] * 16;

/*
* The scale factor is a multiplier that affects how quickly the curve will move towards the minimum,
* that is, how quickly the size factor reaches 0 given increasing font size values.
* For a - b * log2(), lower values of b will make the curve move towards the minimum faster.
* The scale factor is constrained between min and max values.
*/
$minimum_font_size_factor = min( max( 1 - 0.075 * log( $preferred_font_size_in_px, 2 ), $default_minimum_font_size_factor_min ), $default_minimum_font_size_factor_max );
$calculated_minimum_font_size = round( $preferred_size['value'] * $minimum_font_size_factor, 3 );

// Only use calculated min font size if it's > $minimum_font_size_limit value.
if ( ! empty( $minimum_font_size_limit ) && $calculated_minimum_font_size <= $minimum_font_size_limit['value'] ) {
Expand Down
3 changes: 1 addition & 2 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ _Returns_

Computes a fluid font-size value that uses clamp(). A minimum and maximum font size OR a single font size can be specified.

If a single font size is specified, it is scaled up and down by minimumFontSizeFactor and maximumFontSizeFactor to arrive at the minimum and maximum sizes.
If a single font size is specified, it is scaled up and down using a logarithmic scale.

_Usage_

Expand All @@ -423,7 +423,6 @@ _Parameters_
- _args.maximumFontSize_ `?string`: Maximum font size for any clamp() calculation. Optional.
- _args.minimumFontSize_ `?string`: Minimum font size for any clamp() calculation. Optional.
- _args.scaleFactor_ `?number`: A scale factor to determine how fast a font scales within boundaries. Optional.
- _args.minimumFontSizeFactor_ `?number`: How much to scale defaultFontSize by to derive minimumFontSize. Optional.
- _args.minimumFontSizeLimit_ `?string`: The smallest a calculated font size may be. Optional.

_Returns_
Expand Down
45 changes: 31 additions & 14 deletions packages/block-editor/src/components/font-sizes/fluid-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@

// Defaults.
const DEFAULT_MAXIMUM_VIEWPORT_WIDTH = '1600px';
const DEFAULT_MINIMUM_VIEWPORT_WIDTH = '768px';
const DEFAULT_MINIMUM_VIEWPORT_WIDTH = '320px';
const DEFAULT_SCALE_FACTOR = 1;
const DEFAULT_MINIMUM_FONT_SIZE_FACTOR = 0.75;
const DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MIN = 0.25;
const DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MAX = 0.75;
const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px';

/**
* Computes a fluid font-size value that uses clamp(). A minimum and maximum
* font size OR a single font size can be specified.
*
* If a single font size is specified, it is scaled up and down by
* minimumFontSizeFactor and maximumFontSizeFactor to arrive at the minimum and
* maximum sizes.
* If a single font size is specified, it is scaled up and down using a logarithmic scale.
*
* @example
* ```js
Expand All @@ -33,14 +32,13 @@ const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px';
* ```
*
* @param {Object} args
* @param {?string} args.minimumViewPortWidth Minimum viewport size from which type will have fluidity. Optional if fontSize is specified.
* @param {?string} args.maximumViewPortWidth Maximum size up to which type will have fluidity. Optional if fontSize is specified.
* @param {string|number} [args.fontSize] Size to derive maximumFontSize and minimumFontSize from, if necessary. Optional if minimumFontSize and maximumFontSize are specified.
* @param {?string} args.maximumFontSize Maximum font size for any clamp() calculation. Optional.
* @param {?string} args.minimumFontSize Minimum font size for any clamp() calculation. Optional.
* @param {?number} args.scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional.
* @param {?number} args.minimumFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional.
* @param {?string} args.minimumFontSizeLimit The smallest a calculated font size may be. Optional.
* @param {?string} args.minimumViewPortWidth Minimum viewport size from which type will have fluidity. Optional if fontSize is specified.
* @param {?string} args.maximumViewPortWidth Maximum size up to which type will have fluidity. Optional if fontSize is specified.
* @param {string|number} [args.fontSize] Size to derive maximumFontSize and minimumFontSize from, if necessary. Optional if minimumFontSize and maximumFontSize are specified.
* @param {?string} args.maximumFontSize Maximum font size for any clamp() calculation. Optional.
* @param {?string} args.minimumFontSize Minimum font size for any clamp() calculation. Optional.
* @param {?number} args.scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional.
* @param {?string} args.minimumFontSizeLimit The smallest a calculated font size may be. Optional.
*
* @return {string|null} A font-size value using clamp().
*/
Expand All @@ -51,7 +49,6 @@ export function getComputedFluidTypographyValue( {
minimumViewPortWidth = DEFAULT_MINIMUM_VIEWPORT_WIDTH,
maximumViewPortWidth = DEFAULT_MAXIMUM_VIEWPORT_WIDTH,
scaleFactor = DEFAULT_SCALE_FACTOR,
minimumFontSizeFactor = DEFAULT_MINIMUM_FONT_SIZE_FACTOR,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will removing this parameter cause any back compat issues, given that getComputedFluidTypographyValue is part of the public block editor API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising that. Yeah, it's possible.

The minimumFontSizeFactor was a value only configurable in the JS function getComputedFluidTypographyValue()

In the PHP, the size factor was hardcoded, and was never an option that could be passed: https://github.com/WordPress/gutenberg/pull/49707/files#diff-2c54d3c8305590cf826f83511a9fd85d5b405470addd62edc3183ff9118177fbL486

In that way, this PR harmonizes the front and backend code 😅 I guess the question is whether there are JS usages out there where one would expect it work as before (in the editor at least).

But my guess is that it'd be relatively safe given that the backend code never allowed customization of this value.

Furthermore, I've removed minimumFontSizeFactor from the JS completely in this PR so passing it won't cause an error.

I can't find any explicit usages on https://wpdirectory.net/, but happy to follow folks' advice if there are backwards compat alarm bells.

Besides this, in general, this PR does represent a change in the way the min values are calculated and therefore has the potential to change the way a site's typography behaves. Also it changes the default min viewport, which will do the same.

minimumFontSizeLimit,
} ) {
// Validate incoming settings and set defaults.
Expand Down Expand Up @@ -106,6 +103,26 @@ export function getComputedFluidTypographyValue( {
* the given font size multiplied by the min font size scale factor.
*/
if ( ! minimumFontSize ) {
const fontSizeValueInPx =
fontSizeParsed.unit === 'px'
? fontSizeParsed.value
: fontSizeParsed.value * 16;

/*
* The scale factor is a multiplier that affects how quickly the curve will move towards the minimum,
* that is, how quickly the size factor reaches 0 given increasing font size values.
* For a - b * log2(), lower values of b will make the curve move towards the minimum faster.
* The scale factor is constrained between min and max values.
*/
const minimumFontSizeFactor = Math.min(
Math.max(
1 - 0.075 * Math.log2( fontSizeValueInPx ),
DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MIN
),
DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MAX
);

// Calculates the minimum font size.
const calculatedMinimumFontSize = roundToPrecision(
fontSizeParsed.value * minimumFontSizeFactor,
3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
maximumFontSize: '45px',
} );
expect( fluidTypographyValues ).toBe(
'clamp(20px, 1.25rem + ((1vw - 7.68px) * 3.005), 45px)'
'clamp(20px, 1.25rem + ((1vw - 3.2px) * 1.953), 45px)'
);
} );

Expand All @@ -33,7 +33,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
fontSize: '30px',
} );
expect( fluidTypographyValues ).toBe(
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 0.901), 30px)'
'clamp(18.959px, 1.185rem + ((1vw - 3.2px) * 0.863), 30px)'
);
} );

Expand All @@ -42,7 +42,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
fontSize: '30px',
} );
expect( fluidTypographyValues ).toBe(
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 0.901), 30px)'
'clamp(18.959px, 1.185rem + ((1vw - 3.2px) * 0.863), 30px)'
);
} );

Expand All @@ -53,7 +53,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
maximumViewPortWidth: '1000px',
} );
expect( fluidTypographyValues ).toBe(
'clamp(22.5px, 1.406rem + ((1vw - 5px) * 1.5), 30px)'
'clamp(18.959px, 1.185rem + ((1vw - 5px) * 2.208), 30px)'
);
} );

Expand All @@ -63,18 +63,7 @@ describe( 'getComputedFluidTypographyValue()', () => {
scaleFactor: '2',
} );
expect( fluidTypographyValues ).toBe(
'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 1.803), 30px)'
);
} );

it( 'should return a fluid font size when given a min and max font size factor', () => {
const fluidTypographyValues = getComputedFluidTypographyValue( {
fontSize: '30px',
minimumFontSizeFactor: '0.5',
maximumFontSizeFactor: '2',
} );
expect( fluidTypographyValues ).toBe(
'clamp(15px, 0.938rem + ((1vw - 7.68px) * 1.803), 30px)'
'clamp(18.959px, 1.185rem + ((1vw - 3.2px) * 1.725), 30px)'
);
} );

Expand Down
Loading