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

Global Styles: Try per block custom CSS #46571

Merged
merged 18 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
33 changes: 33 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,27 @@ public function get_settings() {
}
}

/**
* Processes the CSS, to apply nesting.
*
* @param string $css The CSS to process.
* @param string $selector The selector to nest.
*
* @return string The processed CSS.
*/
private function process_nested_css( $css, $selector ) {
$processed_css = '';

// Split CSS nested rules.
$parts = explode( '&', $css );
foreach ( $parts as $part ) {
$processed_css .= ( ! str_contains( $part, '{' ) )
? $selector . '{' . $part . '}' // If the part doesn't contain braces, it applies to the root level.
: $selector . $part; // Prepend the selector, which effectively replaces the "&" character.
}
return $processed_css;
}
aristath marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the stylesheet that results of processing
* the theme.json structure this object represents.
Expand Down Expand Up @@ -1062,7 +1083,19 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'

// Load the custom CSS last so it has the highest specificity.
if ( in_array( 'custom-css', $types, true ) ) {
// Add the global styles root CSS.
$stylesheet .= _wp_array_get( $this->theme_json, array( 'styles', 'css' ) );

// Add the global styles block CSS.
if ( isset( $this->theme_json['styles']['blocks'] ) ) {
foreach ( $this->theme_json['styles']['blocks'] as $name => $node ) {
if ( _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $name, 'css' ) ) ) {
$selector = static::$blocks_metadata[ $name ]['selector'];
$custom_block_css = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $name, 'css' ) );
$stylesheet .= $this->process_nested_css( $custom_block_css, $selector );
}
carolinan marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

return $stylesheet;
Expand Down
154 changes: 110 additions & 44 deletions packages/edit-site/src/components/global-styles/context-menu.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
/**
* WordPress dependencies
*/
import { __experimentalItemGroup as ItemGroup } from '@wordpress/components';
import { typography, border, color, layout } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import {
__experimentalItemGroup as ItemGroup,
__experimentalHStack as HStack,
__experimentalSpacer as Spacer,
FlexItem,
CardBody,
CardDivider,
} from '@wordpress/components';
import {
typography,
border,
color,
layout,
chevronLeft,
chevronRight,
} from '@wordpress/icons';
import { isRTL, __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
Expand All @@ -14,6 +30,7 @@ import { useHasDimensionsPanel } from './dimensions-panel';
import { useHasTypographyPanel } from './typography-panel';
import { useHasVariationsPanel } from './variations-panel';
import { NavigationButtonAsItem } from './navigation-button';
import { IconWithCurrentColor } from './icon-with-current-color';
import { ScreenVariations } from './screen-variations';

function ContextMenu( { name, parentMenu = '' } ) {
Expand All @@ -24,48 +41,97 @@ function ContextMenu( { name, parentMenu = '' } ) {
const hasLayoutPanel = hasDimensionsPanel;
const hasVariationsPanel = useHasVariationsPanel( name, parentMenu );

const { canEditCSS } = useSelect( ( select ) => {
carolinan marked this conversation as resolved.
Show resolved Hide resolved
const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } =
select( coreStore );

const globalStylesId = __experimentalGetCurrentGlobalStylesId();
const globalStyles = globalStylesId
? getEntityRecord( 'root', 'globalStyles', globalStylesId )
: undefined;

return {
canEditCSS:
!! globalStyles?._links?.[ 'wp:action-edit-css' ] ?? false,
};
}, [] );

return (
<ItemGroup>
{ hasTypographyPanel && (
<NavigationButtonAsItem
icon={ typography }
path={ parentMenu + '/typography' }
aria-label={ __( 'Typography styles' ) }
>
{ __( 'Typography' ) }
</NavigationButtonAsItem>
) }
{ hasColorPanel && (
<NavigationButtonAsItem
icon={ color }
path={ parentMenu + '/colors' }
aria-label={ __( 'Colors styles' ) }
>
{ __( 'Colors' ) }
</NavigationButtonAsItem>
) }
{ hasBorderPanel && (
<NavigationButtonAsItem
icon={ border }
path={ parentMenu + '/border' }
aria-label={ __( 'Border styles' ) }
>
{ __( 'Border' ) }
</NavigationButtonAsItem>
) }
{ hasLayoutPanel && (
<NavigationButtonAsItem
icon={ layout }
path={ parentMenu + '/layout' }
aria-label={ __( 'Layout styles' ) }
>
{ __( 'Layout' ) }
</NavigationButtonAsItem>
) }
{ hasVariationsPanel && (
<ScreenVariations name={ name } path={ parentMenu } />
) }
</ItemGroup>
<>
<ItemGroup>
{ hasTypographyPanel && (
<NavigationButtonAsItem
icon={ typography }
path={ parentMenu + '/typography' }
aria-label={ __( 'Typography styles' ) }
>
{ __( 'Typography' ) }
</NavigationButtonAsItem>
) }
{ hasColorPanel && (
<NavigationButtonAsItem
icon={ color }
path={ parentMenu + '/colors' }
aria-label={ __( 'Colors styles' ) }
>
{ __( 'Colors' ) }
</NavigationButtonAsItem>
) }
{ hasBorderPanel && (
<NavigationButtonAsItem
icon={ border }
path={ parentMenu + '/border' }
aria-label={ __( 'Border styles' ) }
>
{ __( 'Border' ) }
</NavigationButtonAsItem>
) }
{ hasLayoutPanel && (
<NavigationButtonAsItem
icon={ layout }
path={ parentMenu + '/layout' }
aria-label={ __( 'Layout styles' ) }
>
{ __( 'Layout' ) }
</NavigationButtonAsItem>
) }
{ hasVariationsPanel && (
<ScreenVariations name={ name } path={ parentMenu } />
) }
{ !! parentMenu && !! canEditCSS && (
carolinan marked this conversation as resolved.
Show resolved Hide resolved
<>
<CardDivider />
<CardBody>
<Spacer as="p" paddingTop={ 2 } marginBottom={ 4 }>
{ __(
'Add your own CSS to customize the block appearance.'
) }
</Spacer>
<ItemGroup>
<NavigationButtonAsItem
path={ parentMenu + '/css' }
aria-label={ __( 'Additional block CSS' ) }
>
<HStack justify="space-between">
<FlexItem>
{ __( 'Additional block CSS' ) }
</FlexItem>
<IconWithCurrentColor
icon={
isRTL()
? chevronLeft
: chevronRight
}
/>
</HStack>
</NavigationButtonAsItem>
</ItemGroup>
</CardBody>
<CardDivider />
</>
) }
</ItemGroup>
</>
);
}

Expand Down
10 changes: 7 additions & 3 deletions packages/edit-site/src/components/global-styles/custom-css.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ import { __ } from '@wordpress/i18n';
*/
import { useStyle } from './hooks';

function CustomCSSControl() {
const [ customCSS, setCustomCSS ] = useStyle( 'css' );
const [ themeCSS ] = useStyle( 'css', null, 'base' );
function CustomCSSControl( { blockName } ) {
// If blockName is defined, we are customizing CSS at the block level:
// styles.blocks.blockName.css
const block = !! blockName ? blockName : null;

const [ customCSS, setCustomCSS ] = useStyle( 'css', block );
const [ themeCSS ] = useStyle( 'css', block, 'base' );
const ignoreThemeCustomCSS = '/* IgnoreThemeCustomCSS */';

// If there is custom css from theme.json show it in the edit box
Expand Down
32 changes: 23 additions & 9 deletions packages/edit-site/src/components/global-styles/screen-css.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { sprintf, __ } from '@wordpress/i18n';
import { __experimentalVStack as VStack } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
Expand All @@ -11,19 +12,32 @@ import ScreenHeader from './header';
import Subtitle from './subtitle';
import CustomCSSControl from './custom-css';

function ScreenCSS() {
function ScreenCSS( { name } ) {
// If name is defined, we are customizing CSS at the block level.
// Display the block title in the description.
const blockType = getBlockType( name );
const title = blockType?.title;

const description =
title !== undefined
? sprintf(
// translators: %s: is the name of a block e.g., 'Image' or 'Table'.
__(
'Add your own CSS to customize the appearance of the %s block.'
),
title
)
: __(
'Add your own CSS to customize the appearance and layout of your site.'
);

return (
<>
<ScreenHeader
title={ __( 'CSS' ) }
description={ __(
'Add your own CSS to customize the appearance and layout of your site.'
) }
/>
<ScreenHeader title={ __( 'CSS' ) } description={ description } />
<div className="edit-site-global-styles-screen-css">
<VStack spacing={ 3 }>
<Subtitle>{ __( 'ADDITIONAL CSS' ) }</Subtitle>
<CustomCSSControl />
<CustomCSSControl blockName={ name } />
</VStack>
</div>
</>
Expand Down
7 changes: 4 additions & 3 deletions packages/edit-site/src/components/global-styles/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ function ContextScreens( { name, parentMenu = '' } ) {
<ScreenLayout name={ name } variationPath={ variationPath } />
</GlobalStylesNavigationScreen>

<GlobalStylesNavigationScreen path={ parentMenu + '/css' }>
<ScreenCSS name={ name } />
</GlobalStylesNavigationScreen>

{ !! blockStyleVariations?.length && (
<BlockStylesNavigationScreens
blockStyles={ blockStyleVariations }
Expand Down Expand Up @@ -280,9 +284,6 @@ function GlobalStylesUI( { isStyleBookOpened, onCloseStyleBook } ) {
{ isStyleBookOpened && (
<GlobalStylesStyleBook onClose={ onCloseStyleBook } />
) }
<GlobalStylesNavigationScreen path="/css">
<ScreenCSS />
</GlobalStylesNavigationScreen>
</NavigatorProvider>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,18 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => {
return result;
};

export const getBlockNames = ( blockTypes ) => {
const result = {};
blockTypes.forEach( ( blockType ) => {
const name = blockType.name;
result[ name ] = {
name,
};
} );

return result;
};

/**
* If there is a separator block whose color is defined in theme.json via background,
* update the separator color to the same value by using border color.
Expand Down Expand Up @@ -1016,10 +1028,13 @@ export function useGlobalStylesOutput() {
return [];
}
mergedConfig = updateConfigWithSeparator( mergedConfig );
const blockSelectors = getBlockSelectors(

const blockSelectors = getBlockSelectors(
getBlockTypes(),
getBlockStyles
);
const blockNames = getBlockNames( getBlockTypes() );
aristath marked this conversation as resolved.
Show resolved Hide resolved

const customProperties = toCustomProperties(
mergedConfig,
blockSelectors
Expand Down Expand Up @@ -1049,6 +1064,35 @@ export function useGlobalStylesOutput() {
},
];

const processCSSNesting = ( css, blockSelector ) => {
let processedCSS = '';

// Split CSS nested rules.
const parts = css.split( '&' );
parts.forEach( ( part ) => {
processedCSS += ! part.includes( '{' )
? blockSelector + '{' + part + '}' // If the part doesn't contain braces, it applies to the root level.
: blockSelector + part; // Prepend the selector, which effectively replaces the "&" character.
} );
return processedCSS;
};
aristath marked this conversation as resolved.
Show resolved Hide resolved

// Loop through the blocks to check if there are custom CSS values.
// If there are, get the block selector and push the selector together with
// the CSS value to the 'stylesheets' array.
Object.entries( blockNames ).forEach( ( name ) => {
if ( mergedConfig.styles.blocks[ name[ 0 ] ]?.css ) {
const selector = blockSelectors[ name[ 0 ] ].selector;
stylesheets.push( {
css: processCSSNesting(
mergedConfig.styles.blocks[ name[ 0 ] ]?.css,
selector
),
isGlobalStyles: true,
} );
}
} );

return [ stylesheets, mergedConfig.settings, filters ];
}, [
hasBlockGapSupport,
Expand Down