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

Block Support: Add font style and weight options with combined UI #26444

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 34 additions & 27 deletions lib/block-supports/typography.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,22 @@
* @param WP_Block_Type $block_type Block Type.
*/
function gutenberg_register_typography_support( $block_type ) {
$has_font_size_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false );
}

$has_line_height_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false );
}

$has_text_transform_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false );
if ( ! property_exists( $block_type, 'supports' ) ) {
return;
}

$has_text_decoration_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
}
$has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false );
$has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false );
$has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false );
$has_typography_support = $has_font_appearance_support || $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support;

if ( ! $block_type->attributes ) {
$block_type->attributes = array();
}

if ( ( $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support ) && ! array_key_exists( 'style', $block_type->attributes ) ) {
if ( $has_typography_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
Expand Down Expand Up @@ -65,6 +56,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
$classes = array();
$styles = array();

$has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false );
$has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false );
$has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
Expand Down Expand Up @@ -105,6 +97,21 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
}
}

// Font appearance - style and weight.
if ( $has_font_appearance_support ) {
// Apply font style.
$font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' );
if ( $font_style ) {
$styles[] = $font_style;
}

// Apply font weight.
$font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' );
if ( $font_weight ) {
$styles[] = $font_weight;
}
}

// Line Height.
if ( $has_line_height_support ) {
$has_line_height = isset( $block_attributes['style']['typography']['lineHeight'] );
Expand Down Expand Up @@ -141,15 +148,6 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
return $attributes;
}

// Register the block support.
WP_Block_Supports::get_instance()->register(
'typography',
array(
'register_attribute' => 'gutenberg_register_typography_support',
'apply' => 'gutenberg_apply_typography_support',
)
);

/**
* Generates an inline style for a typography feature e.g. text decoration,
* text transform, and font style.
Expand Down Expand Up @@ -180,3 +178,12 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu
// 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 );
}

// Register the block support.
WP_Block_Supports::get_instance()->register(
'typography',
array(
'register_attribute' => 'gutenberg_register_typography_support',
'apply' => 'gutenberg_apply_typography_support',
)
);
48 changes: 48 additions & 0 deletions lib/experimental-default-theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,54 @@
"size": 42
}
],
"fontStyles": [
Copy link
Member

Choose a reason for hiding this comment

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

Previously @mtias referred to the intention of not having more presets for now, besides the one for the font family. Would you be able to confirm if that's still the case @mtias?
In the fontStyles and fontWeights I'm not sure if presets bring much value. I guess it may make sense that the themes define for a given font family the weights and styles that are available because depending on the font family one font-weight may be loaded or not, but that may be discussed as part of font loading API.

I guess for font-weight and style we can use normal inline styles without presets e.g: "font-weight: 900;".

{
"name": "Regular",
"slug": "normal"
},
{
"name": "Italic",
"slug": "italic"
}
],
"fontWeights": [
{
"name": "Ultralight",
"slug": "100"
},
{
"name": "Thin",
"slug": "200"
},
{
"name": "Light",
"slug": "300"
},
{
"name": "Regular",
"slug": "400"
},
{
"name": "Medium",
"slug": "500"
},
{
"name": "Semibold",
"slug": "600"
},
{
"name": "Bold",
"slug": "700"
Copy link
Member

Choose a reason for hiding this comment

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

In the other presets we use different keys for slug and value. This approach is using the same keys for both. The idea of presets is we can change the value and the places using a given slug will automatically all use the new value. If we use the slug for the value the main objective of the preset is not accomplished as one can not change the value without changing the slug.
The inline style we store ends up containing the value anyway var:preset|font-weight|100. I guess removing presents from the fontWeight and fontStyle and just using values is the way to go.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the other presets we use different keys for slug and value.

I can definitely update the presets to use both slug and value. It just seemed odd to me having both the slug and the value the same for the most part.

The inline style we store ends up containing the value anyway var:preset|font-weight|100. I guess removing presents from the fontWeight and fontStyle and just using values is the way to go.

The benefit I saw in using the presets that could be defined via the theme.json was that it also provided a means to customize the available styles or weights.

},
{
"name": "Heavy",
"slug": "800"
},
{
"name": "Black",
"slug": "900"
}
],
"textTransforms": [
{
"name": "AB",
Expand Down
61 changes: 61 additions & 0 deletions lib/global-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,40 @@ function gutenberg_experimental_global_styles_get_core() {
$font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ];
}
}

$default_font_styles_i18n = array(
'normal' => __( 'Regular', 'gutenberg' ),
'italic' => __( 'Italic', 'gutenberg' ),
'initial' => __( 'Initial', 'gutenberg' ),
'inherit' => __( 'Inherit', 'gutenberg' ),
);

if ( ! empty( $config['global']['settings']['typography']['fontStyles'] ) ) {
foreach ( $config['global']['settings']['typography']['fontStyles'] as &$font_style ) {
$font_style['name'] = $default_font_styles_i18n[ $font_style['slug'] ];
}
}

$default_font_weights_i18n = array(
'100' => __( 'Ultralight', 'gutenberg' ),
'200' => __( 'Thin', 'gutenberg' ),
'300' => __( 'Light', 'gutenberg' ),
'400' => __( 'Regular', 'gutenberg' ),
'500' => __( 'Medium', 'gutenberg' ),
'600' => __( 'Semibold', 'gutenberg' ),
'700' => __( 'Bold', 'gutenberg' ),
'800' => __( 'Heavy', 'gutenberg' ),
'900' => __( 'Black', 'gutenberg' ),
'initial' => __( 'Initial', 'gutenberg' ),
'inherit' => __( 'Inherit', 'gutenberg' ),
);

if ( ! empty( $config['global']['settings']['typography']['fontWeights'] ) ) {
foreach ( $config['global']['settings']['typography']['fontWeights'] as &$font_weight ) {
$font_weight['name'] = $default_font_weights_i18n[ $font_weight['slug'] ];
}
}

// End i18n logic to remove when JSON i18 strings are extracted.
return $config;
}
Expand Down Expand Up @@ -393,6 +427,10 @@ function gutenberg_experimental_global_styles_get_css_property( $style_property
return 'background-color';
case 'fontSize':
return 'font-size';
case 'fontStyle':
return 'font-style';
case 'fontWeight':
return 'font-weight';
case 'lineHeight':
return 'line-height';
case 'fontFamily':
Expand All @@ -419,6 +457,8 @@ function gutenberg_experimental_global_styles_get_style_property() {
'color' => array( 'color', 'text' ),
'fontSize' => array( 'typography', 'fontSize' ),
'fontFamily' => array( 'typography', 'fontFamily' ),
'fontStyle' => array( 'typography', 'fontStyle' ),
'fontWeight' => array( 'typography', 'fontWeight' ),
'lineHeight' => array( 'typography', 'lineHeight' ),
'textDecoration' => array( 'typography', 'textDecoration' ),
'textTransform' => array( 'typography', 'textTransform' ),
Expand All @@ -437,6 +477,8 @@ function gutenberg_experimental_global_styles_get_support_keys() {
'backgroundColor' => array( 'color' ),
'color' => array( 'color' ),
'fontSize' => array( 'fontSize' ),
'fontStyle' => array( '__experimentalFontAppearance' ),
'fontWeight' => array( '__experimentalFontAppearance' ),
'lineHeight' => array( 'lineHeight' ),
'fontFamily' => array( '__experimentalFontFamily' ),
'textDecoration' => array( '__experimentalTextDecoration' ),
Expand Down Expand Up @@ -467,6 +509,14 @@ function gutenberg_experimental_global_styles_get_presets_structure() {
'path' => array( 'typography', 'fontFamilies' ),
'key' => 'fontFamily',
),
'fontStyle' => array(
'path' => array( 'typography', 'fontStyles' ),
'key' => 'slug',
),
'fontWeight' => array(
'path' => array( 'typography', 'fontWeights' ),
'key' => 'slug',
),
'textDecoration' => array(
'path' => array( 'typography', 'textDecorations' ),
'key' => 'value',
Expand Down Expand Up @@ -514,6 +564,7 @@ function gutenberg_experimental_global_styles_get_block_data() {
array(
'supports' => array(
'__experimentalSelector' => ':root',
'__experimentalFontAppearance' => false,
'__experimentalFontFamily' => true,
'fontSize' => true,
'__experimentalTextDecoration' => true,
Expand Down Expand Up @@ -653,6 +704,16 @@ function gutenberg_experimental_global_styles_get_preset_classes( $selector, $se
'key' => 'size',
'property' => 'font-size',
),
'font-style' => array(
'path' => array( 'typography', 'fontStyles' ),
'key' => 'slug',
'property' => 'font-style',
),
'font-weight' => array(
'path' => array( 'typography', 'fontWeights' ),
'key' => 'slug',
'property' => 'font-weight',
),
'text-decoration' => array(
'path' => array( 'typography', 'textDecorations' ),
'key' => 'value',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* WordPress dependencies
*/
import { CustomSelectControl } from '@wordpress/components';
import { useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Control to display unified font style and weight options.
*
* @param {Object} props Component props.
* @param {Object} props.value Currently selected combination of font style and weight.
* @param {Object} props.options Object containing weight and style options.
* @param {Function} props.onChange Handles selection change.
* @return {WPElement} Font appearance control.
*/
export default function FontAppearanceControl( { value, options, onChange } ) {
const { fontStyle, fontWeight } = value;
const { fontStyles = [], fontWeights = [] } = options;
const hasStylesOrWeights = fontStyles.length > 0 || fontWeights.length > 0;

// Map font styles and weights to select options.
const selectOptions = useMemo( () => {
const defaultCombo = { fontStyle: undefined, fontWeight: undefined };
const combinedOptions = [
{
key: 'default',
name: __( 'Default' ),
style: defaultCombo,
presetStyle: defaultCombo,
},
];

fontStyles.forEach( ( { name: styleName, slug: styleSlug } ) => {
fontWeights.forEach( ( { name: weightName, slug: weightSlug } ) => {
combinedOptions.push( {
key: `${ weightSlug }-${ styleSlug }`,
name:
styleSlug === 'normal'
? weightName
: `${ weightName } ${ styleName }`,
Copy link
Member

Choose a reason for hiding this comment

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

I guess it may make sense to use sprintf and our translated function __ for i18n, depending on the locale it may make sense to change the order of weightName and styleName.

// style applies font appearance to the individual select option.
style: { fontStyle: styleSlug, fontWeight: weightSlug },
// presetStyle are the actual typography styles that should be given to onChange.
presetStyle: {
fontStyle: `var:preset|font-style|${ styleSlug }`,
fontWeight: `var:preset|font-weight|${ weightSlug }`,
Copy link
Member

Choose a reason for hiding this comment

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

The other control components with presets that we have:
packages/block-editor/src/components/colors-gradients/control.js
packages/block-editor/src/components/font-family/index.js
packages/block-editor/src/components/font-sizes/font-size-picker.js

Don't handle the different ways a value should be stored, if a preset is used or not. They just receive a value, and a set of possible values (for UI purposes) and allow them to change the value. They are not aware of CSS vars etc..

},
} );
} );
} );

return combinedOptions;
}, [ options ] );

const currentSelection = selectOptions.find(
( option ) =>
option.presetStyle.fontStyle === fontStyle &&
option.presetStyle.fontWeight === fontWeight
);

return (
<fieldset className="components-font-appearance-control">
{ hasStylesOrWeights && (
<CustomSelectControl
className="components-font-appearance-control__select"
label={ __( 'Appearance' ) }
options={ selectOptions }
value={ currentSelection }
onChange={ ( { selectedItem } ) =>
onChange( selectedItem.presetStyle )
}
/>
) }
</fieldset>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.components-font-appearance-control__select {
margin-bottom: 24px;

ul {
li {
color: $gray-900;
text-transform: capitalize;
}
}
}
Loading