Built around Hyperapp, Hyperapp Style let you inline style all your components without hassle, with all the power of CSS! When you use Hyperapp, you can use the style
attribute out of the box, to inline style your elements. They are added in the state
attribute in the generated HTML. Not anymore! When using Hyperapp Style, all the style you write in your HTML is directly compiled into CSS classes, and pushed directly to the DOM using CSS Object Model (or CSSOM)!
import { app } from 'hyperapp'
import h from 'hyperapp-style'
const state = {}
const actions = {}
const view = (state, actions) => (
<div>
<Navbar/>
<Body/>
</div>
)
const Navbar = () => (state, actions) => (
<nav style={{ display: 'flex' }}>
<img src='/cool-logo.png'/>
<a href='#'>Home</a>
</nav>
)
const Body = () => (state, actions) => (
<div style={{ backgroundColor: 'blue' }}>
<h1>I'm an incredible example!</h1>
</div>
)
app(state, actions, view, document.getElementById('app'))
In this example, your div in Body will have a background-color-blue
class linked in the class attribute, and the corresponding atomic class will be put into your browser, directly into a style node into the head
section of the HTML page.
Describing the style for an element is easy: just use classic CSS, but camelized. background-color
becomes backgroundColor
, font-size
becomes fontSize
, etc. The CSS key is the object key, and you can just write as string or int the value you want.
Every key you enter in the style object which does not have a value associated (i.e. which have an object) will be considered as pseudo-class entry. Let's say you want to have a different behavior when you hover an element. You can't use a :hover
class when you're using an inline style. But with Hyperapp Style, you can. An example.
import { app } from 'hyperapp'
import h from 'hyperapp-style'
const state = {}
const actions = {}
const view = (state, actions) => (
<div>
<Navbar/>
<Body/>
</div>
)
const Navbar = () => (state, actions) => (
<nav style={{ display: 'flex' }}>
<img src='/cool-logo.png'/>
<a href='#'>Home</a>
</nav>
)
// We want this component to be blue, and red when hovered.
const Body = () => (state, actions) => (
<div style={{
backgroundColor: 'blue',
hover: {
backgroundColor: 'red'
}
}}>
<h1>I'm an incredible example!</h1>
</div>
)
app(state, actions, view, document.getElementById('app'))
When using the hover
key in the style attribute, and setting an object as value, all CSS inside the object will be considered as pseudo-classed! And this is working with everything you want: hover, active, inactive, invalid, valid, etc.
There is three special cases in the style object: widths
(and not width
, which is regular CSS, be careful), screen
and mobile
. They are special keys used to generate media queries. The screen
and mobile
key are regular keys, like pseudo-classes, but generates media queries. (Under the hood, it computes the atomic class and surround the class with a @media screen { the-rule }
.)
widths
is a little bit different: it accepts an array of objects as value, and each object is a regular style object as usual, but in which you can add a max
and a min
keys, which correspond to the max-width
and min-width
in the media queries! Let's illustrate this.
import { app } from 'hyperapp'
import h from 'hyperapp-style'
const state = {}
const actions = {}
const view = (state, actions) => (
<div>
<Navbar/>
<Body/>
</div>
)
const Navbar = () => (state, actions) => (
<nav style={{ display: 'flex' }}>
<img src='/cool-logo.png'/>
<a href='#'>Home</a>
</nav>
)
// This component is green on small screens, yellow on medium, and
// red on huge screens. Text is also yellow on small screen and cyan
// on small mobile.
const Body = () => (state, actions) => (
<div style={{
widths: [{
max: '450px',
backgroundColor: 'green',
screen: { color: 'yellow' },
mobile: { color: 'cyan' }
}, {
min: '450px',
max: '750px',
backgroundColor: 'yellow'
}, {
min: '750px',
backgroundColor: 'red'
}],
screen: {
fontSize: '12px'
},
mobile: {
fontSize: '16px'
}
}}>
<h1>I'm an incredible example!</h1>
</div>
)
app(state, actions, view, document.getElementById('app'))
To be precise on media queries, they are applied in order of importance, accordingly to the style object, from top to bottom. The last style applied on element will be the last style in the array.
When you're using JSX, you can use lowercased words, like div
, and capitalized words, like NavBar
in your tags. According to the case, it will not search and render things in the same way. In the first case, it will generate a corresponding HTML element. In the other case, it will render what you want by calling the corresponding function. In this case, you maybe want to access the style
property in the function? Well, you can! Just use props.style
and you're good!
An example to help you understand.
const Flex = (props, children) => (
<div style={{ ...props.style, display: 'flex' }}>{children}</div>
)
const App = (state, actions) => (
<Flex style={{ padding: '12px' }}
Here's my content!
</Flex>
)
In this case, the styles are not computed right now, and you're free to use them in your functions to combine them, or whatever you want!
If you went through all of this, you're ready to use Hyperapp Style in all your Hyperapp apps!
I love to hear from you if you're using this package! Please, let me know if you encounter bugs or problem, and feel free to submit PR if you think you got something I didn't thought about!