@haijindev/o-css
Javascript css builder
Documentation
http://o-programming-language.org/
@haijindev/o-css
do?
What does Historically the workflow of the css styles of an application consisted in
- define or read a pseudo-css input file on the server side
- process it to generate a standard css file that a browser understands. For example replace variables by values, mix css classes o compile nested selectors
- expect the client to request for the compiled css file
- respond the css file to the client
Frameworks like React
can dramatically change the workflow with a more dynamic, light and adaptable one.
This library provides a javascript DSL to declare css styles and implement the workflow that follows
- declare the styles the app uses in regular javascript sintax
- send to the client the declared styles as a regular js module in your
React
application - make the client application to process the declared styles and convert them to the css the browser understands
- load the css compiled client side in the browser
It is also possible to use this DSL to compile the css server side using regular function calls in Node.
Installation
npm install @haijindev/o-css
Usage
First define one or more styles to load.
Each style is a regular js class with a method call injectInto(css)
class AppStyle {
injectInto(css) {
// styles definition goes here
}
}
The parameter css
is the css compiler and supports the following protocol
class AppStyle {
injectInto(css) {
// Define the given cssSelector
css.selector(
'.row', {
// ... css attributes
}
)
// Define the given styles as children of the given cssSelector
css.namespace('#container', () => {
// child styles
})
// Append the styles defined in the given styleClass
css.inject(anotherStyleClass)
// Merge the styles defined in the given styleClass in the given cssSelector
css.selector(
'.row', {
_import: anotherStyleClass,
// ...
}
)
// Merge the styles defined in the given styleClasses in the given cssSelector
css.selector(
'.row', {
_import: [anotherStyleClass1, anotherStyleClass2],
// ...
}
)
}
}
Once you have a style object the application must load it into the DOM document using any of the following methods
const {Css} = require('@haijindev/o-css')
const AppStyle = require('./AppStyle')
const style = new AppStyle()
Css.insertStyle({ style, document, element: document.head })
// And load the app like usual
ReactDOM.render(
<MainComponent />,
document.getElementById('root')
)
or first compile the styles to a css string and then load the string
const {Css} = require('@haijindev/o-css')
const AppStyle = require('./AppStyle')
const cssString = Css.fromStyle(style)
Css.insertCss({ cssString, document, element: document.head })
// And load the app like usual
ReactDOM.render(
<MainComponent app={ app } />,
document.getElementById('root')
)
The styles can also be compiled to a file server side with
const {Css} = require('@haijindev/o-css')
const fs = require('fs')
const AppStyle = require('./AppStyle')
const style = new AppStyle()
const cssString = Css.fromStyle(style)
fs.writeFileSync('public/main.css', cssString)
Css definition
Define the class with the css definitions
class AComponentStyle {
injectInto(css) {
}
}
module.exports = AComponentStyle
Then use any of the following definitions
Define attributes to a css selector
class AComponentStyle {
injectInto(css) {
css.selector('.btn', {
'width': '10px',
'height': '10px'
})
}
}
module.exports = AComponentStyle
Define attributes to many css selectors
class AComponentStyle {
injectInto(css) {
css.selector('.btn', {
'width': '10px',
'height': '10px'
})
css.selector('.btn:hover', {
'height': '10px'
})
}
}
module.exports = AComponentStyle
Define nested selectors
class AComponentStyle {
injectInto(css) {
css.selector('.row', {
'width': '10px'
})
css.selector('.row .col', {
'height': '10px'
})
}
}
module.exports = AComponentStyle
Define nested selectors using namespaces
Useful to override selectors for a specific element
class AComponentStyle {
injectInto(css) {
css.namespace('#main', () => {
css.selector('.row', {
'width': '10px'
})
})
}
}
module.exports = AComponentStyle
Import (merge) attributes from another Style
Useful to reuse css values in different elements
class CommonStyle {
injectInto(css) {
css.selector('common', {
'color': 'blue'
})
}
}
module.exports = CommonStyle
const CommonStyle = require('./CommonStyle')
class AComponentStyle {
injectInto(css) {
css.selector('.row', {
_import: new CommonStyle(),
'width': '10px'
})
}
}
module.exports = AComponentStyle
Import (merge) attributes from many other Styles
const CommonStyle1 = require('./CommonStyle1')
const CommonStyle2 = require('./CommonStyle2')
class AComponentStyle {
injectInto(css) {
css.selector('.row', {
_import: [new CommonStyle1(), new CommonStyle2()],
'width': '10px'
})
}
}
module.exports = AComponentStyle
Bundle many Styles into a single Style
Useful to export and load a single Style class
class Style1 {
injectInto(css) {
css.selector('.row', {
'width': '10px'
})
}
}
module.exports = Style1
class Style2 {
injectInto(css) {
css.selector('.btn', {
'height': '10px'
})
}
}
module.exports = Style2
const Style1 = require('./Style1')
const Style2 = require('./Style2')
class MainStyle {
injectInto(css) {
new Style1().injectInto(css)
new Style2().injectInto(css)
}
}
module.exports = MainStyle
Dynamic css values
const width = 10
class Style2 {
injectInto(css) {
css.selector('.btn', {
'width': `${width}px`,
'height': `${width/3}px`
})
}
}
module.exports = Style2
Css parametrised values
class Style {
constructor({color}) {
this.color = color
}
injectInto(css) {
css.selector('.btn', {
'width': `${width}px`,
'color': this.color
})
}
}
module.exports = Style
new Style({color: 'blue'})
Themed styles
const theme = {
color: 'blue'
}
module.exports = theme
class Component1 {
constructor(theme) {
this.theme = theme
}
injectInto(css) {
css.selector('#id1', {
'color': this.theme.color
})
}
}
module.exports = Component1
class Component2 {
constructor(theme) {
this.theme = theme
}
injectInto(css) {
css.selector('#id2', {
'color': this.theme.color
})
}
}
module.exports = Component2
const theme = require('./theme')
const Component1 = require('./Component1')
const Component2 = require('./Component2')
class MainStyle {
injectInto(css) {
new Component1(theme).injectInto(css)
new Component2(theme).injectInto(css)
}
}
module.exports = MainStyle
Change the application theme on the fly
const {Css} = require('@haijindev/o-css')
const MainStyle = require('./MainStyle')
function updateStyles(selectedTheme) {
const styleElement = document.querySelector('head style')
if (styleElement) { styleElement.remove() }
const newStyle = new MainStyle(selectedTheme)
Css.insertStyle({newStyle, document, element: document.head })
}
updateStyles(newTheme)