First off, I think styled-components is great, and that it makes all the right trade-offs for the purpose of being the default "styling in react" solution. I'm not suggesting any change in the core or the focus. I'm just sharing my personal experience/opinion.
My idea/question is about adding optional alternative APIs. I would love if styled-components could be a standard for css-in-js, but also flexible enough to be used in different ways as situations call for. For context, Glamour, for example, seems to let you use different interfaces for defining styles (https://github.com/threepointone/glamor/blob/master/docs/jsxstyle.md).
Sometimes I like the styled-components approach, but I often prefer doing this:
<Block position="absolute" right="0px" top="0px" padding="48px 24px 24px 48px">
<Calendar />
</Block>
I find I like styled-component's "generate a component from a property definition" API when working on UI library components, and for app components which are fairly well defined and reusable. If I were a UI designer working for an agency or a dev working on an open source component kit, I can totally see using the styled-components approach exclusively.
But many (perhaps most) components in my actual applications are not that. They are one-offs just added to get some random layout or style detail.
Just to list two quick issues with the styled-components approach, and leaving out of discussion more nuanced topics like the costs of a DSL. (1) When designing in code, I often want to see the elements with their styles together, nested as they will be in the app to "see" it at a glance, and (2) these kinds of components can be annoying to name like SecondWrapperForFirstLetterOfProfileQuote
(I've been writing in roughly the jsx-style way for two years now, so I know CSS fans find dreaming up new names much less frustrating than I now do!)
More or less, I like the styled-components approach for making widgets, but often prefer the inline JSX approach for the use and especially layout of the widgets in an app.
I'm not sure if it would be possible/reasonable to implement this abstraction in the app itself or an external library and not have at least a small performance hit (at least from putting the diffing of styles you know will never change on React). Right now you can implement the bare minimal API like so, but I expect a better solution would somehow directly use the internals of styled-components.
const mapPropsToCSS = (props) => {
delete props.theme;
// and so forth...
return map(props, (value, rule) => `${rule}: ${value};`)
}
export const Block = styled.div`
${props => mapPropsToCSS(props)}
`
This is all just for the purposes of throwing alternative APIs---and especially inline JSX---on the table.
I'm curious what others thoughts are, and, knowing nothing about the code, whether this is actually possible to do well on top of styled-components.
EDIT: I'd also point out that the inline JSX alternative is consistent with a key constraint behind styled-components, as given in Max's nice talk last month. That being it "gets rid of the mapping" between the component and the styles. Or so it seems to me, anyway!
<Block width={300} />
is just like styled.div'width: 300px'
except JS(X) and following the same pattern used to set properties in every other kind of component. The props for a styling component just happen to be styles. Given how browsers work and actual history, both approaches are valid and useful IMO. And if styled-components supported both, you could always use the CSS version if you happened to want media queries or to handle a task with some selector voodoo! (Personally I rarely feel the need for CSS in app code---as opposed to web page or UI library code---since I have to explicitly represent the, say, hover state or window size for more complex effects anyway...)
Let my put a note that you already can use objects in interpolations directly without mapping. So, minimal API implementation is simple enough to keep in application code:
const Block = styled.div`${({theme, ...props}) => props}`
Speaking of unitless props <Block width={300} />
it was discussed in https://github.com/styled-components/styled-components/pull/317
Thanks for the detailed suggestion. I'm not terribly keen on introducing another top-level syntax for defining styles, mainly because I think the emphasis _should_ be on encapsulating styles behind a named object. Your example name of SecondWrapperForFirstLetterOfProfileQuote
is the downside, of course, which is why we allow nesting:
const ProfileQuote = styled.div`
> div > :first-letter {
font-size: 3em;
}
`
The point being that breaking your styling into _named things_ is a useful exercise — it follows the same structure of good vanilla CSS. When things don't warrant a name themselves, they must be a part of some other named entity, so you can use nesting. It's a balance that works well for me that I'd like to keep as the sole API for defining Styled Components.
That said, your Block
component is a perfectly reasonable component all in itself. As @vdanchenkov points out, the implementation is fairly trivial, and using Block
s in that way throughout your app is totally fine. My suggestion would be that the individual CSS properties are the wrong props
to be passing, though, because you can define your own more advanced logic:
const Block = styled.div`
${ ({absolute}) => absolute && `
position: absolute;
${ absolute.split('-').map(key => `${key}: 0;`).join('') }
` }
${ ({padding}) => typeof padding === "string" && `padding: ${padding};` }
${ ({padding}) => Array.isArray(padding) && `padding: ${padding.map(p => `${p}px`).join(' ')};` }
`
<Block absolute="top-right" padding={[48, 24, 24, 48]}/>
<Block absolute="left-top-right-bottom" padding="1rem"/>
Or whatever makes sense for your app. Maybe an AbsoluteBlock
takes different props to a StaticBlock
or something. But if you find yourself wanting to pass raw CSS values to components, you may as well use inline styles.
Thanks for your detailed feedback! I certainly understand not wanting to complicate things with alternative public APIs.
I was partly feeling around for if you thought there might be obvious performance issues in regular use of something like <Block />
which takes (at least some) styles as props. But it seems it would be at least worth trying.
Most helpful comment
Let my put a note that you already can use objects in interpolations directly without mapping. So, minimal API implementation is simple enough to keep in application code:
Speaking of unitless props
<Block width={300} />
it was discussed in https://github.com/styled-components/styled-components/pull/317