# 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

- :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