Hey, thanks for this great library!
Right now you can do:
const BigText = styled.div`
font-size: 24px;
color: black;
`
And this makes it nice to package up styles such that others can re-use them. But if folks want to use them in places where they need to use a specific semantic HTML tag, there's currently no way to override the default tag.
Imagine later needing to do:
<article>
<BigTag tagName="h1">My Title</BigTag>
<p>...</p>
</article>
It would be nice if there was an easy built-in way to override the tag so that you could write semantic HTML easily, without having every component provider be required to rewrite their own default-tag-using base component I think.
I'd be open to a different name. tagName or tag might be too DOM-centric for React which isn't DOM-specific, in which case something like component might be better.
Thanks!
@ianstormtaylor Thank you for making the issue!
However, it looks like you can use withComponent to change your tag. Is this what you want to do? If not, please provide more details.
@Ailrun ah, I didn't notice that when reading the docs. Thank you!
That said, it feels a bit like an awkward implementation though since you have to declare any potential different tag names upfront, and you can't do it inline in a render function as easily since you'll be creating new component references each time? I wonder why it's designed that way...
// other file...
const BigText = styled.div`
font-size: 24px;
color: black;
`
// your file...
const BigTextH1 = BigText.withComponent('h1')
const BigTextH2 = BigText.withComponent('h2')
...
render() {
return {
<article>
<BigTextH1>My Title</BigTextH1>
<p>...</p>
<BigTextH2>...</BigTextH2>
</article>
}
}
It seems like it (a) requires you to create extra one-off components with configuration that no longer lives directly next to where you use it, (b) requires you to actually name these components which is an inconvenience, and (c) makes it discouraged to do this one-off in a render function when you happen to need it.
Compared to:
// other file...
const BigText = styled.div`
font-size: 24px;
color: black;
`
// your file...
render() {
return {
<article>
<BigText withComponent="h1">My Title</BigText>
<p>...</p>
<BigText withComponent="h2">...</BigText>
</article>
}
}
In that model you shed all of those downsides, and have much more readable code. (Especially when you consider the definition and usage maybe be separated by hundreds of lines, or even across file names in the first example.)
I definitely appreciate that react-emotion follows styled-components in lots of ways as far as compatibility goes, but sometimes they don't get their API design right, so it might make sense to improve on what they offer instead of just mimic. This case seems like the better API design could be a superset of what they offer.
It seems like it (a) requires you to create extra one-off components with configuration that no longer lives directly next to where you use it, (b) requires you to actually name these components which is an inconvenience, and (c) makes it discouraged to do this one-off in a render function when you happen to need it.
I understand these downsides but find them acceptable when looking at the project as a whole. There are numerous ways to solve this problem and each has their own pros and cons. For context, we weren't exactly thrilled with withComponent but as you mentioned it is there for compatibility.
When I run into this problem I usually just do the following:
const BigTextStyles = css`
font-size: 24px;
color: black;
`
export const H1 = styled('H1')(BigTextStyles, `font-size: 32px`) // <- string or object styles work
export const H2 = styled('h2')(BigTextStyles, { fontSize: 28 })
export const H3 = styled('h3')(BigTextStyles, { fontSize: 20 })
// ... another file
import { H1, H3 } from './components/BigText'
render() {
return {
<article>
<H1>My Title</H1>
<p>...</p>
<H3>...</H3>
</article>
}
}
Another option is to just create your own wrapped styled function and add the functionality you are wanting.
@tkh44 thanks, that makes sense. Do you have a link to the docs for that extra string/object argument? Does it just take (...styles) such that any are overriding?
Yes. It is essentially a map/reduce function with similar mechanics to Object.assign with the caveat that we don't make any of the override decisions because the browsers are very good at that. The process can be summed up in the following way, process each argument recursively and concatenate their resulting style strings by joining them with ";".
In our first example above for H1, when the argument BigTextStyles is encountered it will have already been evaluated and its value will be a className generated by emotion.
Something like: css-34d45.
When emotion sees this it will just pull the previously evaluated style string result which has already been parsed and expanded by stylis from the cache ("font-size:24px;color:black;").
Next we tack on the next arguments result: "font-size: 32px"
Then we end up with something like "font-size:24px;color:black;font-size: 32px;"
In the stylesheet it will look like
.css-34d45 {font-size:24px;color:black;font-size:32px;}
and the browser knows exactly how to handle that override.
Edit and tl;dr: All of the arguments are passed to styled's internal css call to create the className prop.
@tkh44 awesome, thank you for such a detailed description! That's perfect. I'll close this out.
Most helpful comment
I understand these downsides but find them acceptable when looking at the project as a whole. There are numerous ways to solve this problem and each has their own pros and cons. For context, we weren't exactly thrilled with
withComponentbut as you mentioned it is there for compatibility.When I run into this problem I usually just do the following:
Another option is to just create your own wrapped
styledfunction and add the functionality you are wanting.