-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix default duotone preset SVG and style generation #38681
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
Changes from all commits
01d593e
51d2bc5
9072348
ccb65ac
93f23af
7b4e11c
795de03
3cda90b
68cdf4e
fe1c37a
6b65616
ece841d
d47041e
bb55054
e6ceb4b
67dbd4b
10e3e26
159afc3
9de2494
1410ae1
6bdf5d0
77b04c7
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 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -69,9 +69,18 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| * | ||||||||||||||||||
| * This contains the necessary metadata to process them: | ||||||||||||||||||
| * | ||||||||||||||||||
| * - path => where to find the preset within the settings section | ||||||||||||||||||
| * - override => whether a theme preset with the same slug as a default preset | ||||||||||||||||||
| * can override it | ||||||||||||||||||
| * - path => Where to find the preset within the settings section. | ||||||||||||||||||
| * - prevent_override => Whether a theme preset with the same slug as a default preset | ||||||||||||||||||
| * should not override it or the path to a setting for the same | ||||||||||||||||||
| * When defaults. | ||||||||||||||||||
| * The relationship between whether to override the defaults | ||||||||||||||||||
| * and whether the defaults are enabled is inverse: | ||||||||||||||||||
| * - If defaults are enabled => theme presets should not be overriden | ||||||||||||||||||
| * - If defaults are disabled => theme presets should be overriden | ||||||||||||||||||
| * For example, a theme sets defaultPalette to false, | ||||||||||||||||||
| * making the default palette hidden from the user. | ||||||||||||||||||
| * In that case, we want all the theme presets to be present, | ||||||||||||||||||
| * so they should override the defaults by setting this false. | ||||||||||||||||||
|
Member
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. are we introducing this concept, not that I guess this is an existing term, but I still find it confusing, especially how we bring that negation into the JS code with Not a big deal, and it's in a rather complicated settings sourcing function so I get is. That's my only real feedback I think I'm able to provide here.
Contributor
Author
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.
It was kind of like that before, but I changed it hoping that it would make things less confusing despite being a negative construction. I'll explain my thought process for replacing Arrays in the {
"settings": {
"color": {
"defaultPalette": false
}
}
}Previously, there was a key called const PRESETS_METADATA = array(
array(
'override' => array( 'color', 'defaultPalette' ), // Path to a value in theme.json settings
),
array(
'override' => true, // A hard-coded value that doesn't have a corresponding theme.json setting
),
);I expected the values to resolve like this when I was reading it. const PRESETS_METADATA = array(
array(
'override' => false // Substitute in the value from theme.json
),
array(
'override' => true // Pass through a boolean
),
);But actually, there was a function, const PRESETS_METADATA = array(
array(
'override' => true // Substitute in the value from theme.json _and_ invert it
),
array(
'override' => true // Booleans don't get inverted
),
)As part of this PR I added const PRESETS_METADATA = array(
array(
'use_default_presets' => array( 'color', 'defaultPalette' ),
),
array(
'use_default_presets' => true,
),
);It needs to resolve to this. const PRESETS_METADATA = array(
array(
'use_default_presets' => false // Substitute in the value from theme.json
),
array(
'use_default_presets' => true // Pass through a boolean
),
);Between the weird behavior of Does that make sense to you? Would you rather have inconsistent path array meanings or a negative construction? Or is there another simple option that I'm overlooking?
Member
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. it's too bad the JSON file has the inversion, rather than something like, probably the confusion stems from having multiple speakers saying the same words but from different perspectives. should in the I guess the idea is that if we have what is the relationship between My confusion peaks in lines like these. It's hard for me to read through the code and explain what impact $skip = ! static::get_metadata_boolean( $this->theme_json['settings'], false, true );
$skip = ! static::get_metadata_boolean( $this->theme_json['settings'], true, true );
$skip = ! static::get_metadata_boolean( $this->theme_json['settings'], [ 'color', $skip = 'defaultPalette' ], true );Are "defaults" and "presets" different?
Contributor
Author
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. Lots of good questions! I'll answer them a bit out of order in a way that they can build upon one another.
A preset is the metadata needed to generate the relevant global styles parts: CSS variables for everything, CSS classes for things like text/background color, and SVGs for things like duotone filters. They come from three different origins listed here in the order that they are processed:
In the theme.json, the presets would be the objects inside the duotone array, for example. {
"settings": {
"color": {
"duotone": [
{
// This object is a preset.
"name": "Black and White",
"slug": "black-and-white",
"colors": [ "#000", "#FFF" ]
}
]
}
}
}
So maybe we could name it The These screenshots from #36811 illustrate it well—
Let's run through an example with a slightly modified theme.json from the #36811 example to see what is being generated from the presets. {
"settings": {
"color": {
"defaultPalette": true,
"palette": [
{
"slug": "black",
"color": "#FF69B4"
},
{
"slug": "dark-gray",
"color": "#2A303C"
},
// ...other theme preset colors...
{
"slug": "white",
"color": "#FF69B4"
}
]
}
}
}Here are the relevant CSS variables that are generated. body {
--wp--preset--color--black: #000000;
--wp--preset--color--cyan-bluish-gray: #abb8c3;
--wp--preset--color--white: #ffffff;
/* ...other default preset colors... */
--wp--preset--color--dark-gray: #2A303C;
/* ...other theme preset colors... */
}Notice that the My expectation as a block theme creator would be that values I pass replace the default ones, but the expected outcome according to #36811 and #37008 is the other way around. With body {
--wp--preset--color--black: #2A303C;
--wp--preset--color--dark-gray: #2A303C;
/* ...other theme preset colors... */
--wp--preset--color--white: #2A303C;
}
In the case of the color/gradient/duotone palettes. I suppose you could say that However, in the case of
They are separate settings. You could add duplicates to the list, and I expect it would generate duplicates in the output. However,
Exactly.
Member
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. that's extremely helpful and clarifying @ajlende, thank you for taking the effort to add that.
Oh no! those names make so much more sense to me 😆. I trust there was a good reason to diverge from them. please don't let this derail the PR or the fixes, it's me doodling on the idea. it's almost like we're looking at a natural hierarchy and talking about what behavior happens when finding duplicate values. the order will always be the kicker is that sometimes we don't want ❓ is there any reason a {
// sets completely new duotone values regardless
// of what was previously set
"color": {
"resetConfig": true,
"duotone": { … }
}
}
{
// retains existing values but if we find a new
// value for the same key, use the new one
"color": {
"duplicateKeys": "replace",
"duotone": { … }
}
}
{
// retains existing values but if we find a new
// value for the same key, use the old one
"color": {
"duplicateKeys": "retain",
"duotone": { … }
}
}I wonder if this description could account not only for on the other hand, it couldn't bring back the default levels with this description since the theme might have already reset them. my other idea was wondering if we are standardized on {
// removes default config values entirely,
// custom config values take priority over theme
"color": {
"configs": [ "theme", "custom" ],
"duplicateKeys": "replace",
"duotone": { … }
}
}while a bit more verbose this would give each level in the hierarchy the control to decide which values should appear and which should take precedence in a conflict. if a theme turned off the default values then a custom config could turn them back on. a custom config to let the defaults stay but wipe out the theme's colors. any thoughts? way too much discussion about something that's already embedded in the system? my noodling is mostly around thinking through what exactly the behaviors are we're doing and avoiding special-casing in the interface, such as when calling out
Contributor
Author
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'm happy to explain the existing system and how it drove my decisions for this bug fix, but, yeah, we're starting to get into big picture changes with some of these. I'm just trying to make sure SVG filters don't get left behind when changes are made to colors/gradients/etc. and, in this case, fixing a bug that is more obvious because of SVG filters. Still, these questions might be interesting for @oandregal and/or @jorgefilipecosta to see since they are more involved in global styles than I am. |
||||||||||||||||||
| * - value_key => the key that represents the value | ||||||||||||||||||
| * - value_func => optionally, instead of value_key, a function to generate | ||||||||||||||||||
| * the value that takes a preset as an argument | ||||||||||||||||||
|
|
@@ -96,7 +105,8 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| const PRESETS_METADATA = array( | ||||||||||||||||||
| array( | ||||||||||||||||||
| 'path' => array( 'color', 'palette' ), | ||||||||||||||||||
| 'override' => array( 'color', 'defaultPalette' ), | ||||||||||||||||||
| 'prevent_override' => array( 'color', 'defaultPalette' ), | ||||||||||||||||||
| 'use_default_presets' => array( 'color', 'defaultPalette' ), | ||||||||||||||||||
| 'use_default_names' => false, | ||||||||||||||||||
| 'value_key' => 'color', | ||||||||||||||||||
| 'css_vars' => '--wp--preset--color--$slug', | ||||||||||||||||||
|
|
@@ -109,7 +119,8 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| ), | ||||||||||||||||||
| array( | ||||||||||||||||||
| 'path' => array( 'color', 'gradients' ), | ||||||||||||||||||
| 'override' => array( 'color', 'defaultGradients' ), | ||||||||||||||||||
| 'prevent_override' => array( 'color', 'defaultGradients' ), | ||||||||||||||||||
| 'use_default_presets' => array( 'color', 'defaultGradients' ), | ||||||||||||||||||
| 'use_default_names' => false, | ||||||||||||||||||
| 'value_key' => 'gradient', | ||||||||||||||||||
| 'css_vars' => '--wp--preset--gradient--$slug', | ||||||||||||||||||
|
|
@@ -118,7 +129,8 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| ), | ||||||||||||||||||
| array( | ||||||||||||||||||
| 'path' => array( 'color', 'duotone' ), | ||||||||||||||||||
| 'override' => true, | ||||||||||||||||||
| 'prevent_override' => array( 'color', 'defaultDuotone' ), | ||||||||||||||||||
| 'use_default_presets' => array( 'color', 'defaultDuotone' ), | ||||||||||||||||||
| 'use_default_names' => false, | ||||||||||||||||||
| 'value_func' => 'gutenberg_get_duotone_filter_property', | ||||||||||||||||||
| 'css_vars' => '--wp--preset--duotone--$slug', | ||||||||||||||||||
|
|
@@ -127,7 +139,8 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| ), | ||||||||||||||||||
| array( | ||||||||||||||||||
| 'path' => array( 'typography', 'fontSizes' ), | ||||||||||||||||||
| 'override' => true, | ||||||||||||||||||
| 'prevent_override' => false, | ||||||||||||||||||
| 'use_default_presets' => true, | ||||||||||||||||||
| 'use_default_names' => true, | ||||||||||||||||||
| 'value_key' => 'size', | ||||||||||||||||||
| 'css_vars' => '--wp--preset--font-size--$slug', | ||||||||||||||||||
|
|
@@ -136,7 +149,8 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| ), | ||||||||||||||||||
| array( | ||||||||||||||||||
| 'path' => array( 'typography', 'fontFamilies' ), | ||||||||||||||||||
| 'override' => true, | ||||||||||||||||||
| 'prevent_override' => false, | ||||||||||||||||||
| 'use_default_presets' => true, | ||||||||||||||||||
| 'use_default_names' => false, | ||||||||||||||||||
| 'value_key' => 'fontFamily', | ||||||||||||||||||
| 'css_vars' => '--wp--preset--font-family--$slug', | ||||||||||||||||||
|
|
@@ -229,6 +243,7 @@ class WP_Theme_JSON_5_9 { | |||||||||||||||||
| 'custom' => null, | ||||||||||||||||||
| 'customDuotone' => null, | ||||||||||||||||||
| 'customGradient' => null, | ||||||||||||||||||
| 'defaultDuotone' => null, | ||||||||||||||||||
ajlende marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
| 'defaultGradients' => null, | ||||||||||||||||||
| 'defaultPalette' => null, | ||||||||||||||||||
| 'duotone' => null, | ||||||||||||||||||
|
|
@@ -1021,9 +1036,14 @@ protected static function scope_selector( $scope, $selector ) { | |||||||||||||||||
| protected static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) { | ||||||||||||||||||
| $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); | ||||||||||||||||||
|
|
||||||||||||||||||
| $skip_default_presets = ! static::get_metadata_boolean( $settings, $preset_metadata['use_default_presets'], true ); | ||||||||||||||||||
|
|
||||||||||||||||||
| $result = array(); | ||||||||||||||||||
| foreach ( $origins as $origin ) { | ||||||||||||||||||
| if ( ! isset( $preset_per_origin[ $origin ] ) ) { | ||||||||||||||||||
| if ( | ||||||||||||||||||
| ! isset( $preset_per_origin[ $origin ] ) || | ||||||||||||||||||
| ( 'default' === $origin && $skip_default_presets ) | ||||||||||||||||||
| ) { | ||||||||||||||||||
| continue; | ||||||||||||||||||
| } | ||||||||||||||||||
| foreach ( $preset_per_origin[ $origin ] as $preset ) { | ||||||||||||||||||
|
|
@@ -1065,9 +1085,14 @@ protected static function get_settings_slugs( $settings, $preset_metadata, $orig | |||||||||||||||||
|
|
||||||||||||||||||
| $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); | ||||||||||||||||||
|
|
||||||||||||||||||
| $skip_default_presets = ! static::get_metadata_boolean( $settings, $preset_metadata['use_default_presets'], true ); | ||||||||||||||||||
|
|
||||||||||||||||||
| $result = array(); | ||||||||||||||||||
| foreach ( $origins as $origin ) { | ||||||||||||||||||
| if ( ! isset( $preset_per_origin[ $origin ] ) ) { | ||||||||||||||||||
| if ( | ||||||||||||||||||
| ! isset( $preset_per_origin[ $origin ] ) || | ||||||||||||||||||
| ( 'default' === $origin && $skip_default_presets ) | ||||||||||||||||||
| ) { | ||||||||||||||||||
| continue; | ||||||||||||||||||
| } | ||||||||||||||||||
| foreach ( $preset_per_origin[ $origin ] as $preset ) { | ||||||||||||||||||
|
|
@@ -1470,7 +1495,7 @@ public function merge( $incoming ) { | |||||||||||||||||
|
|
||||||||||||||||||
| // Replace the presets. | ||||||||||||||||||
| foreach ( static::PRESETS_METADATA as $preset ) { | ||||||||||||||||||
| $override_preset = static::should_override_preset( $this->theme_json, $node['path'], $preset['override'] ); | ||||||||||||||||||
| $override_preset = ! static::get_metadata_boolean( $this->theme_json['settings'], $preset['prevent_override'], true ); | ||||||||||||||||||
|
Member
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. here's an example of the confusion: two changes that may not be ideal but which would remove the confusion from this line would be to: // keep the name so it means the same thing when reading
$prevent_override = static::get_metadata_boolean( … );
…
( 'theme' === $origin && ! $prevent_override )// mirror the permissive language of the setting from the JSON file
// but in the positive sense
$allow_override = ! static::get_metatdata_boolean( … );
…
( 'theme' === $origin && $allow_override ) |
||||||||||||||||||
|
|
||||||||||||||||||
| foreach ( static::VALID_ORIGINS as $origin ) { | ||||||||||||||||||
| $base_path = array_merge( $node['path'], $preset['path'] ); | ||||||||||||||||||
|
|
@@ -1517,6 +1542,7 @@ public function get_svg_filters( $origins ) { | |||||||||||||||||
| $blocks_metadata = static::get_blocks_metadata(); | ||||||||||||||||||
| $setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata ); | ||||||||||||||||||
|
|
||||||||||||||||||
| $filters = ''; | ||||||||||||||||||
| foreach ( $setting_nodes as $metadata ) { | ||||||||||||||||||
| $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); | ||||||||||||||||||
| if ( empty( $node['color']['duotone'] ) ) { | ||||||||||||||||||
|
|
@@ -1525,9 +1551,11 @@ public function get_svg_filters( $origins ) { | |||||||||||||||||
|
|
||||||||||||||||||
| $duotone_presets = $node['color']['duotone']; | ||||||||||||||||||
|
|
||||||||||||||||||
| $filters = ''; | ||||||||||||||||||
| foreach ( $origins as $origin ) { | ||||||||||||||||||
| if ( ! isset( $duotone_presets[ $origin ] ) ) { | ||||||||||||||||||
| if ( | ||||||||||||||||||
| ! isset( $duotone_presets[ $origin ] ) || | ||||||||||||||||||
| ( 'default' === $origin && false === $node['color']['defaultDuotone'] ) | ||||||||||||||||||
| ) { | ||||||||||||||||||
| continue; | ||||||||||||||||||
| } | ||||||||||||||||||
| foreach ( $duotone_presets[ $origin ] as $duotone_preset ) { | ||||||||||||||||||
|
|
@@ -1540,42 +1568,40 @@ public function get_svg_filters( $origins ) { | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Returns whether a presets should be overriden or not. | ||||||||||||||||||
| * For metadata values that can either be booleans or paths to booleans, gets the value. | ||||||||||||||||||
| * | ||||||||||||||||||
| * ```php | ||||||||||||||||||
| * $data = array( | ||||||||||||||||||
| * 'color' => array( | ||||||||||||||||||
| * 'defaultPalette' => true | ||||||||||||||||||
| * ) | ||||||||||||||||||
| * ); | ||||||||||||||||||
| * | ||||||||||||||||||
| * static::get_metadata_boolean( $data, false ); | ||||||||||||||||||
| * // => false | ||||||||||||||||||
| * | ||||||||||||||||||
| * static::get_metadata_boolean( $data, array( 'color', 'defaultPalette' ) ); | ||||||||||||||||||
| * // => true | ||||||||||||||||||
| * ``` | ||||||||||||||||||
| * | ||||||||||||||||||
| * @param array $theme_json The theme.json like structure to inspect. | ||||||||||||||||||
| * @param array $path Path to inspect. | ||||||||||||||||||
| * @param bool|array $override Data to compute whether to override the preset. | ||||||||||||||||||
| * @param array $data The data to inspect. | ||||||||||||||||||
| * @param bool|array $path Boolean or path to a boolean. | ||||||||||||||||||
| * @param bool $default Default value if the referenced path is missing. | ||||||||||||||||||
| * @return boolean | ||||||||||||||||||
| */ | ||||||||||||||||||
| protected static function should_override_preset( $theme_json, $path, $override ) { | ||||||||||||||||||
| if ( is_bool( $override ) ) { | ||||||||||||||||||
| return $override; | ||||||||||||||||||
| protected static function get_metadata_boolean( $data, $path, $default = false ) { | ||||||||||||||||||
| if ( is_bool( $path ) ) { | ||||||||||||||||||
| return $path; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // The relationship between whether to override the defaults | ||||||||||||||||||
| // and whether the defaults are enabled is inverse: | ||||||||||||||||||
| // | ||||||||||||||||||
| // - If defaults are enabled => theme presets should not be overriden | ||||||||||||||||||
| // - If defaults are disabled => theme presets should be overriden | ||||||||||||||||||
| // | ||||||||||||||||||
| // For example, a theme sets defaultPalette to false, | ||||||||||||||||||
| // making the default palette hidden from the user. | ||||||||||||||||||
| // In that case, we want all the theme presets to be present, | ||||||||||||||||||
| // so they should override the defaults. | ||||||||||||||||||
| if ( is_array( $override ) ) { | ||||||||||||||||||
| $value = _wp_array_get( $theme_json, array_merge( $path, $override ) ); | ||||||||||||||||||
| if ( is_array( $path ) ) { | ||||||||||||||||||
| $value = _wp_array_get( $data, $path ); | ||||||||||||||||||
| if ( isset( $value ) ) { | ||||||||||||||||||
| return ! $value; | ||||||||||||||||||
| return $value; | ||||||||||||||||||
|
Member
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. this does seem like a very nice improvement over |
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Search the top-level key if none was found for this node. | ||||||||||||||||||
| $value = _wp_array_get( $theme_json, array_merge( array( 'settings' ), $override ) ); | ||||||||||||||||||
| if ( isset( $value ) ) { | ||||||||||||||||||
| return ! $value; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return true; | ||||||||||||||||||
| } | ||||||||||||||||||
| return $default; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
|
|
||||||||||||||||||


Uh oh!
There was an error while loading. Please reload this page.