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

Automatic CSS Variable fallbacks (Components CSS) #24094

Closed
wants to merge 16 commits into from

Conversation

ItsJonQ
Copy link

@ItsJonQ ItsJonQ commented Jul 21, 2020

Screen Shot 2020-07-21 at 9 29 42 AM

(Image shows a generated CSS declaration fallback for it's var() usage in RangeControl)

This is a follow up to:
#24059

This update adds automatic CSS variable fallback handling for CSS-in-JS generated styles from our @wordpress/components package (powered by Emotion).

This is accomplished by combining a couple of parts:

  • A custom stylis plugin that can transform CSS declaration output
  • A custom Emotion provider to target CSS-in-JS generated styles

✨ Stylis Plugin

Stylis is the light-weight, power, and fast (and awesome) CSS compiler that many CSS-in-JS libraries use (e.g. Styled Components, Emotion, Linaria, etc...). Stylis allows us to add custom middleware (aka. plugins) that can modify output. This pathway is enabled by Emotion with the creation of a "custom cache".

👩‍🎤 Emotion Provider (and custom cache)

Don't let the custom cache scare you. This is how Emotion works under-the-hood! We're creating our own cache instance (the brains that coordinates everything for Emotion) and syncing it with the components via a Provider.

This is the same technique I introduced in this PR:
#23215

(I'll need to update that one once this one merges)

🙏 IE Only Please

To optimize for performance, the transformations only occur on browsers that do not support CSS variables. The plugin auto-detects this by running a window.CSS.supports() check.

This option can be enabled/disabled by passing skipSupportedBrowsers: false when setting up the plugin:

const createCustomCache = memoize( () => {
	return createEmotionCache( {
		stylisPlugins: [
			stylisPluginCssCustomProperties( { skipSupportedBrowsers: false } ),
		],
	} );
} );

This is useful for tests (but also for debugging)

🍿 StyledProvider

This new <StyledProvider /> enables us to seamlessly enhance our CSS-in-JS workflow by using additional plugins.

Some of these enhancements include...

  • Automatic scope boosting (to ensure consistent + predictable CSS overrides)
  • Automatic RTL handling

How has this been tested?

Tested heavily with a barrage of unit tests. These unit tests throw wild (but valid) combinations of CSS variables, fallbacks, and odd value combinations to ensure that the parser works correctly.

The integration with the <StyledProvider /> (and generated styles) were tested manually in local Gutenberg (for both the editor and Site Edit).

To test it locally...

  • Add skipSupportedBrowsers: false to the stylisPluginCssCustomProperties() plugin
  • Run npm run dev
  • Add a Cover block with an image
  • Inspect element on the RangeSlider component
  • You should see a color fallback

Screen Shot 2020-07-21 at 9 29 42 AM

Types of changes

  • Created a custom stylis plugin
  • Created a Provider that hooks it up
  • Integrates Provider with EditPost and EditSite
  • Removed manual CSS variable fallbacks (that were recently added)

cc'ing @diegohaz <3 (Just in case you're interested)

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • [n/a] My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@ItsJonQ ItsJonQ added [Type] Enhancement A suggestion for improvement. [Package] Components /packages/components labels Jul 21, 2020
@ItsJonQ ItsJonQ requested a review from youknowriad July 21, 2020 14:04
@ItsJonQ ItsJonQ self-assigned this Jul 21, 2020
*/
import { stylisPluginCssCustomProperties } from './plugins';

const createCustomCache = memoize( () => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technique suggested by a core Emotion member:
emotion-js/emotion#760 (comment)

@github-actions
Copy link

github-actions bot commented Jul 21, 2020

Size Change: +5.49 kB (0%)

Total Size: 1.21 MB

Filename Size Change
build/block-directory/index.js 8.72 kB -2 B (0%)
build/block-editor/index.js 130 kB +2 B (0%)
build/block-library/index.js 146 kB +1 B
build/components/index.js 177 kB +5.48 kB (3%)
build/data/index.js 8.77 kB -2 B (0%)
build/edit-post/index.js 306 kB +3 B (0%)
build/edit-site/index.js 22 kB +2 B (0%)
build/editor/index.js 43.1 kB +11 B (0%)
build/i18n/index.js 3.57 kB -1 B
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/annotations/index.js 3.78 kB 0 B
build/api-fetch/index.js 3.45 kB 0 B
build/autop/index.js 2.84 kB 0 B
build/blob/index.js 665 B 0 B
build/block-directory/style-rtl.css 943 B 0 B
build/block-directory/style.css 942 B 0 B
build/block-editor/style-rtl.css 11.1 kB 0 B
build/block-editor/style.css 11.1 kB 0 B
build/block-library/editor-rtl.css 8.94 kB 0 B
build/block-library/editor.css 8.94 kB 0 B
build/block-library/style-rtl.css 7.82 kB 0 B
build/block-library/style.css 7.82 kB 0 B
build/block-library/theme-rtl.css 837 B 0 B
build/block-library/theme.css 838 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 48.1 kB 0 B
build/components/style-rtl.css 15.2 kB 0 B
build/components/style.css 15.2 kB 0 B
build/compose/index.js 9.81 kB 0 B
build/core-data/index.js 12.3 kB 0 B
build/data-controls/index.js 772 B 0 B
build/date/index.js 31.8 kB 0 B
build/deprecated/index.js 768 B 0 B
build/dom-ready/index.js 571 B 0 B
build/dom/index.js 4.46 kB 0 B
build/edit-navigation/index.js 11.2 kB 0 B
build/edit-navigation/style-rtl.css 881 B 0 B
build/edit-navigation/style.css 885 B 0 B
build/edit-post/style-rtl.css 6.37 kB 0 B
build/edit-post/style.css 6.36 kB 0 B
build/edit-site/style-rtl.css 3.84 kB 0 B
build/edit-site/style.css 3.83 kB 0 B
build/edit-widgets/index.js 26.4 kB 0 B
build/edit-widgets/style-rtl.css 3.09 kB 0 B
build/edit-widgets/style.css 3.09 kB 0 B
build/editor/editor-styles-rtl.css 480 B 0 B
build/editor/editor-styles.css 482 B 0 B
build/editor/style-rtl.css 3.85 kB 0 B
build/editor/style.css 3.85 kB 0 B
build/element/index.js 4.65 kB 0 B
build/escape-html/index.js 735 B 0 B
build/format-library/index.js 7.7 kB 0 B
build/format-library/style-rtl.css 547 B 0 B
build/format-library/style.css 548 B 0 B
build/hooks/index.js 2.13 kB 0 B
build/html-entities/index.js 623 B 0 B
build/is-shallow-equal/index.js 712 B 0 B
build/keyboard-shortcuts/index.js 2.52 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.11 kB 0 B
build/list-reusable-blocks/style-rtl.css 476 B 0 B
build/list-reusable-blocks/style.css 476 B 0 B
build/media-utils/index.js 5.34 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.42 kB 0 B
build/nux/style-rtl.css 671 B 0 B
build/nux/style.css 668 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.43 kB 0 B
build/priority-queue/index.js 791 B 0 B
build/redux-routine/index.js 2.85 kB 0 B
build/reusable-blocks/index.js 3.06 kB 0 B
build/rich-text/index.js 13.2 kB 0 B
build/server-side-render/index.js 2.77 kB 0 B
build/shortcode/index.js 1.69 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 4.06 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.22 kB 0 B

compressed-size-action

@ItsJonQ
Copy link
Author

ItsJonQ commented Jul 22, 2020

Update! I released the plugin code under my personal Github:
https://github.com/ItsJonQ/stylis-plugin-css-variables

The reason is because I want to have it available for personal use.
It's been published on npm as well:
https://www.npmjs.com/package/stylis-plugin-css-variables

If folks prefer, we can totally keep the plugin code within the project (leave this PR as is).
Alternatively, we can use the npm package ✌️

Open to thoughts!

@ItsJonQ
Copy link
Author

ItsJonQ commented Jul 27, 2020

Update! I've made some updates since the original PR. These updates include fixes to more accurately handle things like rgba and CSS shorthand.

The package has 100% test coverage and I'm actively using it and maintaining it.

I think it may be easier to use the package directly rather than have the code + tests in the project (for now).

If that changes in the future, we can always copy/paste the necessary code into @wordpress/gutenberg.

@ItsJonQ ItsJonQ force-pushed the try/styled-provider-css-var-handling branch from 3afc743 to d9f69b2 Compare July 27, 2020 14:03
*/
import createEmotionCache from '@emotion/cache';
import { CacheProvider } from '@emotion/core';
import stylisPluginCssVariables from 'stylis-plugin-css-variables';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code originally existed in this repo as part of this PR.
It was abstracted to a separate repo/package:
https://github.com/ItsJonQ/stylis-plugin-css-variables

@ItsJonQ ItsJonQ mentioned this pull request Oct 28, 2020
6 tasks
@ItsJonQ
Copy link
Author

ItsJonQ commented Oct 28, 2020

cc'ing @saramarcondes

Haiiiii!!! Continuing our convo from your other CSS-in-JS PRs 😊 .

I think this StyledProvider will help smoother over CSS Variable fallback issues we talked about.
The ui.theme color's value has a fallback in it's value. That means that it should work for IE11 with my Stylis plugin.

Do you think you can take a quick look to make sure it works and the implementation feels okay?

If so, maybe we can proceed with adding the resolver in there as well (perhaps as a hook within StyledProvider)

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @ItsJonQ I like the new automatic abstraction.

I don't like "Providers" in general because they make components less easily reusable but it feels that at least a single provider is mandatory to use the WordPress components and I wonder if should rename it to be able to add any addition providers required later to the same one.

@@ -271,7 +272,7 @@ class EditorProvider extends Component {
);

return (
<>
<StyledProvider>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enables it for the post-editor (probably better moved to edit-post) but not for edit-widgets, edit-site, edit-navigation and list-reusabale-blocks (these are the top level packages).

  • How do we make sure folks don't forget to add these providers?
  • Is there a way to throw a warning if we detect a missing provider?
  • Should we embed other providers there in a single one? (thinking about popover and slot providers).
  • Should this be marked as a breaking change in the changelog of the components package?

@ItsJonQ
Copy link
Author

ItsJonQ commented Nov 2, 2020

Thanks for the PR @ItsJonQ I like the new automatic abstraction.

@youknowriad

Wonderful :)

I do not like Providers either, especially if they are mandatory (which sometimes cannot be avoided).

🌄 Background

For context, I revisited this PR because @saramarcondes started to graciously help out with components by refactoring styles to use CSS-in-JS. In doing so, she's run into a lot of roadblocks, many of which were things I've previously experienced.

🙈 Roadblocks

The primary roadblock would be converting the styles from Sass -> CSS-in-JS safely without introducing side-effects. This is quite tricky due to how (unexpected) coupled certain styles are.

Well intentioned style rules that targeted deeper HTML elements (therefore raising specificity) that happen organically over time.

That being said, these speed bumps are bound to happen regardless of we're doing a 1:1 Sass -> CSS-in-JS conversion or if we're doing something larger (like rewriting components like in G2 Components).

👉 Next Steps

After some decision with @saramarcondes, we felt like it may be best to pause on some of the CSS-in-JS conversion and focus more on G2 Components (Sara, please correct me if I'm wrong). The reason is because many of the issues Sara and I have encountered were resolved within G2's Style System (if not 100% resolved, then at least something to allow us to strategically iterate). We've also been testing out integration strategies for G2 x Gutenberg.

--

P.S. Back to Providers! In contrast, G2's Style System would not require a Provider to achieve this same effect, as the compiling via plugins (same basic technique) happens at a lower level. The only cause where a provider would be needed would be iFrame based rendering.

@ItsJonQ
Copy link
Author

ItsJonQ commented Nov 16, 2020

Closing this up for now! Hopefully we'll be able get this from G2 Components.

@ItsJonQ ItsJonQ closed this Nov 16, 2020
@aristath aristath deleted the try/styled-provider-css-var-handling branch November 16, 2020 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants