# makeStyles ## v0 approach Fela CSS-in-JS - atomic classes - one CSS property = one class - all selectors have one class specificity - can reuse classnames = smaller DOM - classes added to DOM when needed (= in non-deterministic order) For atomic classes to work as expected, shorthand CSS properties must be expanded. You cannot apply two classes setting the same property: - :exclamation: you cannot concatenate classes, you must deepmerge objects - :exclamation: you need to know who your child is - to pass `className` to nonFUI components - to pass `styles` to FUI components - :exclamation: you need to re-evaluate the whole thing in runtime everytime :snail: - you can cache component styles but once an override is passed to the component the source style objects must be merged => no cache - :+1: specificity is controlled by JS TMP overrides are part of theme object, most of them are one-off overrides: - :exclamation: huge object (several files, thousands of lines per component) - :exclamation: unnecessary indirection - :exclamation: no dead code elimination ## useCSS - use Fela for basic component styles and allow overrides to be applied on top of that - without reevaluating basic component styles - use `Emotion` for overrides - monolitic - generate `.usecss.asdf` selector for the override (two classes > Fela's single class) - always pass to children as `className` - as a side effect, add the className and corresponding style object to a global dictionary - if the child is a nonFUI component, it just applies the className - if the child is a FUI component, it referrences the dictionary in its `useCSS` call and insted of concatenating classes, it concatenates stringified style objects ![](https://i.imgur.com/3Fa64iF.png) - :exclamation: not sure this is usable for basic component styles - :exclamation: object string concatenation results in big classes ## v8 makeStyles - add build time step to generate stylesheet - in runtime only apply classes based on props and state - specificity defined by DOM insertion order - works great as long as all selectors are one class, specificity hell otherwise ```css .primary {} .disabled {} .primary.disabled {} /* You don't know how to override this */ ``` -tbd - cannot share styles between components - dom order breaks - inspired by MUI - MUI is facing the same specificity issues, [decided to sacrifice perf and use Emotion](https://github.com/mui-org/material-ui/issues/22342). ## "stylex" approach in makeStyles - inspired by [Facebook stylex](https://www.youtube.com/watch?v=9JZHodNR184). - can we use v8 `makeStyles` (build time styles) but let JS handle the "specificity"? Process the styles build-time: ```js source = { color: 'red', background: 'blue', ':hover': { background: 'green', } } buildOutput = { color: 'classABCD', background: 'classKLMN', ':hover+background': 'classXYZ0', } ``` Merge the classes run-time: ```js merged = { ...myClasses, ...overrideClasses }.values() ``` - splits the expensive part (processing styles, generating classnames) and the cheap part (merging classnames) - expensive part can be done build time - similar sideeffect as in useCSS (passing down classnames but referrencing a dictionary when merging) - hash based classnames = deterministic (vs Fela is sequential = non-deterministic) = supports server side rendering - injects tokens as CSS variables - can easily fallback to runtime evaluation in IE 11 - without any change required on component/overrides side - Similar approach used in Attlasian `compiled` v5 ## Where we are - [x] basic PoC implementation - [x] northstar Avatar (and related components) - [x] northstar Button - [x] hacked perf test [Dec 7] - [x] test in react-button [Dec 7] - [x] global selectors [Dec 10] - [x] keyframes [Dec 10] - [x] media queries [Dec 10] - [x] functions as matchers (improve perf) [Dec 10] - [ ] test overrides in TMP - take notes [Shift] - [x] add mergeStyles to `react-native-web/benchmarks` [Xu] - [ ] hack-test perf in TMP - [ ] test IE 11 - as long as all tokens are known ahead of time, you can do it build time (David has a PR) - [ ] build time magic - [ ] test variants (react-button variants) - how can override `primary` background and not break `disabled` from base component? - maybe it is correct, it is deterministic, overrides always win - you can use variables ## Concerns - `makeStyles` per slot or object in `makeStyles` - how does this affect v8? - backwards compatibility / upgrade path for `makeStyles` - matchers might be the decision factor - accoring to Xu, no components have been shipped with `makeStyles` yet - we should be able to do breaking changes to `makeStyles` - how does this affect Theme Provider work? - we need to do css expand. how can we expand this? ```jsx= padding: var(--four-values-padding) /* --four-values-padding: 1 2 3 4 */ ``` - what is the impact on bundle side having all the expanded styles in bundle? - how do we register (and reference) keyframes ## Performance Performance numbers available here: [/pox-riItRQ2WeGGEWXmATw](/pox-riItRQ2WeGGEWXmATw). ## Thoughts on API surface 1. Support creating simple classes without matchers: ```jsx= // All the same makeStyles({ background: 'red' }); makeStyles([{ background: 'red'}]); makeStyles([[null, { background: 'red '}]]) ``` 2. How are styles overridden in the theme? Example idea: ```jsx= // The Button styles need an id to pull overrides // from the theme: const makeStyles({ id: 'Button', { ... } }); // The theme needs to be able to define them: const theme = { styles: { Button: { // id? styles: makeStyles({ ... }) // maybe? } } } } ``` 3. What is the contract between makeStyles runtime and makeStyles after build-time optimizations? From: ```jsx makeStyles([ { background: 'blue', ':hover': { background: 'red' } }, [{ disabled: true }, { background: 'grey' }], }) ``` To: ```jsx= makeStyles([ { background: { className: 'abc' } }, [{ disabled: true }, { __className: '' }] }, }) ``` We will need to distinguish a classname object from a style object. Maybe `__className` is specific enough? ## Links Babel plugin proto work: https://codesandbox.io/s/makestylesextractor-prototype-owk84?file=/src/makeStylesExtractor.test.js