I didn’t notice that the back quote’s meaning in the styled-components until today! I have been using styled-components for a long time, and I thought it was Babel somehow to transform the code segment. Today, I finally read that article that explained the magic behind styled-components when I was going through the document online.
No more nonsense, let’s get into our gist.
This is a common pattern of styled-components.
const Button = styled.button`
background-color: papayawhip;
border-radius: 3px;
color: palevioletred;
`
Template literals are enclosed by the back-tick (` `) character instead of double or single quotes. Template literals can contain placeholders. These are indicated by the dollar sign and curly braces (${expression}).
let name = 'Tom';
console.log(`This is ${name}.`);
//output:
//This is Tom.
A more advanced form of template literals are tagged templates. Tags allow you to parse template literals with a function.
The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions.
let func = (strs, ...args) => {console.log(strs);console.log(args);};
func`This is ${name}. How are you?`
// output :
[ 'This is ', '. How are you?' ]
[ 'Tom' ]
But if we call this function as a normal way,
func(`This is ${name}. How are you?`);
// output :
["This is Tom. How are you?"]
[]
So we only get the first parameter as a string.
How the magic happens?
Let’s craft a function.
let exec = (...args) => args[1](args[0][0]);
let f = str => console.log(str);
exec`hello${f}`;
//output:
//hello
please note the first parameter is an Array!
Do you get it?
We can pass a function as a template string into the tagged expression.
This is called Tagged Template Literals.
In the end,
const Button = styled.button`
font-size: ${props => props.primary ? '2em' : '1em'};
`
will render:
// font-size: 2em;
<Button primary />
// font-size: 1em;
<Button />
Let’s a more complex example.
const sizes = {
desktop: 992,
tablet: 768,
phone: 576
}
// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {
acc[label] = (...args) => css`
@media (max-width: ${sizes[label] / 16}em) {
${css(...args)}
}
`
return acc
}, {})
const Content = styled.div`
height: 3em;
width: 3em;
background: papayawhip;
/* Now we have our methods on media and can use them instead of raw queries */
${media.desktop`background: dodgerblue;`}
${media.tablet`background: mediumseagreen;`}
${media.phone`background: palevioletred;`}
`;
render(
<Content />
);
Let’s translate it line by line.
//the variable size would become this:
const sizesEquivalent = {
desktop: (...args) => css`
@media (max-width: 62em) {
${css(...args)}
}
`
,
tablet: (...args) => css`
@media (max-width: 48em) {
${css(...args)}
}
`
,
phone: (...args) => css`
@media (max-width: 36em) {
${css(...args)}
}
`
}
And what the hell is css function?
A helper function to generate CSS from atemplate literal with interpolations.
You need to use this if you return a template literal with interpolations inside an interpolation.
It returns an array of interpolations,
which is a flattened data structure that you can pass as an interpolation itself.
console.log(css`color: green;`)
console.log(css`color: ${props => props.color};`)
//["color: green;"]
//["color: ", props => props.color, ";"]
As you can see, if you pass a plain string as a parameter into the function, css just return the plain string in an Array. If you do like tagged template, it will return a array just like we analyzed before.
So, in a conclusion, we would have our component like this:
const ContentEquvalent = styled.div`
height: 3em;
width: 3em;
background: papayawhip;
@media (max-width: 62em) {
background: dodgerblue;
}
@media (max-width: 48em) {
background: mediumseagreen;
}
@media (max-width: 36em) {
background: palevioletred;
}
`;