diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d643b9b7ab4a1d..f4496cd49cbc42 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -7,9 +7,10 @@ - `ExternalLink`: No longer sets the `rel` attribute by default. Consumers relying on the previous behavior should pass `rel` explicitly ([#79743](https://github.com/WordPress/gutenberg/pull/79743)). - `View`: The legacy Emotion `css` prop no longer applies styles and is now accepted as a no-op for compatibility. Use `style` for inline styles or `className` for CSS-based styling instead ([#79443](https://github.com/WordPress/gutenberg/pull/79443)). - Components that compose Emotion style fragments with `cx()` should pass source-order-dependent fragments in a single `css()` call. Passing separate fragments can change override order after the following components stopped rendering styles through Emotion: - - `Truncate` ([#79446](https://github.com/WordPress/gutenberg/pull/79446)) - - `Divider` ([#79444](https://github.com/WordPress/gutenberg/pull/79444)) + - `Flex` ([#79450](https://github.com/WordPress/gutenberg/pull/79450)) - `Surface` ([#79445](https://github.com/WordPress/gutenberg/pull/79445)) + - `Theme` ([#79447](https://github.com/WordPress/gutenberg/pull/79447)) + - `Truncate` ([#79446](https://github.com/WordPress/gutenberg/pull/79446)) - `View` ([#79443](https://github.com/WordPress/gutenberg/pull/79443)) - The `__next40pxDefaultSize` prop is now true by default. The prop can be safely removed from the following: - `BorderBoxControl` ([#79420](https://github.com/WordPress/gutenberg/pull/79420)) @@ -49,6 +50,7 @@ - Update `@ariakit/react` to `0.4.32` ([#79860](https://github.com/WordPress/gutenberg/pull/79860)). - `Flex`: Migrate styles from Emotion to SCSS Modules ([#79450](https://github.com/WordPress/gutenberg/pull/79450)). - `Surface`: Migrate styles from Emotion to SCSS Modules and use WPDS tokens for migrated visual values ([#79445](https://github.com/WordPress/gutenberg/pull/79445)). +- `Theme`: Migrate styles from Emotion to SCSS Modules ([#79447](https://github.com/WordPress/gutenberg/pull/79447)). - `Truncate`: Migrate styles from Emotion to SCSS Modules ([#79446](https://github.com/WordPress/gutenberg/pull/79446)). - `View`: Migrate away from Emotion while preserving polymorphic `as` behavior and style cascade order ([#79443](https://github.com/WordPress/gutenberg/pull/79443)). @@ -58,6 +60,8 @@ ### Breaking Changes +- Components that compose Emotion style fragments with `cx()` should pass source-order-dependent fragments in a single `css()` call. Passing separate fragments can change override order after the following components stopped rendering styles through Emotion: + - `Divider` ([#79444](https://github.com/WordPress/gutenberg/pull/79444)) - The `__next40pxDefaultSize` prop is now true by default. The prop can be safely removed from the following: - `BoxControl` ([#79419](https://github.com/WordPress/gutenberg/pull/79419)) - `TextControl` ([#79386](https://github.com/WordPress/gutenberg/pull/79386)) diff --git a/packages/components/src/theme/index.tsx b/packages/components/src/theme/index.tsx index fbb92f59278d1b..f75cbff2933753 100644 --- a/packages/components/src/theme/index.tsx +++ b/packages/components/src/theme/index.tsx @@ -1,16 +1,31 @@ -/** - * WordPress dependencies - */ +import clsx from 'clsx'; +import type { CSSProperties } from 'react'; + import { useMemo } from '@wordpress/element'; -/** - * Internal dependencies - */ -import type { ThemeProps } from './types'; +import type { ThemeOutputValues, ThemeProps } from './types'; import type { WordPressComponentProps } from '../context'; -import { colorVariables, Wrapper } from './styles'; import { generateThemeVariables } from './color-algorithms'; -import { useCx } from '../utils'; +import styles from './style.module.scss'; +import { PolymorphicElement } from '../utils/polymorphic-element'; + +const getColorVariables = ( { + colors, +}: ThemeOutputValues ): CSSProperties => ( { + '--wp-components-color-accent': colors.accent, + '--wp-components-color-accent-darker-10': colors.accentDarker10, + '--wp-components-color-accent-darker-20': colors.accentDarker20, + '--wp-components-color-accent-inverted': colors.accentInverted, + '--wp-components-color-background': colors.background, + '--wp-components-color-foreground': colors.foreground, + '--wp-components-color-foreground-inverted': colors.foregroundInverted, + ...Object.fromEntries( + Object.entries( colors.gray ?? {} ).map( ( [ key, value ] ) => [ + `--wp-components-color-gray-${ key }`, + value, + ] ) + ), +} ); /** * `Theme` allows defining theme variables for components in the `@wordpress/components` package. @@ -35,21 +50,31 @@ function Theme( { accent, background, className, + style, ...props }: WordPressComponentProps< ThemeProps, 'div', true > ) { - const cx = useCx(); - const classes = useMemo( + const themeVariables = useMemo( () => - cx( - ...colorVariables( - generateThemeVariables( { accent, background } ) - ), - className + getColorVariables( + generateThemeVariables( { accent, background } ) ), - [ accent, background, className, cx ] + [ accent, background ] + ); + const wrapperStyle = useMemo( + () => ( { + ...themeVariables, + ...style, + } ), + [ style, themeVariables ] ); - return ; + return ( + + ); } export default Theme; diff --git a/packages/components/src/theme/style.module.scss b/packages/components/src/theme/style.module.scss new file mode 100644 index 00000000000000..7dbb3487c7f9ca --- /dev/null +++ b/packages/components/src/theme/style.module.scss @@ -0,0 +1,4 @@ +.wrapper { + /* stylelint-disable-next-line declaration-property-value-disallowed-list -- Preserve Theme's currentColor fallback when no foreground variable is generated. */ + color: var(--wp-components-color-foreground, currentColor); +} diff --git a/packages/components/src/theme/styles.ts b/packages/components/src/theme/styles.ts deleted file mode 100644 index 948c2d586649fb..00000000000000 --- a/packages/components/src/theme/styles.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * External dependencies - */ -import styled from '@emotion/styled'; -import { css } from '@emotion/react'; - -/** - * Internal dependencies - */ -import type { ThemeOutputValues } from './types'; - -export const colorVariables = ( { colors }: ThemeOutputValues ) => { - const shades = Object.entries( colors.gray || {} ) - .map( ( [ k, v ] ) => `--wp-components-color-gray-${ k }: ${ v };` ) - .join( '' ); - - return [ - css` - --wp-components-color-accent: ${ colors.accent }; - --wp-components-color-accent-darker-10: ${ colors.accentDarker10 }; - --wp-components-color-accent-darker-20: ${ colors.accentDarker20 }; - --wp-components-color-accent-inverted: ${ colors.accentInverted }; - - --wp-components-color-background: ${ colors.background }; - --wp-components-color-foreground: ${ colors.foreground }; - --wp-components-color-foreground-inverted: ${ colors.foregroundInverted }; - - ${ shades } - `, - ]; -}; - -export const Wrapper = styled.div` - color: var( --wp-components-color-foreground, currentColor ); -`; diff --git a/packages/components/src/theme/test/index.tsx b/packages/components/src/theme/test/index.tsx index 1b32fa1dfa2d66..18efa4e2d443ae 100644 --- a/packages/components/src/theme/test/index.tsx +++ b/packages/components/src/theme/test/index.tsx @@ -1,12 +1,6 @@ -/** - * External dependencies - */ import { render, screen } from '@testing-library/react'; import type { ReactNode } from 'react'; -/** - * Internal dependencies - */ import Theme from '../'; type MyThemableComponentProps = { @@ -25,6 +19,29 @@ const MyThemableComponent = ( props: MyThemableComponentProps ) => { }; describe( 'Theme', () => { + it( 'should support the as prop', () => { + render( ); + + expect( screen.getByTestId( 'theme' ) ).toHaveProperty( + 'tagName', + 'SECTION' + ); + } ); + + it( 'lets user styles override generated theme variables', () => { + render( + + ); + + expect( screen.getByTestId( 'theme' ) ).toHaveStyle( { + '--wp-components-color-accent': '#654321', + } ); + } ); + describe( 'accent color', () => { it( 'does not define the accent color (and its variations) as a CSS variable when the `accent` prop is undefined', () => { render( diff --git a/tools/eslint/suppressions.json b/tools/eslint/suppressions.json index a561536ec018ed..3784567c512942 100644 --- a/tools/eslint/suppressions.json +++ b/tools/eslint/suppressions.json @@ -892,11 +892,6 @@ "count": 2 } }, - "packages/components/src/theme/styles.ts": { - "no-restricted-imports": { - "count": 2 - } - }, "packages/components/src/toggle-group-control/toggle-group-control-option-base/styles.ts": { "no-restricted-imports": { "count": 2