-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Block supports: add fluid typography #39529
Changes from all commits
5901eaa
7cb5b09
6b96585
1182f26
3998a1d
82caf16
3b7c42d
9efbd8b
f2d1668
15403eb
912b80e
69168ba
fed9f2f
dd25039
aa67638
9304829
e74b07b
00c51d1
eea3aeb
15b5bee
62beef5
edeaca7
ded379d
62fa6f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -222,6 +222,200 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu | |
return sprintf( '%s:var(--wp--preset--%s--%s);', $css_property, $css_property, $slug ); | ||
} | ||
|
||
/** | ||
* Internal method that checks a string for a unit and value and returns an array consisting of `'value'` and `'unit'`, e.g., [ '42', 'rem' ]. | ||
* | ||
* @access private | ||
* | ||
* @param string $raw_value Raw size value from theme.json. | ||
* @param array $options array( | ||
* 'coerce_to' => (string) Coerce the value to rem or px. Default `'rem'`. | ||
* 'root_size_value' => (number) Value of root font size for rem|em <-> px conversion. Default `16`. | ||
* 'acceptable_units' => (array) An array of font size units. Default `[ 'rem', 'px', 'em' ]`; | ||
* );. | ||
* @return array An array consisting of `'value'` and `'unit'`, e.g., [ '42', 'rem' ] | ||
*/ | ||
function gutenberg_get_typography_value_and_unit( $raw_value, $options = array() ) { | ||
if ( empty( $raw_value ) ) { | ||
return null; | ||
} | ||
|
||
$defaults = array( | ||
'coerce_to' => '', | ||
'root_size_value' => 16, | ||
'acceptable_units' => array( 'rem', 'px', 'em' ), | ||
); | ||
|
||
$options = wp_parse_args( $options, $defaults ); | ||
|
||
$acceptable_units_group = implode( '|', $options['acceptable_units'] ); | ||
$pattern = '/^(\d*\.?\d+)(' . $acceptable_units_group . '){1,1}$/'; | ||
|
||
preg_match( $pattern, $raw_value, $matches ); | ||
|
||
// We need a number value and a px or rem unit. | ||
if ( ! isset( $matches[1] ) || ! isset( $matches[2] ) ) { | ||
return null; | ||
} | ||
|
||
$value = $matches[1]; | ||
$unit = $matches[2]; | ||
|
||
// Default browser font size. Later we could inject some JS to compute this `getComputedStyle( document.querySelector( "html" ) ).fontSize`. | ||
if ( 'px' === $options['coerce_to'] && ( 'em' === $unit || 'rem' === $unit ) ) { | ||
$value = $value * $options['root_size_value']; | ||
$unit = $options['coerce_to']; | ||
} | ||
|
||
if ( 'px' === $unit && ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) ) { | ||
$value = $value / $options['root_size_value']; | ||
$unit = $options['coerce_to']; | ||
} | ||
|
||
return array( | ||
'value' => $value, | ||
'unit' => $unit, | ||
); | ||
} | ||
|
||
/** | ||
* Internal implementation of clamp() based on available min/max viewport width, and min/max font sizes. | ||
* | ||
* @access private | ||
* | ||
* @param array $args array( | ||
* 'maximum_viewport_width' => (string) Maximum size up to which type will have fluidity. | ||
* 'minimum_viewport_width' => (string) Minimum viewport size from which type will have fluidity. | ||
* 'maximum_font_size' => (string) Maximum font size for any clamp() calculation. | ||
* 'minimum_font_size' => (string) Minimum font size for any clamp() calculation. | ||
* 'scale_factor' => (number) A scale factor to determine how fast a font scales within boundaries. | ||
* );. | ||
* @return string|null A font-size value using clamp(). | ||
*/ | ||
function gutenberg_get_computed_fluid_typography_value( $args = array() ) { | ||
$maximum_viewport_width_raw = isset( $args['maximum_viewport_width'] ) ? $args['maximum_viewport_width'] : null; | ||
$minimum_viewport_width_raw = isset( $args['minimum_viewport_width'] ) ? $args['minimum_viewport_width'] : null; | ||
$maximum_font_size_raw = isset( $args['maximum_font_size'] ) ? $args['maximum_font_size'] : null; | ||
$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. | ||
$minimum_font_size = gutenberg_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. | ||
$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. | ||
$maximum_font_size = gutenberg_get_typography_value_and_unit( | ||
$maximum_font_size_raw, | ||
array( | ||
'coerce_to' => $font_size_unit, | ||
) | ||
); | ||
|
||
// Protect against unsupported units. | ||
if ( ! $maximum_font_size || ! $minimum_font_size ) { | ||
return null; | ||
} | ||
|
||
// Use rem for accessible fluid target font scaling. | ||
$minimum_font_size_rem = gutenberg_get_typography_value_and_unit( | ||
$minimum_font_size_raw, | ||
array( | ||
'coerce_to' => 'rem', | ||
) | ||
); | ||
|
||
// Viewport widths defined for fluid typography. Normalize units. | ||
$maximum_viewport_width = gutenberg_get_typography_value_and_unit( | ||
$maximum_viewport_width_raw, | ||
array( | ||
'coerce_to' => $font_size_unit, | ||
) | ||
); | ||
$minimum_viewport_width = gutenberg_get_typography_value_and_unit( | ||
$minimum_viewport_width_raw, | ||
array( | ||
'coerce_to' => $font_size_unit, | ||
) | ||
); | ||
|
||
// Build CSS rule. | ||
// Borrowed from https://websemantics.uk/tools/responsive-font-calculator/. | ||
$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)"; | ||
|
||
return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)"; | ||
} | ||
|
||
/** | ||
* Returns a font-size value based on a given font-size preset. | ||
* Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values. | ||
* | ||
* @param array $preset fontSizes preset value as seen in theme.json. | ||
* @param boolean $should_use_fluid_typography An override to switch fluid typography "on". Can be used for unit testing. | ||
* @return string Font-size value. | ||
*/ | ||
function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_typography = false ) { | ||
// Check if fluid font sizes are activated. | ||
$typography_settings = gutenberg_get_global_settings( array( 'typography' ) ); | ||
$should_use_fluid_typography = isset( $typography_settings['fluid'] ) && true === $typography_settings['fluid'] ? true : $should_use_fluid_typography; | ||
|
||
if ( ! $should_use_fluid_typography ) { | ||
return $preset['size']; | ||
} | ||
|
||
// Defaults. | ||
$default_maximum_viewport_width = '1600px'; | ||
$default_minimum_viewport_width = '768px'; | ||
$default_minimum_font_size_factor = 0.75; | ||
$default_maximum_font_size_factor = 1.5; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should default to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, what if we set the scale to .5? My concern is that we don't want sizes to grow significantly on common viewport sizes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree, in the absence of any For example, it could represent the max (or min font size) value as you suggest. Or it could be the middle point. There's probably an argument for the suitability of all three where there are no explicit fluid min or max values. We'd have to be careful not to allow font sizes to become too small, especially on mobile, but also not too large. In general, I think it's something we can play with to achieve a balanced spread. I'll land this and we can test further. Thanks a lot! |
||
$default_scale_factor = 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, ignore my comment about default scale factor, you're using it here 😄 |
||
|
||
// Font sizes. | ||
$fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null; | ||
|
||
// Try to grab explicit min and max fluid font sizes. | ||
$minimum_font_size_raw = isset( $fluid_font_size_settings['min'] ) ? $fluid_font_size_settings['min'] : null; | ||
$maximum_font_size_raw = isset( $fluid_font_size_settings['max'] ) ? $fluid_font_size_settings['max'] : null; | ||
|
||
// Font sizes. | ||
$preferred_size = gutenberg_get_typography_value_and_unit( $preset['size'] ); | ||
|
||
// Protect against unsupported units. | ||
if ( empty( $preferred_size['unit'] ) ) { | ||
return $preset['size']; | ||
} | ||
|
||
// If no fluid min or max font sizes are available, create some using min/max font size factors. | ||
if ( ! $minimum_font_size_raw ) { | ||
$minimum_font_size_raw = ( $preferred_size['value'] * $default_minimum_font_size_factor ) . $preferred_size['unit']; | ||
} | ||
|
||
if ( ! $maximum_font_size_raw ) { | ||
$maximum_font_size_raw = ( $preferred_size['value'] * $default_maximum_font_size_factor ) . $preferred_size['unit']; | ||
} | ||
|
||
$fluid_font_size_value = gutenberg_get_computed_fluid_typography_value( | ||
array( | ||
'minimum_viewport_width' => $default_minimum_viewport_width, | ||
'maximum_viewport_width' => $default_maximum_viewport_width, | ||
'minimum_font_size' => $minimum_font_size_raw, | ||
'maximum_font_size' => $maximum_font_size_raw, | ||
'scale_factor' => $default_scale_factor, | ||
) | ||
); | ||
|
||
if ( ! empty( $fluid_font_size_value ) ) { | ||
return $fluid_font_size_value; | ||
} | ||
|
||
return $preset['size']; | ||
} | ||
|
||
// Register the block support. | ||
WP_Block_Supports::get_instance()->register( | ||
'typography', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should
$scale_factor
have a fallback value if not provided?