From 84ebf2835186cdf196c0acbc1e7a52f5695b25bd Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Wed, 19 Oct 2022 12:25:21 +0000 Subject: [PATCH] Editor: Add font size constraints for fluid typography. This commit: * Adds default minimum font size limits so that min font size, where provided, does not become smaller than `14px`/`0.875rem`/`0.875em`. * For font sizes of `< 14px` that have no defined minimum sizes, uses the font size to set the floor of the `clamp()` value. This bugfix prevents converting existing small font sizes to clamp values that will reduce their font size even further in narrow widths. It therefore improves backward compatibility and accessibility. Original PR from Gutenberg repository: * [https://github.com/WordPress/gutenberg/pull/44993 #44993 Fluid typography: add font size constraints] Follow-up to [54260], [54360], [54497], [54500]. Props ramonopoly, andrewserong, isabel_brison, Joen, bernhard-reiter. See #56467. git-svn-id: https://develop.svn.wordpress.org/trunk@54646 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-supports/typography.php | 83 +++++++++--- .../tests/block-supports/typography.php | 120 +++++++++++++++--- 2 files changed, 171 insertions(+), 32 deletions(-) diff --git a/src/wp-includes/block-supports/typography.php b/src/wp-includes/block-supports/typography.php index c54c740e58b08..2a1d90f55efea 100644 --- a/src/wp-includes/block-supports/typography.php +++ b/src/wp-includes/block-supports/typography.php @@ -119,11 +119,11 @@ function wp_apply_typography_support( $block_type, $block_attributes ) { $custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] ) ? $block_attributes['style']['typography']['fontSize'] : null; - $typography_block_styles['fontSize'] = $preset_font_size ? $preset_font_size : wp_get_typography_font_size_value( - array( - 'size' => $custom_font_size, - ) - ); + $typography_block_styles['fontSize'] = $preset_font_size ? $preset_font_size : wp_get_typography_font_size_value( + array( + 'size' => $custom_font_size, + ) + ); } if ( $has_font_family_support && ! $should_skip_font_family ) { @@ -348,8 +348,17 @@ function wp_get_typography_value_and_unit( $raw_value, $options = array() ) { $unit = $options['coerce_to']; } + /* + * No calculation is required if swapping between em and rem yet, + * since we assume a root size value. Later we might like to differentiate between + * :root font size (rem) and parent element font size (em) relativity. + */ + if ( ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) && ( 'em' === $unit || 'rem' === $unit ) ) { + $unit = $options['coerce_to']; + } + return array( - 'value' => $value, + 'value' => round( $value, 3 ), 'unit' => $unit, ); } @@ -380,13 +389,16 @@ function wp_get_computed_fluid_typography_value( $args = array() ) { $minimum_font_size_raw = isset( $args['minimum_font_size'] ) ? $args['minimum_font_size'] : null; $scale_factor = isset( $args['scale_factor'] ) ? $args['scale_factor'] : null; - // Grab the minimum font size and normalize it in order to use the value for calculations. + // Normalizes the minimum font size in order to use the value for calculations. $minimum_font_size = wp_get_typography_value_and_unit( $minimum_font_size_raw ); - // We get a 'preferred' unit to keep units consistent when calculating, otherwise the result will not be accurate. + /* + * We get a 'preferred' unit to keep units consistent when calculating, + * otherwise the result will not be accurate. + */ $font_size_unit = isset( $minimum_font_size['unit'] ) ? $minimum_font_size['unit'] : 'rem'; - // Grab the maximum font size and normalize it in order to use the value for calculations. + // Normalizes the maximum font size in order to use the value for calculations. $maximum_font_size = wp_get_typography_value_and_unit( $maximum_font_size_raw, array( @@ -394,12 +406,12 @@ function wp_get_computed_fluid_typography_value( $args = array() ) { ) ); - // Protect against unsupported units. + // Checks for mandatory min and max sizes, and protects against unsupported units. if ( ! $maximum_font_size || ! $minimum_font_size ) { return null; } - // Use rem for accessible fluid target font scaling. + // Uses rem for accessible fluid target font scaling. $minimum_font_size_rem = wp_get_typography_value_and_unit( $minimum_font_size_raw, array( @@ -427,8 +439,9 @@ function wp_get_computed_fluid_typography_value( $args = array() ) { */ $view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit; $linear_factor = 100 * ( ( $maximum_font_size['value'] - $minimum_font_size['value'] ) / ( $maximum_viewport_width['value'] - $minimum_viewport_width['value'] ) ); - $linear_factor = round( $linear_factor, 3 ) * $scale_factor; - $fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor)"; + $linear_factor_scaled = round( $linear_factor * $scale_factor, 3 ); + $linear_factor_scaled = empty( $linear_factor_scaled ) ? 1 : $linear_factor_scaled; + $fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)"; return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)"; } @@ -478,6 +491,7 @@ function wp_get_typography_font_size_value( $preset, $should_use_fluid_typograph $default_minimum_font_size_factor = 0.75; $default_maximum_font_size_factor = 1.5; $default_scale_factor = 1; + $default_minimum_font_size_limit = '14px'; // Font sizes. $fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null; @@ -499,13 +513,48 @@ function wp_get_typography_font_size_value( $preset, $should_use_fluid_typograph return $preset['size']; } - // If no fluid min or max font sizes are available, create some using min/max font size factors. + // If no fluid max font size is available, create one using max font size factor. + if ( ! $maximum_font_size_raw ) { + $maximum_font_size_raw = round( $preferred_size['value'] * $default_maximum_font_size_factor, 3 ) . $preferred_size['unit']; + } + + // If no fluid min font size is available, create one using min font size factor. if ( ! $minimum_font_size_raw ) { - $minimum_font_size_raw = ( $preferred_size['value'] * $default_minimum_font_size_factor ) . $preferred_size['unit']; + $minimum_font_size_raw = round( $preferred_size['value'] * $default_minimum_font_size_factor, 3 ) . $preferred_size['unit']; } - if ( ! $maximum_font_size_raw ) { - $maximum_font_size_raw = ( $preferred_size['value'] * $default_maximum_font_size_factor ) . $preferred_size['unit']; + // Normalizes the minimum font size limit according to the incoming unit, so we can perform checks using it. + $minimum_font_size_limit = wp_get_typography_value_and_unit( + $default_minimum_font_size_limit, + array( + 'coerce_to' => $preferred_size['unit'], + ) + ); + + if ( ! empty( $minimum_font_size_limit ) ) { + /* + * If a minimum size was not passed to this function + * and the user-defined font size is lower than $minimum_font_size_limit, + * then use the user-defined font size as the minimum font-size. + */ + if ( ! isset( $fluid_font_size_settings['min'] ) && $preferred_size['value'] < $minimum_font_size_limit['value'] ) { + $minimum_font_size_raw = implode( '', $preferred_size ); + } else { + $minimum_font_size_parsed = wp_get_typography_value_and_unit( + $minimum_font_size_raw, + array( + 'coerce_to' => $preferred_size['unit'], + ) + ); + + /* + * If the passed or calculated minimum font size is lower than $minimum_font_size_limit + * use $minimum_font_size_limit instead. + */ + if ( ! empty( $minimum_font_size_parsed ) && $minimum_font_size_parsed['value'] < $minimum_font_size_limit['value'] ) { + $minimum_font_size_raw = implode( '', $minimum_font_size_limit ); + } + } } $fluid_font_size_value = wp_get_computed_fluid_typography_value( diff --git a/tests/phpunit/tests/block-supports/typography.php b/tests/phpunit/tests/block-supports/typography.php index e2e5a69766447..fb97de4deb6eb 100644 --- a/tests/phpunit/tests/block-supports/typography.php +++ b/tests/phpunit/tests/block-supports/typography.php @@ -363,11 +363,11 @@ public function data_generate_font_size_preset_fixtures() { 'default_return_value_when_value_is_already_clamped' => array( 'font_size_preset' => array( - 'size' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'size' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', 'fluid' => false, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', ), 'default_return_value_with_unsupported_unit' => array( @@ -384,7 +384,7 @@ public function data_generate_font_size_preset_fixtures() { 'size' => '1.75rem', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(1.3125rem, 1.3125rem + ((1vw - 0.48rem) * 2.524), 2.625rem)', + 'expected_output' => 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 2.523), 2.625rem)', ), 'return_fluid_value_with_floats_with_units' => array( @@ -392,7 +392,7 @@ public function data_generate_font_size_preset_fixtures() { 'size' => '100.175px', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.13125px, 4.695703125rem + ((1vw - 7.68px) * 9.03), 150.2625px)', + 'expected_output' => 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 9.03), 150.263px)', ), 'return_fluid_value_with_integer_coerced_to_px' => array( @@ -400,7 +400,7 @@ public function data_generate_font_size_preset_fixtures() { 'size' => 33, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(24.75px, 1.546875rem + ((1vw - 7.68px) * 2.975), 49.5px)', + 'expected_output' => 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 2.975), 49.5px)', ), 'return_fluid_value_with_float_coerced_to_px' => array( @@ -408,7 +408,7 @@ public function data_generate_font_size_preset_fixtures() { 'size' => 100.23, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.1725px, 4.69828125rem + ((1vw - 7.68px) * 9.035), 150.345px)', + 'expected_output' => 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 9.035), 150.345px)', ), 'return_default_fluid_values_with_empty_fluid_array' => array( @@ -417,7 +417,7 @@ public function data_generate_font_size_preset_fixtures() { 'fluid' => array(), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', ), 'return_default_fluid_values_with_null_value' => array( @@ -426,7 +426,19 @@ public function data_generate_font_size_preset_fixtures() { 'fluid' => null, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + ), + + 'return_clamped_value_if_min_font_size_is_greater_than_max' => array( + 'font_size_preset' => array( + 'size' => '3rem', + 'fluid' => array( + 'min' => '5rem', + 'max' => '32px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(5rem, 5rem + ((1vw - 0.48rem) * -5.769), 32px)', ), 'return_size_with_invalid_fluid_units' => array( @@ -441,7 +453,15 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '10em', ), - 'return_fluid_clamp_value' => array( + 'return_clamped_size_where_no_min_is_given_and_less_than_default_min_size' => array( + 'font_size_preset' => array( + 'size' => '3px', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(3px, 0.188rem + ((1vw - 7.68px) * 0.18), 4.5px)', + ), + + 'return_fluid_clamp_value_with_different_min_max_units' => array( 'font_size_preset' => array( 'size' => '28px', 'fluid' => array( @@ -472,7 +492,77 @@ public function data_generate_font_size_preset_fixtures() { ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 7.091), 80px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 7.091), 80px)', + ), + + 'should_adjust_computed_min_in_px_to_min_limit' => array( + 'font_size_preset' => array( + 'size' => '14px', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', + ), + + 'should_adjust_computed_min_in_rem_to_min_limit' => array( + 'font_size_preset' => array( + 'size' => '1.1rem', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 1.49), 1.65rem)', + ), + + 'default_return_clamp_value_with_replaced_fluid_min_value_in_em' => array( + 'font_size_preset' => array( + 'size' => '1.1em', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875em, 0.875rem + ((1vw - 0.48em) * 1.49), 1.65em)', + ), + + 'should_adjust_fluid_min_value_in_px_to_min_limit' => array( + 'font_size_preset' => array( + 'size' => '20px', + 'fluid' => array( + 'min' => '12px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 1.923), 30px)', + ), + + 'should_adjust_fluid_min_value_in_rem_to_min_limit' => array( + 'font_size_preset' => array( + 'size' => '1.5rem', + 'fluid' => array( + 'min' => '0.5rem', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 2.644), 2.25rem)', + ), + + 'should_adjust_fluid_min_value_but_honor_max_value' => array( + 'font_size_preset' => array( + 'size' => '1.5rem', + 'fluid' => array( + 'min' => '0.5rem', + 'max' => '5rem', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 7.933), 5rem)', + ), + + 'should_return_fluid_value_when_min_and_max_font_sizes_are_equal' => array( + 'font_size_preset' => array( + 'size' => '4rem', + 'fluid' => array( + 'min' => '30px', + 'max' => '30px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(30px, 1.875rem + ((1vw - 7.68px) * 1), 30px)', ), ); } @@ -547,7 +637,7 @@ public function data_generate_block_supports_font_size_fixtures() { 'return_value_with_fluid_typography' => array( 'font_size_value' => '50px', 'should_use_fluid_typography' => true, - 'expected_output' => 'font-size:clamp(37.5px, 2.34375rem + ((1vw - 7.68px) * 4.507), 75px);', + 'expected_output' => 'font-size:clamp(37.5px, 2.344rem + ((1vw - 7.68px) * 4.507), 75px);', ), ); } @@ -623,13 +713,13 @@ public function data_generate_replace_inline_font_styles_with_fluid_values_fixtu 'block_content' => '

A paragraph inside a group

', 'font_size_value' => '20px', 'should_use_fluid_typography' => true, - 'expected_output' => '

A paragraph inside a group

', + 'expected_output' => '

A paragraph inside a group

', ), 'return_content_with_first_match_replace_only' => array( - 'block_content' => "
\n \n

A paragraph inside a group

", - 'font_size_value' => '1em', + 'block_content' => "
\n \n

A paragraph inside a group

", + 'font_size_value' => '1.5em', 'should_use_fluid_typography' => true, - 'expected_output' => "
\n \n

A paragraph inside a group

", + 'expected_output' => "
\n \n

A paragraph inside a group

", ), ); }