-
-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Motivation & Examples
Currently css returns a single string with all class names, so it isn't possible to get a single class name for a specific property. For example:
const classNames = css({ color: 'blue', background: 'red' }) // e.g output: "c0 c1"
classNames.color // <<< this isn't possibleIt'd be great if the result of css was like:
{
color: 'c0',
background: 'c1',
toString() { return 'c0 c1' },
}An example use case:
const styles = css({ color: 'blue', background: 'red' })
function MyComponent() {
return (
<div className={styles.background}>
<ChildComponent className={styles.color} />
</div>
)
}This would offer many benefits:
- As a user, you could avoid using a selector to style children (e.g:
& > div) and just pass down class names as props. In this case, a child component that is re-usable but it should be styled differently depending on where it is used. For example, a re-usableHeadingcomponent which could have different color. - It'd make it easier to create a Webpack plugin/loader to extract static styles Ahead of time style processing with static CSS extraction and a Babel or webpack plugin #37 because. For example:
Static analysis is difficult or impossible
// A.js
const ComponentA = ({ color }) => <div className={css({ color })} /> // What is color??
// B.js
const ComponentB = () => <ComponentA color="#fff" />However, this is better and makes it possible to make static analysis of the code:
// A.js
const ComponentA = ({ color }) => <div className={color} />
// B.js
const styles = css({ color: '#fff' }) // Static analysis is possible
const ComponentB = <ComponentA color={styles.color} />Details
stylex was shown in ReactConf by Facebook, so the original idea comes from there:
const styles = stylex.create({
blue: { color: 'blue' },
red: { color: 'red' },
})
function MyComponent() {
return (
<span className={styles('red', 'blue')}>I'm blue</span>
)
}Based on that:
csscould return an object instead of serializing all classnames:
const styles = css({
color: 'hotpink',
backgroundColor: 'red',
selectors: {
'&:focus': { color: 'green' },
'&:hover': { color: 'lime' },
},
})
// Returned value should look like:
const styles = {
color: 'c0',
backgroundColor: 'c1',
selectors: {
'&:focus': { color: 'c2' },
'&:hover': { color: 'c3' },
toString() { return 'c2 c3' },
},
toString() { return 'c0 c1 c2 c3' },
}This could allow to access any classname:
styles.toString() // c0 c1 c2 c3
styles.color // c0
styles.selectors.toString() // c2 c3
styles.selectors['&:focus'] // c2Note: also add valueOf()? 🤔
otioncould export acreatemethod (or choose better name) that could allow to create a styles object. This could basically just be (overly simplified version):
function create(styleObj: Record<string, CSSPropsThing>) { // << Generic type is better
const keys = Object.keys(styleObj)
const styles = {}
keys.forEach(k => styles[k] = css(styleObj[k])) // << Take into account nested stuff (selectors)
styles.toString = function() { return '....' /* get all classnames somehow */ }
}So, similarly as with css:
const styles = create({
square: {
color: 'hotpink',
border: '1px solid blue',
},
})
// Returned value should look like
const styles = {
square: {
color: 'c0',
border: 'c1',
toString() {...}
},
toString() { return combineAllStyles(...) } // See next 👇
}otioncould export acombinemethod (or choose better) to combine style objects and deduplicate styles:
const style1 = css({ color: 'red', background: 'blue' }) // { color: 'c0', background: 'c1' }
const style2 = css({ color: 'blue' }) // { color: 'c2' }
const finalStyle = combineOrDedupeOrFancyName(style1, style2)
// Return should be
const finalStyle = { color: 'c2', background: 'c1' } // color is blue, background is blueTL;DR
It's a breaking change but I believe this could allow for:
- Easier static analysis, so a babel plugin would be easier to write (see: https://github.com/giuseppeg/style-sheet)
- It allows for composable class names that could be passed down as props in React apps (or similarly outside of React) and can potentially allow the user to avoid writing complex selectors that style children
Summarizing how the new API could look like and it's usage:
// A.js - unchanged (because `toString()`)
const MyComponentA = () => (
<div className={css({ color: 'red' })} />
)
// B.js - `toString` of `create` return would combine & serialize all class names
const MyComponentB = () => (
<div className={css.create({
box: { border: '1px solid red' },
other: { color: 'blue' }
})} />
)
// C.js - composable
const MyComponentC = ({ className }) => <div className={className} />
const styles = css.create({
box: { border: '1px solid green', background: 'grey' },
child: { color: 'blue' },
})
const MyComponentD = ({ inheritStyles }) => (
<div className={css.combine(styles.box, inheritStyles)}> // < Combine own styles & optionally inherit from props
<MyComponentC className={styles.child} /> // pass down single class name
</div>
)
const sectionStyles = css.create({
div: { ... }
box: { border: '2px solid lime' },
})
const MyComponentE = () => (
<div className={sectionStyles.div}>
<MyComponentD inheritStyles={sectionStyles.box} /> // <<< override MyComponentD `border`
</div>
)Let me know what do you think 😅 and if it's within the scope of otion to support this. I can help with the implementation 😄