postjss
postjss copied to clipboard
Use the power of PostCSS in compiling with JSS
PostJSS
This project allows to use PostCSS features (plugins, syntaxes etc.) for compiling to JSS object via babel, so you can take benefits from both.
- Advantages
- Installation
- Babel Plugin Options
-
How it works?
- As tagged template literal
- As separate styles file
- Custom PostJSS Syntax
- Hot Module Replacement
- Linting
- Rebuild optimization
- Runtime Usage (sync and async)
Advantages
- With PostJSS very easy to start using JSS from SASS/SCSS/PostCSS etc. All you need - update your components and use PostJSS Babel Plugin
- You can continue use your favorite syntax and the power of PostCSS plugins (stylelint, sort-order etc.), but take advantages of CSS in JS - awesome!
- You can write styles in styled-components way, css-modules way or in both - the choice is yours
- You can build your project just with babel
- Static compilation, no runtime overhead!
- You can use any library that is compatible with the JSS-object, not only JSS
Installation
npm i postjss -S
Babel Plugin Options
This plugin uses postcss-load-config, so you need to set PostCSS options in package.json/.postcssrc/postcss.config.js/.postcssrc.js
Plugin Options:
-
extensionsRe
:String
- RegExp for extensions. By default(c|(s[ac]?))ss
- for css, sass, scss, sss -
namespace
:String
- Set your custom namespace for tagged literals. By default itspostjss
-
throwError
:Boolean
- Plugin will throw an error and stop transpiling, if error caused by PostCSS (egstyling
errors). By defaultfalse
.babelrc example:
plugins: [
[
'postjss/babel', {
extensionsRe: 's[ac]?ss',
namespace: 'customPostJSSNamespace',
throwError: false
}
]
]
How it works?
Please check the counter example
As tagged template literal
Babel PostJSS plugin transforms tagged literal into the JSS-object by PostCSS, like:
const styles = postjss`
.${selector}
left: ${() => 0}
margin-${marginType}: 10px
transition: ${'opacity'} 1s
color: ${color}
&::before
content: '😱'
`
After transpile it would look like:
const styles = {
[selector]: {
left: () => 0,
[`margin-${marginType}`]: '10px',
transition: 'opacity 1s',
color: color,
'&::before': {
content: "'😱'"
}
}
}
You are free to use all PostCSS feature and custom postjss syntax
(see bellow)
Notice that if you are using
stylelint
andproperty-no-unknown
rule, you need to set an option like this (it's required for current postjss parser implementation):property-no-unknown: [true, { ignoreProperties: ['/\$\^var__/'] }]
As a separate styles file
After transpiling imported styles inlined into variable (import name) as a function expression, that accepts an object with arguments and returns a JSS object with styles with arguments usage. Notice that arguments name has uniq scope, so you need not worry about names conflict.
Say you have this styles.sss (with SugarSS i.e.):
.lang-list
display: flex
margin: 0
padding: 0
list-style: none
.lang
margin-right: 10px
padding: 5px
&.current
border-bottom: 1px solid red
And a component using it:
import style from './style.sss'
After babel transpiling your component become as:
const styles = function (izexozk) {
izexozk = Object.assign({}, izexozk);
return {
"langList": {
"display": "flex",
"margin": "0",
"padding": "0",
"listStyle": "none"
},
"lang": {
"marginRight": "10px",
"padding": "5px",
"&.current": {
"borderBottom": "1px solid red"
}
}
};
}
And you can use this with JSS like:
injectSheet(style())
Custom PostJSS Syntax
You can use specific syntax for some JSS-features in your CSS:
-
/JS Code/
- you can place JS-block wrapped by/
in every property value
.app
display: /({ visible }) => visible ? 'block' : 'none'/
-
$^variableName
- allows you to use variables passed as arguments to the style function
.app
background-color: /^color || $color/
-
defaults
block for default values - it would be default for accepting args
defaults:
prop: /^prop || 'test'/
selector: ''
-
$^propertyName
- for custom property/selector names
.app
$^prop: 100vw
$^selector:
display: none
Hot Module Replacement
For tagged literals HMR supported out of the box. But for separate styles you need to use postjss hot-loader
with webpack:
Notice, that you need to set this loader after babel-loader
.
use: [
'babel-loader',
'postjss/webpack/hot-loader',
]
Linting
You can use stylelint for linting and postcss-reporter for warnings and errors. So with PostJSS it works like a charm:
For CSS files:
For tagged literals:
Rebuild optimization
Let's say you use some tool for linting. But if you set throwError: true
for postjss
, it will cause a babel transpilling error, so babel will have to build other files next time, not only fixed.
You can set throwError: false
for dev building to avoid this, and use postjss report-loader
for webpack (set this loader before babel-loader
:
use: [
'postjss/webpack/report-loader',
'babel-loader',
]
This will break webpack compiling if there are some errors in PostJSS, but not babel transpiling. And then babel needs to rebuild only fixed file.
Runtime
You can use this module in runtime without babel-plugin.
- it works just on node.js
- you can use async and sync versions, so async is more preferable, because sync is blocking your process
- and it may cause some performance issues
So I recommend this usage only for tests and first steps :)
Sync version:
import postjss from 'postjss'
console.log(postjss`
.app
color: red
`)
// output: { app: { color: 'red' } }
Async version:
import postjssAsync from 'postjss/runtime/async'
;(async () => {
const postjss = await postjssAsync
console.log(await postjss`
.app
color: red
`)
// output: { app: { color: 'red' } }
})()
You may also be interested in this project for runtime usage: jss-from-postcss
Full Example
style.sss
@import 'vars.sss'
@import 'mixins.sss'
$color: 'green'
:root
--color: $color
defaults:
prop: /$^prop || 'test'/
selector: ''
.app
position: absolute
top: 0
left: 0
display: /({ name }) => name + 1 + $color-from-import/
overflow-y: auto
flex-direction: column
width: 100vw
height: 100vh
color: var(--color)
background-color: /$^value || $color/
$^prop: 100vw
$^selector:
display: none
.content
@mixin container
padding: 20px
.header
border-bottom: 1px solid #eee
component.jsx
const style = function (izezix) {
izezix = Object.assign({
"prop": izezix.prop || 'test',
"selector": "''"
}, izezix);
return {
"app": {
"position": "absolute",
"top": "0",
"left": "0",
"display": function ({
name
}) {
return name + 1 + 'black';
},
"overflowY": "auto",
"flexDirection": "column",
"width": "100vw",
"height": "100vh",
"color": "green",
"backgroundColor": izezix.value || 'green',
[izezix.prop]: "100vw"
},
[izezix.selector]: {
"display": "none"
},
"content": {
"position": "absolute",
"margin": "0 auto",
"&::before": {
"content": "''"
},
"padding": "20px"
},
"header": {
"borderBottom": "1px solid #eee"
}
};
}
Links
License
MIT