-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Predefined set of font sizes #5269
Conversation
blocks/library/paragraph/index.js
Outdated
@@ -81,6 +84,39 @@ class ParagraphBlock extends Component { | |||
this.nodeRef = node; | |||
} | |||
|
|||
setFontSize( value ) { | |||
const { setAttributes } = this.props; | |||
if ( fontSizeValues.includes( value ) ) { |
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.
Array#includes
is not polyfilled. This will error in IE11.
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.
You might try findKey
and an object of font size values to simplify this:
const FONT_SIZES = {
small: 14,
// ...
};
// ...
setFontSize( fontSize ) {
const size = findKey( FONT_SIZES, fontSize );
let textClass;
if ( size ) {
textClass = `is-${ size }-text`;
}
this.props.setAttributes( { fontSize, textClass } );
}
blocks/library/paragraph/index.js
Outdated
const { setAttributes } = this.props; | ||
if ( fontSizeValues.includes( value ) ) { | ||
if ( fontSizeValues[ 0 ] === value ) { | ||
setAttributes( { fontSize: fontSizeValues[ 0 ], textClass: 'is-small-text' } ); |
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.
In fact, there's redundancy between fontSize
and textClass
. I think we only need one or the other, probably the modifier "small", "regular", "large". Everything else (the class name, the pixel value) can be derived from the constant value.
I like the arbitrary font size slider, it allows for almost illustration-like layouts and designs, especially combined with colors. Although the slider can feel like a heavy-handed interface for adjusting things, in this particular case, I find it an enjoyable UI, because it can quickly cross a very wide range of sizes, and with the live preview on the left this can make for a pretty magical experience. It feels like the challenge in this case is the confusion around inline styles and classes, and making it obvious when either is used. This is a difficult UI. While one solution could be to remove the slider in favor of an input field, this doesn't solve the particular issue of clarifying to the user when a value is set and customized vs. when the value is intrinsic or default. The closest pattern to this, is images. Images work fine when their width and heights are unset — sometimes that's what you want. But other times you want to manually configure those dimensions. Matías you even mentioned the pill-button shortcut UI for the ticket to address that: #4914 (comment). To see how to improve the UI, I decided to try and tackle both font size, and image dimensions, to see if the pattern could be replicated. As part of that I looked at some improvements to the slider design itself, also embracing the pill button shortcuts. Notes:
This "shortcut" pattern, I tried replicating for images as well, where the intrinsic values for an image (when not sized) are shown as placeholder text, and a press of the shortcut button to 100% sets those values explicitly. Thoughts? CC: @mtias Sidenotes: I noticed that GoDaddy's site builder has a slider for font sizes also, though it's for the whole document: If we feel the slider is being too widely used, we can eliminate it in places where the precision control isn't quite as valuable. Perhaps it's not the ideal interface for the gallery columns interface. Maybe that could be just a stepper, perhaps with a little CSS to make the up/down buttons a little bigger and friendlier. |
What about placing the sizes at the top of the slider? I see the relationship as "basic -> advanced". Same as with colors. |
4fdc5d7
to
3131add
Compare
blocks/library/paragraph/index.js
Outdated
@@ -118,20 +145,58 @@ class ParagraphBlock extends Component { | |||
isSelected && ( | |||
<InspectorControls key="inspector"> | |||
<PanelBody title={ __( 'Text Settings' ) }> | |||
<div className="blocks-font-size__main"> |
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.
This whole thing should become a reusable block-component.
text-transform: uppercase; | ||
font-style: normal; | ||
p { | ||
&.is-small-text { |
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 this classes (is-small-text, is-regular-text) etc... be outside of paragraph so other blocks can make use of our font size mechanism?
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.
maybe, but I'm cautious about defining styles generically for *.is-small-text
. Also, in some other blocks reusing this component, they might want to have more fine-grained targets.
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.
So this is a problem because it is applying outside the paragraph block. There's no scoping on the p
selector to make this specific to the paragraph block.
@@ -46,6 +49,13 @@ const ContrastCheckerWithFallbackStyles = withFallbackStyles( ( node, ownProps ) | |||
}; | |||
} )( ContrastChecker ); | |||
|
|||
const FONT_SIZES = { |
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 we try to read this values from the theme, e.g: provide and extensibility mechanism for themes to change them, or use invisible elements plus getComputedStyles to check if themes css changes our styles?
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.
definitely allow a theme to set this, but should be done separately
blocks/library/paragraph/index.js
Outdated
max={ 100 } | ||
beforeIcon="editor-textcolor" | ||
/> | ||
<span>{ attributes.textClass }</span> |
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.
this is just for debugging
blocks/library/paragraph/index.js
Outdated
@@ -242,6 +307,9 @@ const schema = { | |||
fontSize: { | |||
type: 'number', | |||
}, | |||
textClass: { |
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.
We have two attributes for font size (fontSize and textClass) both holding information about the size of the text. Plus having a class as attribute seems redundant because it is already in the p tag and it seems it should have been parsed from there.
What about using just one attribute with for the size and when it is a number it is interpreted that a specific font size should be used, when is something like %regular, we assume that a class should be used?
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.
and it seems it should have been parsed from there.
I agree.
What about using just one attribute with for the size and when it is a number it is interpreted that a specific font size should be used, when is something like %regular, we assume that a class should be used?
Can you expand a bit on how you see this implemented?
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.
Not certain if it is the test approach or not, but we can have just the fontSize attribute as a string. On save/edit if it starts with '%' like '%regular' we assume it's a "named font size" / class and use it to generate the corresponding class e.g: "is-regular-text". If the fontSize is a number we do the same as we do now.
I used a version of this logic in #5273.
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.
Why do we need the obscure prefix?
isFinite( '26' )
// true
isFinite( 'regular' )
// false
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.
Yes, we don't need any prefix in this case, in the colors one we needed and I did not made the mental switch.
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.
Even in the color one, can't you identify a color value? What is it, a hex?
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.
In the colors, a color can be 'red', '#ff0', or 'rgb( 255, 0, 0)' and we may want a named color called red but with a value equal to #d82e00 so a special prefix for our named colors seems like the best approach e.g: %red.
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.
This is working EXCEPTIONALLY well. I have only ONE tiny niggle, which is best illustrated in this GIF: At the end when I press Reset, the slider handle is centered in the slider. Which, in context of just having resized the text, makes it seem like suddenly the text should be large (because of the slider handle position). I realize that the handle at the center is the "unset" configuration for the element: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range — but is there something we can do to indicate that the slider is unset? |
We could try hiding the disc. Once you click on the bar, it would set it again. |
Or making it the same gray as the track maybe. Or maybe it's a non issue... You'll learn what reset means by the context anyway. |
text-transform: uppercase; | ||
font-style: normal; | ||
p { | ||
&.is-small-text { |
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.
So this is a problem because it is applying outside the paragraph block. There's no scoping on the p
selector to make this specific to the paragraph block.
<PanelBody title={ __( 'Text Settings' ) }> | ||
<PanelBody title={ __( 'Text Settings' ) } className="blocks-font-size"> | ||
<div className="blocks-font-size__main"> | ||
<ButtonGroup aria-label={ __( 'Font Size' ) }> |
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.
Won't we want aria-label
on each of the button options instead for "Small", "Medium", etc. ?
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.
We discussed this and opted for not having one for now.
blocks/library/paragraph/index.js
Outdated
@@ -242,6 +307,9 @@ const schema = { | |||
fontSize: { | |||
type: 'number', | |||
}, | |||
textClass: { |
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.
Why do we need the obscure prefix?
isFinite( '26' )
// true
isFinite( 'regular' )
// false
const classes = classnames( 'components-button-group', className ); | ||
|
||
return ( | ||
<div { ...props } className={ classes } role="group" /> |
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.
A group should be used to form a logical collection of items with related functionality, such as children in a tree widget forming a collection of siblings in a hierarchy, or a collection of items having the same container in a directory. However, when a group is used in the context of list, authors must limit its children to listitem elements. Group elements may be nested.
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_group_role
Maybe we need to Children.map
to apply a wrapper to each child?
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.
Discussed in slack channel and markup seemed good as is.
blocks/library/paragraph/index.js
Outdated
aria-pressed={ attributes.textClass === 'is-small-text' } | ||
onClick={ () => this.setFontSize( FONT_SIZES.small ) } | ||
> | ||
S |
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.
How well do these single-characters localize?
blocks/library/paragraph/index.js
Outdated
<PanelBody title={ __( 'Text Settings' ) } className="blocks-font-size"> | ||
<div className="blocks-font-size__main"> | ||
<ButtonGroup aria-label={ __( 'Font Size' ) }> | ||
<Button |
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.
map( {
S: 'small',
M: 'regular',
L: 'large',
XL: 'larger',
}, ( size, label ) => (
<Button
key={ size }
isLarge
isPrimary={ attributes.textClass === `is-${ size }-text` }
aria-pressed={ attributes.textClass === `is-${ size }-text` }
onClick={ () => this.setFontSize( FONT_SIZES[ size ] ) }
>
{ label }
</Button>
) )
@@ -117,21 +144,63 @@ class ParagraphBlock extends Component { | |||
), | |||
isSelected && ( | |||
<InspectorControls key="inspector"> | |||
<PanelBody title={ __( 'Text Settings' ) }> | |||
<PanelBody title={ __( 'Text Settings' ) } className="blocks-font-size"> |
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.
I expect we're planning to extract this to a separate component?
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.
Indeed.
When font size control is used and the value matches one of the predefined values, the editor would output a special class instead of an inline fontSize number.
ce8d4a9
to
3959a97
Compare
@@ -1,3 +1,13 @@ | |||
.editor-block-list__block:not( .is-multi-selected ) .wp-block-paragraph { | |||
background: white; | |||
} | |||
|
|||
.blocks-font-size__main { |
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.
This will be extracted into a block component.
blocks/library/paragraph/index.js
Outdated
fontSize, | ||
} = attributes; | ||
|
||
const thresholdFontSize = findKey( FONT_SIZES, ( size ) => size === fontSize ); |
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.
One thing here is that we'd want this to be abstracted away from blocks when we make FontSize a component.
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.
Maybe this could be a filterLike function props, fontSize => updateProps
const props = addFontSizeProps( { style, className }, fontSize )
blocks/library/paragraph/index.js
Outdated
const className = classnames( { | ||
[ `align${ width }` ]: width, | ||
'has-background': backgroundColor, | ||
'has-drop-cap': dropCap, | ||
} ); | ||
}, thresholdFontSize ? `is-${ thresholdFontSize }-text` : undefined ); |
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.
Why not assign into the prior object similar to as align
is assigned?
classnames( {
// ...
[ `is-${ thresholdFontSize }-text` ]: thresholdFontSize
} );
} | ||
|
||
&.has-drop-cap { | ||
&:first-letter { |
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.
Nesting necessary? vs.
&.has-drop-cap:first-letter {
}
blocks/library/paragraph/index.js
Outdated
const styles = { | ||
backgroundColor: backgroundColor, | ||
color: textColor, | ||
fontSize: fontSize, | ||
fontSize: thresholdFontSize ? null : fontSize, |
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.
Normally in this situation, I would use thresholdFontSize ? undefined : fontSize,. But in this case, I think using null is safe as false values will be ignored.
blocks/library/paragraph/index.js
Outdated
fontSize, | ||
} = attributes; | ||
|
||
const thresholdFontSize = findKey( FONT_SIZES, ( size ) => size === fontSize ); |
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.
The attribute contains the font size as a number. I think this raises some problems. If I'm in a theme that sets large font size as 36 and I switch to a theme that sets large fontSize as 37 the blocks with large fontSize created on the old theme will become invalid.
I think for threshold font sizes we need to store the attribute as a string and not refer to the current number.
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.
Yes, this is why I had a separate attribute for the class. The number should only be used when it's a custom value done by the user (and internally to render the inline style). But we should not save anything about the number if it's coming from the predefined size.
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.
I was not aware of the need to make these thresholds customizable. In that case, I can update the PR to restore the size attribute
blocks/library/paragraph/index.js
Outdated
if ( fontSizeValue ) { | ||
return fontSizeValue; | ||
} | ||
const fontSizeNumeric = parseInt( fontSize ); //compatibility with old font size attribute. |
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.
Not certain if this back compatibility logic should exist. But without it, although the existing blocks are not changed, in the front-end or markup. The editor does not show existing blocks with custom sizes with the correct size (but does not changes them so they continue to be ok in the frontEnd).
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.
I wonder if this could be achieved with the "deprecated" block logic using the migrate
function https://wordpress.org/gutenberg/handbook/block-api/deprecated-blocks/#changing-the-attributes-set
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.
We can keep it for a few releases.
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.
Hi I think we can add a migration function as @youknowriad suggested to convert fontSize to customFontSize if the font size is a number, I will give it a try.
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.
I'm fine with not doing this conversion and just dropping the logic.
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.
Sorry @mtias I was in the middle of adding the conversion logic and did not check your message.
The conversion logic seems to be working fine now and correctly converts exist fontSize to customFontSize, using our deprecation mechanism, as the type is different before it was number now it is string I think having this logic is fine and does not cause problems with the new paragraph blocks,.
I did some tests and it looks like things are working well.
After taking a look more deeply to the latest changes, I noticed an issue: An old paragraph with a custom font size is still valid even if the markup is different. So I think we have two options:
|
e564db3
to
d524a2b
Compare
Hi @youknowriad, this PR now uses the deprecation mechanism to convert existing blocks. The problem you described should be fixed. |
I'll defer to @mtias here. While this fixes the problem, it seems that the default block's implementation is getting bigger and bigger just to handle legacy issues. While that's totally fine once Gutenberg merged into Core, it's good to try to limit it while not done yet. Maybe it's fine to keep it for a release or two and then remove it later. |
Totally agree here, my expectation is that it would follow our normal deprecation rules. And stay there two releases, as soon as an existing post is open this code coverts it. So most users will not be affected by this change. |
Let's keep it then but make sure to remove in a couple releases. |
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.
LGTM 👍
Allow font-size control to have a predefined set of sizes (small, regular, large, larger) which applies classes instead of inline font-size style values. If the value does not correspond to a value in the supplied set, it falls back to previous mechanism.
Themes will be able to define their own set to overwrite the default.
The design aim would be to add a new component (segmented control) which we could use for the image size set as well.
Implementation
ButtonGroup
component.Current State