Skip to content

Commit

Permalink
Components: Add create-styles (#30509)
Browse files Browse the repository at this point in the history
* Components: Add create-styles

* Fix disallowed syntaxes

* Add missing TS resolution

* Remove ThemeProvider and duplicated hooks

* Clean up READMEs

* Remove unnecessary deps

* Remove stray `is` import

* Change namespace to experimental to avoid introducing new public APIs

* Remove stray mentions of g2

* Remove `css` prop and automatic prop filtering
  • Loading branch information
sarayourfriend authored Apr 8, 2021
1 parent c71a997 commit 8aa446b
Show file tree
Hide file tree
Showing 39 changed files with 2,289 additions and 1 deletion.
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@babel/runtime": "^7.13.10",
"@emotion/core": "^10.1.1",
"@emotion/css": "^10.0.22",
"@emotion/hash": "^0.8.0",
"@emotion/native": "^10.0.22",
"@emotion/styled": "^10.0.23",
"@wordpress/a11y": "file:../a11y",
Expand All @@ -50,12 +51,15 @@
"@wp-g2/styles": "^0.0.160",
"@wp-g2/utils": "^0.0.160",
"classnames": "^2.2.5",
"create-emotion": "^10.0.27",
"dom-scroll-into-view": "^1.2.1",
"downshift": "^6.0.15",
"gradient-parser": "^0.1.5",
"highlight-words-core": "^1.2.2",
"hoist-non-react-statics": "^3.3.2",
"lodash": "^4.17.19",
"memize": "^1.1.0",
"mitt": "^2.1.0",
"moment": "^2.22.1",
"re-resizable": "^6.4.0",
"react-dates": "^17.1.1",
Expand All @@ -64,6 +68,7 @@
"react-use-gesture": "^9.0.0",
"reakit": "^1.3.5",
"rememo": "^3.0.0",
"styled-griddie": "^0.1.3",
"tinycolor2": "^1.4.2",
"uuid": "^8.3.0"
},
Expand Down
55 changes: 55 additions & 0 deletions packages/components/src/ui/create-styles/create-compiler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# create-compiler

This module creates the Emotion instance that backs the style system. It integrates plugins and creates the core `css` function that wraps Emotion's `css` function adding support for breakpoint values on each property.

## Breakpoint values

Breakpoint values are supported by passing an array of values to a CSS property. For example:

```js
css({
width: [300, 500, 700],
});
```

This will dynamically respond to breakpoints and render the appropriate width for each `min-width`. The breakpoints are documented in the code in [`utils.js`](./utils.js).

## Plugins

`createCompiler` supports passing certain parameters to plugins. Plugin initialization should be contained to [`plugins/index.js`](./plugins/index.js).

The individual plugins are documented in [`plugins/README.md`](./plugins/README.md).

## Custom iframe support

Emotion by default does not support iframe styling. This style system solves this by implementing a custom `sheet.insert` that exposes a `sheet.insert` event which can be listened to by style providers to receive styles from outside of the current iframe.

## Interplated Components

`css` also supports passing style system-connected components as selectors in the same style as `styled-components`. It does this _without any Babel transformations_. Interpolated components are transformed to a special interpolated class name by the `css` function. Components are given an interpolation class name (prefixed by `ic-`) by either the `contextConnect` hook or by `styled` itself. `css` then detects when a component has been passed in and transorms it into a CSS selector.

For example:

```js
const Text = styled.div`
color: red;
`;

const greenText = css`
${Text} {
color: green;
}
`;
```

Now any child `Text` of a component that applies the `greenText` generated class name will be targeted with the `color: green` styles.

Psueudo selectors against the interpolated component are possible as well:

```js
const blueText = css`
${Text}:first-child {
color: blue;
}
`;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* External dependencies
*/
import createEmotion from 'create-emotion';
import mitt from 'mitt';

/**
* Internal dependencies
*/
import { RootStore } from '../css-custom-properties';
import { createCSS } from './create-css';
import { createPlugins } from './plugins';
import { breakpoints, generateInterpolationName } from './utils';

const defaultOptions = {
key: 'css',
specificityLevel: 1,
rootStore: new RootStore(),
};

/* eslint-disable jsdoc/valid-types */
/**
* @typedef {import('create-emotion').Emotion & {
* breakpoints: typeof breakpoints,
* __events: import('mitt').Emitter,
* generateInterpolationName(): string,
* }} Compiler
*/
/* eslint-enable jsdoc/valid-types */

/**
* @typedef {import('create-emotion').Options & {
* key?: string,
* specificityLevel?: number,
* rootStore: import('../css-custom-properties').RootStore
* }} CreateCompilerOptions
*/

/**
* @param {CreateCompilerOptions} options
* @return {Compiler} The compiler.
*/
export function createCompiler( options ) {
const mergedOptions = {
...defaultOptions,
...options,
};

const { key, rootStore, specificityLevel } = mergedOptions;

const defaultPlugins = createPlugins( {
key,
specificityLevel,
rootStore,
} );

if ( options.stylisPlugins ) {
if ( Array.isArray( options.stylisPlugins ) ) {
mergedOptions.stylisPlugins = [
...defaultPlugins,
...options.stylisPlugins,
];
} else if ( typeof options.stylisPlugins !== 'undefined' ) {
// just a single plugin was passed in, as is allowed by emotion
mergedOptions.stylisPlugins = [
...defaultPlugins,
options.stylisPlugins,
];
} else {
mergedOptions.stylisPlugins = defaultPlugins;
}
} else {
mergedOptions.stylisPlugins = defaultPlugins;
}

/**
* We're creating a custom Emotion instance to ensure that the style system
* does not conflict with (potential) existing Emotion instances.
*
* We're also able to provide createEmotion with our custom Stylis plugins.
*/
const customEmotionInstance = {
...createEmotion( mergedOptions ),
/**
* Exposing the breakpoints used in the internal Style system.
*/
breakpoints,
/**
* An internal custom event emitter (pub/sub) for Emotion.
* This is currently used in <StyleFrameProvider />
* to subscribe to and sync style injection.
*/
__events: mitt(),
generateInterpolationName,
};

/**
* Enhance the base css function from Emotion to add features like responsive
* value handling and compiling an Array of css() calls.
*/
const { css } = customEmotionInstance;
customEmotionInstance.css = createCSS( css );

/**
* Modify the sheet.insert method to emit a `sheet.insert` event
* within the internal custom event emitter.
*/
const __insert = customEmotionInstance.sheet.insert;
customEmotionInstance.sheet.insert = (
/* eslint-disable jsdoc/valid-types */
/** @type {[rule: string]} */ ...args
) =>
/* eslint-enable jsdoc/valid-types */
{
__insert.apply( customEmotionInstance.sheet, [ ...args ] );
customEmotionInstance.__events.emit( 'sheet.insert', ...args );
};

return customEmotionInstance;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* External dependencies
*/
import { isPlainObject } from 'lodash';

/**
* Internal dependencies
*/
import { responsive } from './responsive';

/**
* @param {CSS} compile
* @return {CSS} The CSS function
*/
export function createCSS( compile ) {
/**
* An enhanced version of the compiler's (Emotion) CSS function.
* This enhanced CSS supports dynamic responsive (breakpoint-based) styles if
* the value is an array of values.
*
* @example
* ```js
* // The following will render a CSS rule where the widths will be:
* // 100px for mobile
* // 200px for tablet
* // 500px for desktop
* css({
* width: [100, 200, 500]
* })
* ```
* @param {Parameters<CSS>} args
* @return {ReturnType<CSS>} The compiled CSS className associated with the styles.
*/
function css( ...args ) {
const [ arg, ...rest ] = args;

if ( isPlainObject( arg ) ) {
return compile(
responsive( /** @type {ObjectInterpolation} */ ( arg ) )
);
}

if ( Array.isArray( arg ) ) {
for ( let i = 0, len = arg.length; i < len; i++ ) {
const n = arg[ i ];
if ( isPlainObject( n ) ) {
arg[ i ] = responsive(
/** @type {ObjectInterpolation} */ ( n )
);
}
}
return compile( ...[ arg, ...rest ] );
}

return compile( ...args );
}

// @ts-ignore No amount of zhuzhing will convince TypeScript that a function with the parameters and return type for CSS is in fact the same type
return css;
}

/* eslint-disable jsdoc/valid-types */
/** @typedef {import('create-emotion').Emotion['css']} CSS */
/** @typedef {import('create-emotion').ObjectInterpolation<unknown>} ObjectInterpolation */
/* eslint-enable jsdoc/valid-types */
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './create-compiler';
export * from './create-css';
export * from './responsive';
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# plugins

This foler contains all the applied plugins in the style system.

**Nota bene**: All of the plugins can be removed once IE11 support is officially dropped.

## Extra Specificity

This plugin automatically compounds selector specificity by simply repeating the selector x number of times. For example, if a specificity of 3 is passed in, the plugin will transform:

```css
.css-abc123 {
color: red;
}
```

into:

```css
.css-abc123.css-abc123.css-abc123 {
color: red;
}
```

This is meant to prevent "hacks" from being applied to the component system via regular css selection (or rather to make it difficult/annoying to do so), forcing consumers to use the style system itself, for example, the `css` prop and theme variables, to apply custom styles.

It is currently set to a specificity of 1 to disable it. This may be reversed in the future. If it isn't reversed in the future, at some point we shoiuld just remove it.

## CSS Variable Fallback

The [`css-variables.js` ](./css-variables.js) plugin automatically generates fallback variables to support browsers that lack CSS variable support.

Given WordPress core is dropping IE11 support,we might be able to drop this plugin altogether.
Loading

0 comments on commit 8aa446b

Please sign in to comment.