Skip to content

Commit

Permalink
Global styles: add background to top-level theme.json styles (#59354)
Browse files Browse the repository at this point in the history
* First commit. Define theme.json schema

Updating constants and docs

Fix props

* Adding background to global styles output in the site editor

* Testing generic block support function to calculate block support for background and global styles

* Pass the entire background object

* testing out theme source

* Comments and some checks

* Added image fixture for theme source
Added tests
Supporting string and object for backgroundImage

* Use the JS style engine to generate styles

* Default background size is always 'cover' - this matches current behaviour
Now assuming that a string value will have it's own `url()`

* The linter of doom

* Whoops, reverting bad setting

* Reverting source:theme support

* Update theme.json schema

* Tests

* LINTY!

* We don't need assets in block theme since the "theme" source functionality was reverted

* Something about yoda conditions

Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
  • Loading branch information
3 people committed Feb 29, 2024
1 parent 1c4418b commit 9957b85
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 33 deletions.
13 changes: 13 additions & 0 deletions docs/reference-guides/theme-json-reference/theme-json-living.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,19 @@ Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested-
## Styles


### background

Background styles

| Property | Type | Props |
| --- | --- |--- |
| backgroundImage | string, object | |
| backgroundPosition | string, object | |
| backgroundRepeat | string, object | |
| backgroundSize | string, object | |

---

### border

Border styles.
Expand Down
53 changes: 25 additions & 28 deletions lib/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ function gutenberg_register_background_support( $block_type ) {
}
}

/**
* Given a theme.json or block background styles, returns the background styles for a block.
*
* @since 6.6.0
*
* @param array $background_styles Background style properties.
* @return array Style engine array of CSS string and style declarations.
*/
function gutenberg_get_background_support_styles( $background_styles = array() ) {
$background_image_source = isset( $background_styles['backgroundImage']['source'] ) ? $background_styles['backgroundImage']['source'] : null;
$background_styles['backgroundSize'] = ! empty( $background_styles['backgroundSize'] ) ? $background_styles['backgroundSize'] : 'cover';

if ( 'file' === $background_image_source && ! empty( $background_styles['backgroundImage']['url'] ) ) {
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_styles['backgroundSize'] && ! isset( $background_styles['backgroundPosition'] ) ) {
$background_styles['backgroundPosition'] = 'center';
}
}

return gutenberg_style_engine_get_styles( array( 'background' => $background_styles ) );
}

/**
* Renders the background styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
Expand All @@ -46,38 +68,13 @@ function gutenberg_render_background_support( $block_content, $block ) {

if (
! $has_background_image_support ||
wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' )
wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' ) ||
! isset( $block_attributes['style']['background'] )
) {
return $block_content;
}

$background_image_source = $block_attributes['style']['background']['backgroundImage']['source'] ?? null;
$background_image_url = $block_attributes['style']['background']['backgroundImage']['url'] ?? null;
$background_size = $block_attributes['style']['background']['backgroundSize'] ?? 'cover';
$background_position = $block_attributes['style']['background']['backgroundPosition'] ?? null;
$background_repeat = $block_attributes['style']['background']['backgroundRepeat'] ?? null;

$background_block_styles = array();

if (
'file' === $background_image_source &&
$background_image_url
) {
// Set file based background URL.
// TODO: In a follow-up, similar logic could be added to inject a featured image url.
$background_block_styles['backgroundImage']['url'] = $background_image_url;
// Only output the background size and repeat when an image url is set.
$background_block_styles['backgroundSize'] = $background_size;
$background_block_styles['backgroundRepeat'] = $background_repeat;
$background_block_styles['backgroundPosition'] = $background_position;

// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_size && ! isset( $background_position ) ) {
$background_block_styles['backgroundPosition'] = 'center';
}
}

$styles = gutenberg_style_engine_get_styles( array( 'background' => $background_block_styles ) );
$styles = gutenberg_get_background_support_styles( $block_attributes['style']['background'] );

if ( ! empty( $styles['css'] ) ) {
// Inject background styles to the first element, presuming it's the wrapper, if it exists.
Expand Down
18 changes: 18 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,18 @@ class WP_Theme_JSON_Gutenberg {
* removed the `--wp--style--block-gap` property.
* @since 6.2.0 Added `outline-*`, and `min-height` properties.
* @since 6.3.0 Added `writing-mode` property.
* @since 6.6.0 Added `background-[image|position|repeat|size]` properties.
*
* @var array
*/
const PROPERTIES_METADATA = array(
'aspect-ratio' => array( 'dimensions', 'aspectRatio' ),
'background' => array( 'color', 'gradient' ),
'background-color' => array( 'color', 'background' ),
'background-image' => array( 'background', 'backgroundImage' ),
'background-position' => array( 'background', 'backgroundPosition' ),
'background-repeat' => array( 'background', 'backgroundRepeat' ),
'background-size' => array( 'background', 'backgroundSize' ),
'border-radius' => array( 'border', 'radius' ),
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
Expand Down Expand Up @@ -461,10 +466,17 @@ class WP_Theme_JSON_Gutenberg {
* added new property `shadow`,
* updated `blockGap` to be allowed at any level.
* @since 6.2.0 Added `outline`, and `minHeight` properties.
* @since 6.6.0 Added `background` sub properties to top-level only.
*
* @var array
*/
const VALID_STYLES = array(
'background' => array(
'backgroundImage' => 'top',
'backgroundPosition' => 'top',
'backgroundRepeat' => 'top',
'backgroundSize' => 'top',
),
'border' => array(
'color' => null,
'radius' => null,
Expand Down Expand Up @@ -2119,6 +2131,12 @@ protected static function compute_style_properties( $styles, $settings = array()
}
}

// Processes background styles.
if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) {
$background_styles = gutenberg_get_background_support_styles( $styles['background'] );
$value = $background_styles['declarations'][ $css_property ] ?? $value;
}

// Skip if empty and not "0" or value represents array of longhand values.
$has_missing_value = empty( $value ) && ! is_numeric( $value );
if ( $has_missing_value || is_array( $value ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ const STYLE_KEYS = [
'filter',
'outline',
'shadow',
'background',
];

function pickStyleKeys( treeToPickFrom ) {
Expand Down
5 changes: 5 additions & 0 deletions packages/blocks/src/api/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
requiresOptOut: true,
useEngine: true,
},
backgroundImage: {
value: [ 'background', 'backgroundImage' ],
support: [ 'background', 'backgroundImage' ],
useEngine: true,
},
backgroundRepeat: {
value: [ 'background', 'backgroundRepeat' ],
support: [ 'background', 'backgroundRepeat' ],
Expand Down
18 changes: 17 additions & 1 deletion packages/style-engine/src/styles/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,23 @@ const backgroundImage = {
return styleRules;
}

if ( _backgroundImage?.source === 'file' && _backgroundImage?.url ) {
/*
* If the background image is a string, it could already contain a url() function,
* or have a linear-gradient value.
*/
if ( typeof _backgroundImage === 'string' ) {
styleRules.push( {
selector: options.selector,
key: 'backgroundImage',
value: _backgroundImage,
} );
}

if (
typeof _backgroundImage === 'object' &&
_backgroundImage?.source === 'file' &&
_backgroundImage?.url
) {
styleRules.push( {
selector: options.selector,
key: 'backgroundImage',
Expand Down
27 changes: 27 additions & 0 deletions packages/style-engine/src/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,33 @@ describe( 'getCSSRules', () => {
] );
} );

it( 'should output background image value when that value is a string', () => {
expect(
getCSSRules(
{
background: {
backgroundImage:
"linear-gradient(to bottom,rgb(255 255 0 / 50%),rgb(0 0 255 / 50%), url('https://example.com/image.jpg')",
},
},
{
selector: '.some-selector',
}
)
).toEqual( [
{
selector: '.some-selector',
key: 'backgroundImage',
value: "linear-gradient(to bottom,rgb(255 255 0 / 50%),rgb(0 0 255 / 50%), url('https://example.com/image.jpg')",
},
{
selector: '.some-selector',
key: 'backgroundSize',
value: 'cover',
},
] );
} );

it( 'should output fallback center position for contain background size', () => {
expect(
getCSSRules(
Expand Down
7 changes: 3 additions & 4 deletions packages/style-engine/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ export interface BorderIndividualStyles< T extends BoxEdge > {

export interface Style {
background?: {
backgroundImage: {
url?: CSSProperties[ 'backgroundImage' ];
source?: string;
};
backgroundImage?:
| { url?: CSSProperties[ 'backgroundImage' ]; source?: string }
| CSSProperties[ 'backgroundImage' ];
backgroundPosition?: CSSProperties[ 'backgroundPosition' ];
backgroundRepeat?: CSSProperties[ 'backgroundRepeat' ];
backgroundSize?: CSSProperties[ 'backgroundSize' ];
Expand Down
79 changes: 79 additions & 0 deletions phpunit/block-supports/background-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ public function data_background_block_support() {
'expected_wrapper' => '<div class="has-background" style="background-image:url(&#039;https://example.com/image.jpg&#039;);background-size:cover;">Content</div>',
'wrapper' => '<div>Content</div>',
),
'background image style is applied when backgroundImage is a string' => array(
'theme_name' => 'block-theme-child-with-fluid-typography',
'block_name' => 'test/background-rules-are-output',
'background_settings' => array(
'backgroundImage' => true,
),
'background_style' => array(
'backgroundImage' => "url('https://example.com/image.jpg')",
),
'expected_wrapper' => '<div class="has-background" style="background-image:url(&#039;https://example.com/image.jpg&#039;);background-size:cover;">Content</div>',
'wrapper' => '<div>Content</div>',
),
'background image style with contain, position, and repeat is applied' => array(
'theme_name' => 'block-theme-child-with-fluid-typography',
'block_name' => 'test/background-rules-are-output',
Expand Down Expand Up @@ -201,4 +213,71 @@ public function data_background_block_support() {
),
);
}

/**
* Tests generating background styles.
*
* @covers ::gutenberg_get_background_support_styles
*
* @dataProvider data_get_background_support_styles
*
* @param mixed $background_style The background styles within the block attributes.
* @param string $expected_css Expected markup for the block wrapper.
*/
public function test_get_background_support_styles( $background_style, $expected_css ) {
$actual = gutenberg_get_background_support_styles( $background_style )['css'];

$this->assertEquals(
$expected_css,
$actual,
'Background CSS should be correct.'
);
}
public function data_get_background_support_styles() {
return array(
'css generated with file source' => array(
'background_style' => array(
'backgroundImage' => array(
'url' => 'https://example.com/image.jpg',
'source' => 'file',
),
),
'expected_css' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
),
'css generated where backgroundImage is a string' => array(
'background_style' => array(
'backgroundImage' => "url('https://example.com/image.jpg')",
),
'expected_css' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
),
'css generated with escaped URL' => array(
'background_style' => array(
'backgroundImage' => array(
'url' => 'https://example.com/image.jpg?q=pom-poms+%3Cscript%3Eevil_script()%3C/script%3E',
),
'backgroundSize' => 'cover',
),
'expected_css' => 'background-size:cover;',
),
'css generated with expected properties' => array(
'background_style' => array(
'backgroundImage' => "url('https://example.com/image.jpg')",
'backgroundSize' => '6px, auto, contain',
'backgroundPosition' => 'bottom 10px right 20px',
'backgroundRepeat' => 'repeat space',
),
'expected_css' => "background-image:url('https://example.com/image.jpg');background-position:bottom 10px right 20px;background-repeat:repeat space;background-size:6px, auto, contain;",
),
'css generated for file source with contain size should add center position' => array(
'background_style' => array(
'backgroundImage' => array(
'url' => 'https://example.com/image.jpg',
'source' => 'file',
),
'backgroundSize' => 'contain',
),
'expected_css' => "background-image:url('https://example.com/image.jpg');background-position:center;background-size:contain;",
),
);
}
}
46 changes: 46 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -4745,6 +4745,52 @@ public function test_get_shadow_styles_for_blocks() {
$this->assertSame( $expected_styles, $theme_json->get_stylesheet() );
}

public function test_get_top_level_background_image_styles() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'styles' => array(
'background' => array(
'backgroundImage' => array(
'url' => 'http://example.org/image.png',
),
'backgroundSize' => 'cover',
'backgroundRepeat' => 'no-repeat',
'backgroundPosition' => 'center center',
),
'blocks' => array(
'core/paragraph' => array(
'background' => array(
'backgroundImage' => array(
'url' => 'http://example.org/image.png',
'source' => 'file',
),
'backgroundSize' => 'cover',
'backgroundRepeat' => 'no-repeat',
'backgroundPosition' => 'center center',
),
),
),
'elements' => array(
'button' => array(
'background' => array(
'backgroundImage' => array(
'url' => 'http://example.org/image.png',
),
'backgroundSize' => 'cover',
'backgroundRepeat' => 'no-repeat',
'backgroundPosition' => 'center center',
),
),
),
),
)
);

$expected_styles = "body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{background-image: url('http://example.org/image.png');background-position: center center;background-repeat: no-repeat;background-size: cover;}";
$this->assertSame( $expected_styles, $theme_json->get_stylesheet() );
}

public function test_get_custom_css_handles_global_custom_css() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
Expand Down
Loading

0 comments on commit 9957b85

Please sign in to comment.