PostCSS syntax for CSS-in-JS like styled-components
79
stars
76
commits
JavaScript
primary language
Jun 23, 2026
updated
PostCSS syntax for template literals CSS-in-JS (e. g. styled-components, Emotion). It was built to be used as Stylelint custom syntax or with PostCSS plugins.
Syntax supports:
styled.foo``styled(Component)``styled.foo.attrs({})``styled(Component).attrs({})``styled.foo(props => ``)styled(Component)(props => ``)css``createGlobalStyle``let Component = styled.p`
color: #bada55;
`;
npm install --save-dev postcss-styled-syntax
Install syntax and add to a Stylelint config:
{
"customSyntax": "postcss-styled-syntax"
}
Stylelint custom syntax documentation.
Install syntax and add to a PostCSS config:
export default {
syntax: 'postcss-styled-syntax',
plugins: [ /* ... */ ],
};
An example assumes using PostCSS CLI or another PostCSS runner with config support.
Syntax parser JavaScript/TypeScript code and find all supported components and functions (e.g., css``). Then, it goes over them and builds a PostCSS AST, where all found components become Root nodes inside the Document node.
All interpolations within the found component CSS end up in the AST. E. g. for a declaration color: ${brand} Decl node will look like this:
Decl {
prop: 'color',
value: '${brand}',
}
When interpolation is not part of any node, it goes to the next node's raws.before. For example, for the following code:
let Component = styled.p`
${textStyles}
color: red;
`;
AST will look like:
Decl {
prop: 'color',
value: 'red',
raws: {
before: '\n\t${textStyles}\n\n\t',
// ...
}
}
If there is no next node after interpolation, it will go to parents raws.after. For example, for the following code:
let Component = styled.p`
color: red;
${textStyles}
`;
AST will look like:
Root {
nodes: [
Decl {
prop: 'color',
value: 'red',
},
],
raws: {
after: '\n\n\t${textStyles}\n'
// ...
},
}
Mostly, it works just as the default PostCSS stringifyer. The main difference is the css helper in interpolations within a styled component code. E. g. situations like this:
let Component = styled.p`
${(props) =>
props.isPrimary
? css`
background: green;
`
: css`
border: 1px solid blue;
`}
color: red;
`;
css helper inside an interpolation within Component code.
During parsing, the whole interpolation (${(props) ... }) is added as raws.before to color: red node. And it should not be modified. Each css helper remembers their original content (as a string).
When stringifyer reaches a node's raws.before, it checks if it has interpolations with css helpers. If yes, it searches for the original content of css helper and replaces it with a stringified css helper. This way, changes to the AST of the css helper will be stringified.
Double-slash comments (//) will result in a parsing error. Use standard CSS comments instead (/* */). It is definitely possible to add support for double-slash comments, but let's use standard CSS as much as possible
Source maps won't work or cannot be trusted. I did not disable them on purpose. But did not test them at all. Because of the way we need to handle css helpers within a styled component, source.end positions on a node might change if css AST changes. See the “How it works” section on stringifying for more info.
PostCSS for tokenizer, parser, stringifier, and tests for them.
Prettier for styled-components detection function in an ESTree AST.
JavaScript
100.0%
PostCSS syntax for CSS-in-JS like styled-components
79
stars
76
commits
JavaScript
primary language
Jun 23, 2026
updated
PostCSS syntax for template literals CSS-in-JS (e. g. styled-components, Emotion). It was built to be used as Stylelint custom syntax or with PostCSS plugins.
Syntax supports:
styled.foo``styled(Component)``styled.foo.attrs({})``styled(Component).attrs({})``styled.foo(props => ``)styled(Component)(props => ``)css``createGlobalStyle``let Component = styled.p`
color: #bada55;
`;
npm install --save-dev postcss-styled-syntax
Install syntax and add to a Stylelint config:
{
"customSyntax": "postcss-styled-syntax"
}
Stylelint custom syntax documentation.
Install syntax and add to a PostCSS config:
export default {
syntax: 'postcss-styled-syntax',
plugins: [ /* ... */ ],
};
An example assumes using PostCSS CLI or another PostCSS runner with config support.
Syntax parser JavaScript/TypeScript code and find all supported components and functions (e.g., css``). Then, it goes over them and builds a PostCSS AST, where all found components become Root nodes inside the Document node.
All interpolations within the found component CSS end up in the AST. E. g. for a declaration color: ${brand} Decl node will look like this:
Decl {
prop: 'color',
value: '${brand}',
}
When interpolation is not part of any node, it goes to the next node's raws.before. For example, for the following code:
let Component = styled.p`
${textStyles}
color: red;
`;
AST will look like:
Decl {
prop: 'color',
value: 'red',
raws: {
before: '\n\t${textStyles}\n\n\t',
// ...
}
}
If there is no next node after interpolation, it will go to parents raws.after. For example, for the following code:
let Component = styled.p`
color: red;
${textStyles}
`;
AST will look like:
Root {
nodes: [
Decl {
prop: 'color',
value: 'red',
},
],
raws: {
after: '\n\n\t${textStyles}\n'
// ...
},
}
Mostly, it works just as the default PostCSS stringifyer. The main difference is the css helper in interpolations within a styled component code. E. g. situations like this:
let Component = styled.p`
${(props) =>
props.isPrimary
? css`
background: green;
`
: css`
border: 1px solid blue;
`}
color: red;
`;
css helper inside an interpolation within Component code.
During parsing, the whole interpolation (${(props) ... }) is added as raws.before to color: red node. And it should not be modified. Each css helper remembers their original content (as a string).
When stringifyer reaches a node's raws.before, it checks if it has interpolations with css helpers. If yes, it searches for the original content of css helper and replaces it with a stringified css helper. This way, changes to the AST of the css helper will be stringified.
Double-slash comments (//) will result in a parsing error. Use standard CSS comments instead (/* */). It is definitely possible to add support for double-slash comments, but let's use standard CSS as much as possible
Source maps won't work or cannot be trusted. I did not disable them on purpose. But did not test them at all. Because of the way we need to handle css helpers within a styled component, source.end positions on a node might change if css AST changes. See the “How it works” section on stringifying for more info.
PostCSS for tokenizer, parser, stringifier, and tests for them.
Prettier for styled-components detection function in an ESTree AST.
JavaScript
100.0%