Skip to content

Commit

Permalink
Components: Try color theming (#44668)
Browse files Browse the repository at this point in the history
* Add Theme component

* rename button color variable

* add darker color variants, replace in button component

* Move types to separate file

* README

* Refactor styles to separate file

* Export component as experimental

* Add unit tests

* CHANGELOG

* docs index

* Improve README wording

* Move theme variables to separate sass file

* Polish storybook

* Add initial chapted to CONTRIBUTING docs

* Add color validation

* Refine type description

* Add eslint disable comment for console warn

* Add JSDocs for the default-exported component

Co-authored-by: chad1008 <13856531+chad1008@users.noreply.github.com>
Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>
  • Loading branch information
3 people authored Oct 12, 2022
1 parent 10953a2 commit 5d773ab
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 25 deletions.
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,12 @@
"markdown_source": "../packages/components/src/textarea-control/README.md",
"parent": "components"
},
{
"title": "Theme",
"slug": "theme",
"markdown_source": "../packages/components/src/theme/README.md",
"parent": "components"
},
{
"title": "ToggleControl",
"slug": "toggle-control",
Expand Down
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
- `FontSizePicker`: Convert to TypeScript ([#44449](https://github.com/WordPress/gutenberg/pull/44449)).
- `FontSizePicker`: Replace SCSS with Emotion + components ([#44483](https://github.com/WordPress/gutenberg/pull/44483)).

### Experimental

- Add experimental `Theme` component ([#44668](https://github.com/WordPress/gutenberg/pull/44668)).

## 21.1.0 (2022-09-21)

### Deprecations
Expand Down
20 changes: 20 additions & 0 deletions packages/components/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,26 @@ All new component should be styled using [Emotion](https://emotion.sh/docs/intro

Note: Instead of using Emotion's standard `cx` function, the custom [`useCx` hook](/packages/components/src/utils/hooks/use-cx.ts) should be used instead.

### Themeing

_Note: Themeing is an experimental feature and still being actively developed. Its APIs are an early implementation subject to drastic and breaking changes_

The [`Theme` component](/packages/components/src/theme/README.md) can be used to tweak the visual appearance of the components from the `@wordpress/components` package.

```jsx
import { __experimentalTheme as Theme } from '@wordpress/components';

const Example = () => {
return (
<Theme accent="red">
<Button variant="primary">I'm red</Button>
<Theme accent="blue">
<Button variant="primary">I'm blue</Button>
</Theme>
</Theme>
);
};
```

### Deprecating styles

Expand Down
50 changes: 25 additions & 25 deletions packages/components/src/button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

&[aria-expanded="true"],
&:hover {
color: var(--wp-admin-theme-color);
color: $components-color-accent;
}

// Unset some hovers, instead of adding :not specificity.
Expand All @@ -31,7 +31,7 @@
// Focus.
// See https://github.com/WordPress/gutenberg/issues/13267 for more context on these selectors.
&:focus:not(:disabled) {
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) $components-color-accent;

// Windows High Contrast mode will show this outline, but not the box-shadow.
outline: 3px solid transparent;
Expand All @@ -43,7 +43,7 @@

&.is-primary {
white-space: nowrap;
background: var(--wp-admin-theme-color);
background: $components-color-accent;
color: $white;
text-decoration: none;
text-shadow: none;
Expand All @@ -52,18 +52,18 @@
outline: 1px solid transparent;

&:hover:not(:disabled) {
background: var(--wp-admin-theme-color-darker-10);
background: $components-color-accent-darker-10;
color: $white;
}

&:active:not(:disabled) {
background: var(--wp-admin-theme-color-darker-20);
border-color: var(--wp-admin-theme-color-darker-20);
background: $components-color-accent-darker-20;
border-color: $components-color-accent-darker-20;
color: $white;
}

&:focus:not(:disabled) {
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) $components-color-accent;
}

&:disabled,
Expand All @@ -72,15 +72,15 @@
&[aria-disabled="true"]:enabled, // This catches a situation where a Button is aria-disabled, but not disabled.
&[aria-disabled="true"]:active:enabled {
color: rgba($white, 0.4);
background: var(--wp-admin-theme-color);
border-color: var(--wp-admin-theme-color);
background: $components-color-accent;
border-color: $components-color-accent;
opacity: 1;
outline: none;

&:focus:enabled {
box-shadow:
0 0 0 $border-width $white,
0 0 0 3px var(--wp-admin-theme-color);
0 0 0 3px $components-color-accent;
}
}

Expand All @@ -93,13 +93,13 @@
/* stylelint-disable */
background-image: linear-gradient(
-45deg,
var(--wp-admin-theme-color) 33%,
var(--wp-admin-theme-color-darker-20) 33%,
var(--wp-admin-theme-color-darker-20) 70%,
var(--wp-admin-theme-color) 70%
$components-color-accent 33%,
$components-color-accent-darker-20 33%,
$components-color-accent-darker-20 70%,
$components-color-accent 70%
);
/* stylelint-enable */
border-color: var(--wp-admin-theme-color);
border-color: $components-color-accent;
}
}

Expand All @@ -114,13 +114,13 @@

&:active:not(:disabled) {
background: $gray-300;
color: var(--wp-admin-theme-color-darker-10);
color: $components-color-accent-darker-10;
box-shadow: none;
}

&:hover:not(:disabled) {
color: var(--wp-admin-theme-color-darker-10);
box-shadow: inset 0 0 0 $border-width var(--wp-admin-theme-color-darker-10);
color: $components-color-accent-darker-10;
box-shadow: inset 0 0 0 $border-width $components-color-accent-darker-10;
}

&:disabled,
Expand All @@ -140,10 +140,10 @@
*/

&.is-secondary {
box-shadow: inset 0 0 0 $border-width var(--wp-admin-theme-color);
box-shadow: inset 0 0 0 $border-width $components-color-accent;
outline: 1px solid transparent; // Shown in high contrast mode.
white-space: nowrap;
color: var(--wp-admin-theme-color);
color: $components-color-accent;
background: transparent;
}

Expand All @@ -153,7 +153,7 @@

&.is-tertiary {
white-space: nowrap;
color: var(--wp-admin-theme-color);
color: $components-color-accent;
background: transparent;
padding: $grid-unit-15 * 0.5; // This reduces the horizontal padding on tertiary/text buttons, so as to space them optically.

Expand Down Expand Up @@ -182,7 +182,7 @@
}

&:focus:not(:disabled) {
color: var(--wp-admin-theme-color);
color: $components-color-accent;
}

&:active:not(:disabled) {
Expand Down Expand Up @@ -230,7 +230,7 @@
background: none;
outline: none;
text-align: left;
color: var(--wp-admin-theme-color);
color: $components-color-accent;
text-decoration: underline;
transition-property: border, background, color;
transition-duration: 0.05s;
Expand All @@ -253,7 +253,7 @@
}

&:focus:not(:disabled) {
color: var(--wp-admin-theme-color);
color: $components-color-accent;
}
}
}
Expand Down Expand Up @@ -333,7 +333,7 @@
background: $gray-900;

&:focus:not(:disabled) {
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) $components-color-accent;

// Windows High Contrast mode will show this outline, but not the box-shadow.
outline: 2px solid transparent;
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export { Text as __experimentalText } from './text';
export { default as TextControl } from './text-control';
export { default as TextareaControl } from './textarea-control';
export { default as TextHighlight } from './text-highlight';
export { default as __experimentalTheme } from './theme';
export { default as Tip } from './tip';
export { default as ToggleControl } from './toggle-control';
export {
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Variables
@import "./utils/theme-variables.scss";

// Components
@import "./animate/style.scss";
@import "./autocomplete/style.scss";
@import "./button-group/style.scss";
Expand Down
34 changes: 34 additions & 0 deletions packages/components/src/theme/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Theme

<div class="callout callout-alert">
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
</div>

`Theme` allows defining theme variables for components in the `@wordpress/components` package.

Multiple `Theme` components can be nested in order to override specific theme variables.

## Usage

```jsx
import { __experimentalTheme as Theme } from '@wordpress/components';

const Example = () => {
return (
<Theme accent="red">
<Button variant="primary">I'm red</Button>
<Theme accent="blue">
<Button variant="primary">I'm blue</Button>
</Theme>
</Theme>
);
};
```

## Props

### `accent`: `string`

Used to set the accent color (used by components as the primary color). If an accent color is not defined, the default fallback value is the original WP Admin main theme color. No all valid CSS color syntaxes are supported — in particular, keywords (like `'currentcolor'`, `'inherit'`, `'initial'`, `'revert'`, `'unset'`...) and CSS custom properties (e.g. `var(--my-custom-property)`) are _not_ supported values for this property.

- Required: No
51 changes: 51 additions & 0 deletions packages/components/src/theme/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* External dependencies
*/
import { colord, extend } from 'colord';
import a11yPlugin from 'colord/plugins/a11y';
import namesPlugin from 'colord/plugins/names';

/**
* Internal dependencies
*/
import type { ThemeProps } from './types';
import type { WordPressComponentProps } from '../ui/context';
import { Wrapper } from './styles';

extend( [ namesPlugin, a11yPlugin ] );

/**
* `Theme` allows defining theme variables for components in the `@wordpress/components` package.
*
* Multiple `Theme` components can be nested in order to override specific theme variables.
*
*
* @example
* ```jsx
* import { __experimentalTheme as Theme } from '@wordpress/components';
*
* const Example = () => {
* return (
* <Theme accent="red">
* <Button variant="primary">I'm red</Button>
* <Theme accent="blue">
* <Button variant="primary">I'm blue</Button>
* </Theme>
* </Theme>
* );
* };
* ```
*/
function Theme( props: WordPressComponentProps< ThemeProps, 'div', true > ) {
const { accent } = props;
if ( accent && ! colord( accent ).isValid() ) {
// eslint-disable-next-line no-console
console.warn(
`wp.components.Theme: "${ accent }" is not a valid color value for the 'accent' prop.`
);
}

return <Wrapper { ...props } />;
}

export default Theme;
47 changes: 47 additions & 0 deletions packages/components/src/theme/stories/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';

/**
* Internal dependencies
*/
import Theme from '../index';
import Button from '../../button';

const meta: ComponentMeta< typeof Theme > = {
component: Theme,
title: 'Components (Experimental)/Theme',
argTypes: {
accent: { control: { type: 'color' } },
},
parameters: {
controls: { expanded: true },
docs: { source: { state: 'open' } },
},
};
export default meta;

const Template: ComponentStory< typeof Theme > = ( args ) => (
<Theme { ...args }>
<Button variant="primary">Hello</Button>
</Theme>
);

export const Default = Template.bind( {} );
Default.args = {};

export const Nested: ComponentStory< typeof Theme > = ( args ) => (
<Theme accent="tomato">
<Button variant="primary">Outer theme (hardcoded)</Button>

<Theme { ...args }>
<Button variant="primary">
Inner theme (set via Storybook controls)
</Button>
</Theme>
</Theme>
);
Nested.args = {
accent: 'blue',
};
28 changes: 28 additions & 0 deletions packages/components/src/theme/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { colord } from 'colord';
import styled from '@emotion/styled';
import { css } from '@emotion/react';

/**
* Internal dependencies
*/
import type { ThemeProps } from './types';

const accentColor = ( { accent }: ThemeProps ) =>
accent
? css`
--wp-components-color-accent: ${ accent };
--wp-components-color-accent-darker-10: ${ colord( accent )
.darken( 0.1 )
.toHex() };
--wp-components-color-accent-darker-20: ${ colord( accent )
.darken( 0.2 )
.toHex() };
`
: undefined;

export const Wrapper = styled.div< ThemeProps >`
${ accentColor }
`;
Loading

0 comments on commit 5d773ab

Please sign in to comment.