Minimalist 5th Generation CSS-in-JS built for concision and extension. Fast af, powered by nano-css.
npm i hypostyle
Typical 4th
gen
CSS-in-JS can be very verbose. hypostyle
provides a way of authoring CSS in
Javascript using shortcuts and abbreviations — like atomic CSS and frameworks
like Tailwind — at a fraction of the bundle size.
hypostyle
is also framework-agnostic, meaning it can be adapted into any UI
framework and any CSS-in-JS library that supports style objects.
Full Example
import { hypostyle } from 'hypostyle'
const { css } = hypostyle({
breakpoints: ['400px', '800px', '1200px'],
tokens: {
color: {
primary: '#ff4567',
},
space: [0, 4, 8, 12, 16, 20, 24, 28, 32],
},
shorthands: {
c: ['color'],
px: ['paddingLeft', 'paddingRight'],
},
})
➕
const classname = css({
c: 'primary',
px: [4, 8],
})
function Component() {
return <div className={classname} />
}
👇
color: #ff4567;
padding-left: 16px;
padding-right: 16px;
@media (min-width: 400px) {
padding-left: 32px;
padding-right: 32px;
}
The hypostyle()
function is used to configure a theme and return an instance.
Your theme can contain the following properties:
tokens
- design system tokens, like colors, spacing, etcbreakpoints
- array of breakpoint widths (optional)shorthands
- abbreviated properties that map to one or more CSS properties and (optionally) design tokensmacros
- boolean properties mapped to pre-defined style objectsvariants
- named groups of pre-defined style objectsproperties
- config supported CSS properties
Hypostyle includes optional preset objects with a starter set of tokens, shorthands, and macros. Source code.
import * as presets from 'hypostyle/presets'
const { css } = hypostyle(presets)
All values can be responsive by passing an array of values. This array maps to
the breakpoints
array on your theme. The first value is mobile, and the
remaining values are convered into @media
breakpoints.
css({
d: ['block', 'none'],
})
Will generate:
.__3sxrbm {
display: block;
}
@media (min-width: 400px) {
.__3sxrbm {
display: none;
}
}
Alternatively — and useful if you want to only specify a single breakpoint — you can use object syntax. Just use the indices as object keys:
css({
d: { 1: 'none' },
})
@media (min-width: 400px) {
.__3sxrbm {
display: none;
}
}
Tokens are either objects of named values, or arrays (scales) of values.
const { css } = hypostyle({
tokens: {
color: {
primary: '#ff4557',
},
space: [0, 4, 8, 12, 16],
},
})
Tokens are associated with configurable CSS properties so that you can use token values in your styles. By default, most CSS properties are supported and are self referencing.
Shorthands are like shortcuts that allow you to abbreviate CSS these properties. The rest of the above example could look like this:
const { css } = hypostyle({
tokens: {
color: {
primary: '#ff4557',
},
space: [0, 4, 8, 12, 16],
},
shorthands: {
bg: 'background',
},
})
Which can be used like this, because background
is associated with the color
tokens by default:
css({ bg: 'primary' }) // => { background: '#ff4567' }
macros
are simple boolean values that expand to be full style objects. The
style objects can use any shorthands or tokens you have configured.
const { css } = hypostyle({
macros: {
cover: { top: 0, bottom: 0, left: 0, right: 0 },
},
})
css({ cover: true }) // => { top: 0, bottom: 0, ... }
These are most helpful when used with JSX (via React, hyposcript, or otherwise) i.e.:
<Box cover />
Slightly higher-level than macros are variants, which allow you to define named style blocks based on property values. Again, your style blocks here can use any shorthands and tokens you've configured.
import * as presets from 'hypostyle/presets'
const { css } = hypostyle({
...presets,
variants: {
appearance: {
link: {
c: 'blue',
textDecoration: 'underline',
},
},
},
})
Which look like this when used:
css({ appearance: 'link' }) // => { color: 'blue', textDecoration: 'underline' }
const link = css({
color: 'black',
'&:hover': {
color: 'blue',
},
'.icon': {
transform: 'translateX(5px)',
},
})
Also included alongside css
is injectGlobal
.
import { hypostyle } from 'hypostyle'
import * as presets from 'hypostyle/presets'
const { injectGlobal } = hypostyle(presets)
injectGlobal({
'html, body': {
c: '#333',
boxSizing: 'border-box',
},
})
Also also included alongside css
is keyframes
.
const { keyframes } = hypostyle(presets)
const animation = keyframes({
'0%': {
opacity: 0,
},
'100%': {
opacity: 1,
},
})
css({
animation: `${animation} 1s linear infinite`,
})
Hypostyle comes with built-in support for most CSS properties. To add
additional support, have a look at the properties.js
file for API, and pass
your additional props to the constructor. Example:
const hypo = hypostyle({
...,
tokens: {
...,
radii: ['4px', '8px']
},
shorthands: {
...,
bTLR: 'borderTopLeftRadius'
},
properties: {
borderTopLeftRadius: {
token: 'radii'
}
}
})
Usage:
hypo.style({ bTLR: 1 }) // => { borderTopLeftRadius: '8px' }
hypostyle
is isomorphic!
const { css, flush, injectGlobal } = hypostyle(presets)
injectGlobal({ '*': { boxSizing: 'border-box' } })
const classname = css({ c: 'primary' })
const stylesheet = flush()
const html = `
<!DOCTYPE html>
<html>
<head>
<style>${stylesheet}</style>
</head>
<body>
<div class="${classname}">Hello world</div>
</body>
</html>
`
MIT License © Sure Thing