Skip to content

Commit

Permalink
Initial commit: add typography to style engine
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonjd committed Apr 6, 2022
1 parent c8ba761 commit ebbb509
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 61 deletions.
126 changes: 71 additions & 55 deletions lib/block-supports/typography.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,117 +92,135 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
$has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false );

if ( $has_font_size_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' ) ) {
// Whether to skip individual block support features.
$should_skip_font_size = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' );
$should_skip_font_family = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' );
$should_skip_font_style = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' );
$should_skip_font_weight = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' );
$should_skip_line_height = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' );
$should_skip_text_decoration = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' );
$should_skip_text_transform = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' );
$should_skip_letter_spacing = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' );

if ( $has_font_size_support && ! $should_skip_font_size ) {
$has_named_font_size = array_key_exists( 'fontSize', $block_attributes );
$has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] );

if ( $has_named_font_size ) {
$classes[] = sprintf( 'has-%s-font-size', _wp_to_kebab_case( $block_attributes['fontSize'] ) );
} elseif ( $has_custom_font_size ) {
$styles[] = sprintf( 'font-size: %s;', $block_attributes['style']['typography']['fontSize'] );
$styles['fontSize'] = $block_attributes['style']['typography']['fontSize'];
}
}

if ( $has_font_family_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' ) ) {
if ( $has_font_family_support && ! $should_skip_font_family ) {
$has_named_font_family = array_key_exists( 'fontFamily', $block_attributes );
$has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] );

if ( $has_named_font_family ) {
$classes[] = sprintf( 'has-%s-font-family', _wp_to_kebab_case( $block_attributes['fontFamily'] ) );
} elseif ( $has_custom_font_family ) {
// Before using classes, the value was serialized as a CSS Custom Property.
// We don't need this code path when it lands in core.
$font_family_custom = $block_attributes['style']['typography']['fontFamily'];
if ( strpos( $font_family_custom, 'var:preset|font-family' ) !== false ) {
$index_to_splice = strrpos( $font_family_custom, '|' ) + 1;
$font_family_slug = _wp_to_kebab_case( substr( $font_family_custom, $index_to_splice ) );
$font_family_custom = sprintf( 'var(--wp--preset--font-family--%s)', $font_family_slug );
}
$styles[] = sprintf( 'font-family: %s;', $font_family_custom );
// Before using classes, the value was serialized as a CSS Custom Property.
// We don't need to check for a preset when it lands in core.
$font_family_value = gutenberg_typography_get_preset_inline_style_value( $font_family_custom, 'font-family' );
$styles['fontFamily'] = $font_family_value;
}
}

if ( $has_font_style_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' ) ) {
$font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' );
if ( $font_style ) {
$styles[] = $font_style;
if ( $has_font_style_support && ! $should_skip_font_style ) {
$font_style = _wp_array_get( $block_attributes, array( 'style', 'typography', 'fontStyle' ), null );
$font_style_value = gutenberg_typography_get_preset_inline_style_value( $font_style, 'font-style' );
if ( $font_style_value ) {
$styles['fontStyle'] = $font_style_value;
}
}

if ( $has_font_weight_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' ) ) {
$font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' );
if ( $font_weight ) {
$styles[] = $font_weight;
if ( $has_font_weight_support && ! $should_skip_font_weight ) {
$font_weight = _wp_array_get( $block_attributes, array( 'style', 'typography', 'fontWeight' ), null );
$font_weight_value = gutenberg_typography_get_preset_inline_style_value( $font_weight, 'font-weight' );
if ( $font_weight_value ) {
$styles['fontWeight'] = $font_weight_value;
}
}

if ( $has_line_height_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' ) ) {
if ( $has_line_height_support && ! $should_skip_line_height ) {
$has_line_height = isset( $block_attributes['style']['typography']['lineHeight'] );
if ( $has_line_height ) {
$styles[] = sprintf( 'line-height: %s;', $block_attributes['style']['typography']['lineHeight'] );
$styles['lineHeight'] = $block_attributes['style']['typography']['lineHeight'];
}
}

if ( $has_text_decoration_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ) ) {
$text_decoration_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'textDecoration', 'text-decoration' );
if ( $text_decoration_style ) {
$styles[] = $text_decoration_style;
if ( $has_text_decoration_support && ! $should_skip_text_decoration ) {
$text_decoration = _wp_array_get( $block_attributes, array( 'style', 'typography', 'textDecoration' ), null );
$text_decoration_value = gutenberg_typography_get_preset_inline_style_value( $text_decoration, 'text-decoration' );
if ( $text_decoration_value ) {
$styles['textDecoration'] = $text_decoration_value;
}
}

if ( $has_text_transform_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ) ) {
$text_transform_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'textTransform', 'text-transform' );
if ( $text_transform_style ) {
$styles[] = $text_transform_style;
if ( $has_text_transform_support && ! $should_skip_text_transform ) {
$text_transform = _wp_array_get( $block_attributes, array( 'style', 'typography', 'textTransform' ), null );
$text_transform_value = gutenberg_typography_get_preset_inline_style_value( $text_transform, 'text-transform' );
if ( $text_transform_value ) {
$styles['textTransform'] = $text_transform_value;
}
}

if ( $has_letter_spacing_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ) ) {
$letter_spacing_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'letterSpacing', 'letter-spacing' );
if ( $letter_spacing_style ) {
$styles[] = $letter_spacing_style;
if ( $has_letter_spacing_support && ! $should_skip_letter_spacing ) {
$letter_spacing = _wp_array_get( $block_attributes, array( 'style', 'typography', 'letterSpacing' ), null );
$letter_spacing_value = gutenberg_typography_get_preset_inline_style_value( $letter_spacing, 'letter-spacing' );
if ( $letter_spacing_value ) {
$styles['letterSpacing'] = $letter_spacing_value;
}
}

if ( ! empty( $classes ) ) {
$attributes['class'] = implode( ' ', $classes );
}
if ( ! empty( $styles ) ) {
$attributes['style'] = implode( ' ', $styles );

$style_engine = WP_Style_Engine_Gutenberg::get_instance();
$inline_styles = $style_engine->generate(
array( 'typography' => $styles ),
array(
'inline' => true,
)
);

if ( ! empty( $inline_styles ) ) {
$attributes['style'] = $inline_styles;
}

return $attributes;
}

/**
* Generates an inline style for a typography feature e.g. text decoration,
* Generates an inline style value for a typography feature e.g. text decoration,
* text transform, and font style.
*
* @param array $attributes Block's attributes.
* @param string $feature Key for the feature within the typography styles.
* @param string $css_property Slug for the CSS property the inline style sets.
* @param string $style_value A raw style value for a single typography feature from a block's style attribute.
* @param string $css_property Slug for the CSS property the inline style sets.
*
* @return string CSS inline style.
* @return string? A CSS inline style value.
*/
function gutenberg_typography_get_css_variable_inline_style( $attributes, $feature, $css_property ) {
// Retrieve current attribute value or skip if not found.
$style_value = _wp_array_get( $attributes, array( 'style', 'typography', $feature ), false );
if ( ! $style_value ) {
return;
}

// If we don't have a preset CSS variable, we'll assume it's a regular CSS value.
if ( strpos( $style_value, "var:preset|{$css_property}|" ) === false ) {
return sprintf( '%s:%s;', $css_property, $style_value );
function gutenberg_typography_get_preset_inline_style_value( $style_value, $css_property ) {
// If the style value is not a preset CSS variable go no further.
if ( empty( $style_value ) || strpos( $style_value, "var:preset|{$css_property}|" ) === false ) {
return $style_value;
}

// For backwards compatibility.
// Presets were removed in https://github.com/WordPress/gutenberg/pull/27555.
// We have a preset CSS variable as the style.
// Get the style value from the string and return CSS style.
$index_to_splice = strrpos( $style_value, '|' ) + 1;
$slug = substr( $style_value, $index_to_splice );
// @TODO
// Font family requires the slugs to be converted to kebab case. Should this be optional in this method?
// Let's test with some older blocks.
$slug = _wp_to_kebab_case( substr( $style_value, $index_to_splice ) );

// Return the actual CSS inline style e.g. `text-decoration:var(--wp--preset--text-decoration--underline);`.
return sprintf( '%s:var(--wp--preset--%s--%s);', $css_property, $css_property, $slug );
// Return the actual CSS inline style value e.g. `var(--wp--preset--text-decoration--underline);`.
return sprintf( 'var(--wp--preset--%s--%s);', $css_property, $slug );
}

// Register the block support.
Expand All @@ -213,5 +231,3 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu
'apply' => 'gutenberg_apply_typography_support',
)
);


55 changes: 50 additions & 5 deletions lib/experimental/style-engine/class-wp-style-engine-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,58 @@ class WP_Style_Engine_Gutenberg {
* For example, `'padding' => 'array( 'top' => '1em' )` will return `array( 'padding-top' => '1em' )`
*/
const BLOCK_STYLE_DEFINITIONS_METADATA = array(
'spacing' => array(
'spacing' => array(
'padding' => array(
'property_key' => 'padding',
'path' => array( 'spacing', 'padding' ),
'value_func' => 'static::get_css_box_rules',
'value_func' => 'static::get_css_rules',
),
'margin' => array(
'property_key' => 'margin',
'path' => array( 'spacing', 'margin' ),
'value_func' => 'static::get_css_box_rules',
'value_func' => 'static::get_css_rules',
),
),
'typography' => array(
'fontSize' => array(
'property_key' => 'font-size',
'path' => array( 'typography', 'fontSize' ),
'value_func' => 'static::get_css_rules',
),
'fontFamily' => array(
'property_key' => 'font-family',
'path' => array( 'typography', 'fontFamily' ),
'value_func' => 'static::get_css_rules',
),
'fontStyle' => array(
'property_key' => 'font-style',
'path' => array( 'typography', 'fontStyle' ),
'value_func' => 'static::get_css_rules',
),
'fontWeight' => array(
'property_key' => 'font-weight',
'path' => array( 'typography', 'fontWeight' ),
'value_func' => 'static::get_css_rules',
),
'lineHeight' => array(
'property_key' => 'line-height',
'path' => array( 'typography', 'lineHeight' ),
'value_func' => 'static::get_css_rules',
),
'textDecoration' => array(
'property_key' => 'text-decoration',
'path' => array( 'typography', 'textDecoration' ),
'value_func' => 'static::get_css_rules',
),
'textTransform' => array(
'property_key' => 'text-transform',
'path' => array( 'typography', 'textTransform' ),
'value_func' => 'static::get_css_rules',
),
'letterSpacing' => array(
'property_key' => 'letter-spacing',
'path' => array( 'typography', 'letterSpacing' ),
'value_func' => 'static::get_css_rules',
),
),
);
Expand Down Expand Up @@ -145,20 +187,23 @@ public function generate( $block_styles, $options = array() ) {
}

/**
* Returns a CSS ruleset for box model styles such as margins, padding, and borders.
* Default style value parser that returns a CSS ruleset.
* If the input contains an array, it will treated like a box model
* for styles such as margins, padding, and borders
*
* @param string|array $style_value A single raw Gutenberg style attributes value for a CSS property.
* @param string $style_property The CSS property for which we're creating a rule.
*
* @return array The class name for the added style.
*/
public static function get_css_box_rules( $style_value, $style_property ) {
public static function get_css_rules( $style_value, $style_property ) {
$rules = array();

if ( ! $style_value ) {
return $rules;
}

// We assume box model-like properties.
if ( is_array( $style_value ) ) {
foreach ( $style_value as $key => $value ) {
$rules[ "$style_property-$key" ] = $value;
Expand Down
21 changes: 20 additions & 1 deletion phpunit/style-engine/class-wp-style-engine-gutenberg-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public function data_block_styles_fixtures() {
'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;',
),

'inline_valid_multiple_style' => array(
'inline_valid_multiple_spacing_style' => array(
'block_styles' => array(
'spacing' => array(
'padding' => array(
Expand All @@ -151,6 +151,25 @@ public function data_block_styles_fixtures() {
),
'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;margin-top:12rem;margin-left:2vh;margin-bottom:2px;margin-right:10em;',
),

'inline_valid_multiple_typography_style' => array(
'block_styles' => array(
'typography' => array(
'fontSize' => 'clamp(2em, 2vw, 4em)',
'fontFamily' => 'Roboto,Oxygen-Sans,Ubuntu,sans-serif',
'fontStyle' => 'italic',
'fontWeight' => '800',
'lineHeight' => '1.3',
'textDecoration' => 'underline',
'textTransform' => 'uppercase',
'letterSpacing' => '2',
),
),
'options' => array(
'inline' => true,
),
'expected_output' => 'font-family:Roboto,Oxygen-Sans,Ubuntu,sans-serif;font-style:italic;font-weight:800;line-height:1.3;text-decoration:underline;text-transform:uppercase;letter-spacing:2;',
),
);
}
}

0 comments on commit ebbb509

Please sign in to comment.