stylething
v2.7.0
Published
Style aware components with BSS and Javascript
Downloads
21
Maintainers
Readme
A Style Thing
Style aware components on top of Better Style Sheets.
npm i stylething
Stylething is a css-in-js utility for creating ~~React~~ Javascript components that have styles attached to them. Stylething works in tandem with the BSS library and is inspired by styled-components and @emotion/styled.
As with styled-components and @emotion/styled, Stylething allows users to combine visual primitives into component systems by just defining component styles.
Unlike styled-components and emotion, Stylething is geared to also work well with virtual DOM libraries other than React.
Table of Contents
Usage
Installation
Sylething was designed to work in concert with BSS (css-in-js-layer) and a virtual DOM library of your choosing (currently adapters exist for React, Preact and Mithril). Install BSS, Stylething and e. g. Mithril via npm
:
npm i mithril bss stylething
Setup
First, import the Vdom library and BSS.
Then create a styled
component factory.
import m from 'mithril'
import b from 'bss'
import { createComponentFactory } from 'stylething'
// Set up styled component factory
const styled = createComponentFactory(b, { m, outputType: 'class' })
// create a component
const PlainButton = styled('button')
Stylething ships without dependencies. Hence passing the BSS
and Mithril
instance into createComponentFactory
is required to instrument the styled
function.
Style aware components
With PlainButton
we now have a style aware Mithril component.
Style aware means that the component recognises any css style attribute passed as a prop.
// rendering PlainButton with ad hoc style overrides
m.mount(document.body,
{ view: () =>
m(PlainButton,
{ background: 'transparent',
borderRadius: '3px',
border: '2px solid palevioletred',
color: 'palevioletred',
margin: '0 1em',
padding: '0.25em 1em',
onclick: () => alert('Hello Pinky!') },
'Click Me'
)
}
)
A live example is available here.
Static defaults
Ad hoc style overrides as demonstrated above, shall come in handy when used in tandem with static style definition at component initialisation.
Static component styles may be declared by passing a style definition as the 2nd argument of the styled
factory.
// pass a fancy button style definition (example uses bss),
// note bss shorthand style attributes!
const FancyButton = styled('button', b`
bc grey
c white
p 12 24
fs 16
tt uppercase
border none
br 3
min-width 120
cursor pointer
transition 0.3 transform, 0.3 opacity
bs 0 1 2 rgba(0,0,0,.35)
-webkit-font-smoothing: antialiased;
`.$hover`
transform translateY(-1px)
filter brightness(130%)
bs 0 1 5 rgba(0,0,0,.35)
`.$active`
filter brightness(100%)
transform translateY(0)
filter
bs 0 1 0 rgba(0,0,0,.35)
`)
const tealOrTomato = Math.random() > 0.5 ? 'teal' : 'tomato'
// rendering FancyButton, overriding just backgroundColor
m.mount(document.body,
{ view: () =>
m(FancyButton,
{ backgroundColor: tealOrTomato,
onclick: () => alert('Hello Fancy!') },
'Click Me'
)
}
)
A live example is available here.
The Stylething API is flexible. Any of the following static style definition flavours (or combinations thereof) are supported:
1) BSS style definition (recommended)
BSS is the recommended way for defining styles. Refer to the BSS documentation for more details.
const Box = styled('div', b`padding 1em`)
2) Hyperscript queries
const Span = styled('span#identifyer.one.two[hot=true][hyper=coool]') // <span id="identifyer" class="one two" hot=true hyper="cool"></span>
3) Externally defined css classes
It is possible to reference external css by passing a class string as the second argument
const Box = styled('div', 'externally defined class') // <div class="externally defined class"></div>
4) POJO style definition
const Box = styled('div', { padding: '1em' })
Inherit and extend
It is possible to extend the static style definition of a previously defined component by passing said component into the styled
factory as the first argument.
// The Button from the last section without the interpolations
const StandardButton = styled('button', b`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`)
// A new component based on StandardButton, but with some override styles
const TomatoButton = styled(Button, b`
color: tomato;
border-color: tomato;
`)
Stylething supports the "as" polymorphic prop. This allows users to dynamically swap out the element that will receive the styles down the line.
m.mount(document.body, {
view: function () {
return m('div', [
m(StandardButton,
{ onclick: () => alert('Hello Standard!') },
'Click Me'),
m(TomatoButton,
{ onclick: () => alert('Hello Tomato!') },
'Me too!'),
// render `StandardButton` as a linl (`<a></a>`)
m(StandardButton,
{ as: 'a',
fontFamily: 'sans-serif',
href:'#',
onclick: () => alert('Hello Standard as Link!') },
'As link!')
])
}
})
A live example is available here
Responsive Styles and Theming
For handling responsive styles, it is recommended to define media queries directly on the BSS instance.
import m from 'mithril'
import b from 'bss'
import { createComponentFactory } from 'stylething'
// use BSS media query groupers
b.helper('notSmall', style => b.$media('screen and (min-width: 30em)', style))
b.helper('large', style => b.$media('screen and (min-width: 60em)', style))
// set up styled component factory
const styled = createComponentFactory(b, { m })
// create a responsively styled static component
const Responsive = styled('button', b`
border: 2px solid palevioletred;
border-radius: 3px;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
`.notSmall`
border: 2px dotted tomato;
border-radius: 5px;
color: tomato;
font-size: 20px;
padding: 0.4em 1.25em;
`.large`
border: 2px solid red;
border-radius: 7px;
color: red;
font-size: 24px;
padding: 0.6em 1.4em;
`)
m.mount(document.body,
{ view: () =>
m(Responsive,
{ onclick: () => alert('Hello responsive thing!') },
'Responsively styled with BSS')
})
A live example is available here
Preconfigured media querie groupers for BSS are available in the stylething/bssHelpers
module. The following examples illustrates how to activate Stylething helpers:
import b from 'bss'
import { createBssHelpers } from 'stylething/bssHelpers.esm'
// initialising Stylething helpers on the BSS instance
b.helper(createBssHelpers(b))
Initialize Stylething helpers by passing the return value of the createBssHelpers
function into the helper
method of the b
instance.
[TODO] link to list of available helpers
CSS systems
The styled
component factory was designed to also work in tandem with style yielding functions provided by libraries such as Systemthing and styled-system. This represents an alternative (albeit slightly more expensive) approach to working with themed values and responsive styles.
Tapping style yielding functions is useful in situations where responsive ad hoc style overwrites can not be avoided.
Style yielding functions are available via npm
.
npm i systemthing
The styled
component factory can then be overloaded with n-number of these functions.
import { borderRadius, borders, color, fontSize, space } from 'systemthing'
// create a dynamic responsively styled component
const Responsive = styled('button', borderRadius, borders, color, fontSize, space)
m.mount(document.body,
{ view: () =>
m('div',
m(Responsive, {
// hardcoded border def across multiple breakpoints
border: [ '2px solid palevioletred', '2px dotted tomato', '2px solid red' ],
// hardcoded border radii across multiple breakpoints
borderRadius: [ '3px', '5px', '7px' ],
// hardcoded color across multiple breakpoints
color: [ 'palevioletred', 'tomato', 'red' ],
// fontSize values on the default typographic scale across multiple breakpoints
fontSize: [ 1, 2, 3 ],
// hardcoded padding across multiple breakpoints
padding: [ '0.25em 1em', '0.4em 1.25em', '0.6em 1.4em' ],
// hard coded non responsive margins
margin: '1em',
// component event handler
onclick: () => console.log('works')
}, 'Very responsive')
)
}
)
A live example is available here.
Custom theme
Systemthing's default values can be customised when initially setting up the styled
factory. Here is an idiomatic example that passes a custom theme into Stylethings createComponentFactory
function.
import m from 'mithril'
import b from 'bss'
import { borderRadius, borders, color, fontSize, space } from 'systemthing'
import { createComponentFactory } from 'stylething'
const newTheme = {
breakpoints: [ '32em', '48em', '64em' ],
space: [ 0, 6, 12, 18, 24 ],
fontSizes: [ 12, 16, 18, 24, 36, 72 ],
radii: [ 3, 5, 7],
colors: {
blue: '#07c',
green: '#1c0',
gray: ['#ccc', '#555']
}
}
// aliases
newTheme.space.big = 64
newTheme.fontSizes.big = 128
const styled = createComponentFactory(b, {
m,
outputType: 'class',
theme: newTheme
})
const Button = styled('button', b`
bc grey
c white
p 12 24
fs 16
tt uppercase
border none
br 3
min-width 120
cursor pointer
transition 0.3 transform, 0.3 opacity
bs 0 1 2 rgba(0,0,0,.35)
-webkit-font-smoothing: antialiased;
`.$hover`
transform translateY(-1px)
filter brightness(130%)
bs 0 1 5 rgba(0,0,0,.35)
`.$active`
filter brightness(100%)
transform translateY(0)
filter
bs 0 1 0 rgba(0,0,0,.35)
`, borderRadius, borders, color, fontSize, space)
m.mount(document.body,
{ view: () =>
m('div',
m(Button, {
// hardcoded border def across multiple breakpoints
border: [ '2px solid palevioletred', '2px dotted tomato', '2px solid red' ],
// themed border radii (theme.radii) across multiple breakpoints
borderRadius: [ 0, 1, 2 ],
// hardcoded color across multiple breakpoints
color: [ 'blue', 'green', 'gray.0' ],
// fontSize values on custom typographic scale (theme.fontSizes) across multiple breakpoints
fontSize: [ 1, 2, 3 ],
// themed vertical and horizontal padding (theme.space) across multiple breakpoints
py: [ 1, 2, 3 ],
px: [ '1em', '1.25em', '1.4em' ],
// hard coded non responsive margins
margin: '1em',
// component event handler
onclick: () => console.log('Hello Fancy Responsive')
}, 'Resoposively themed')
)
}
)
Live example is availabe here
An example theme with preset values can be obtained from the the stylething/theme
module.
import * as dfaultTheme from 'stylething/theme.esm'
The default theme exposes the following values
/* lib/theme.js */
// docs/style-cookbook/media-queries.md
export const breakpoints = [ '30em', '60em' ]
// docs/style-cookbook/layout/spacing.md
export const space = [ 0, '.25rem', '.5rem', '1rem', '2rem', '4rem', '8rem', '16rem' ]
// docs/style-cookbook/typography/type-scale.md
export const fontSizes = [ '.75rem', '.875rem', '1rem', '1.25rem', '1.5rem', '2.25rem', '3rem', '5rem', '6rem' ]
fontSizes.subheadline = fontSizes[ 7 ]
fontSizes.headline = fontSizes[ 8 ]
// docs/style-cookbook/themed/border-radii.md
export const radii = [ '0', '.125rem', '.25rem', '.5rem', '1rem' ]
radii.pill = '9999px'
radii.max = '100%'
// docs/style-cookbook/themed/colors.md
export const colors = {
// Grayscale Solids
black: '#000',
nearBlack: '#111',
darkGray: '#333',
midGray: '#555',
gray: '#777',
silver: '#999',
lightSilver: '#aaa',
moonGray: '#ccc',
lightGray: '#eee',
nearWhite: '#f4f4f4',
white: '#fff',
// Grayscale Transparencies
transparent: 'transparent',
black90: 'rgba(0,0,0,.9)',
black80: 'rgba(0,0,0,.8)',
black70: 'rgba(0,0,0,.7)',
black60: 'rgba(0,0,0,.6)',
black50: 'rgba(0,0,0,.5)',
black40: 'rgba(0,0,0,.4)',
black30: 'rgba(0,0,0,.3)',
black20: 'rgba(0,0,0,.2)',
black10: 'rgba(0,0,0,.1)',
black05: 'rgba(0,0,0,.05)',
black025: 'rgba(0,0,0,.025)',
black0125: 'rgba(0,0,0,.0125)',
white90: 'rgba(255,255,255,.9)',
white80: 'rgba(255,255,255,.8)',
white70: 'rgba(255,255,255,.7)',
white60: 'rgba(255,255,255,.6)',
white50: 'rgba(255,255,255,.5)',
white40: 'rgba(255,255,255,.4)',
white30: 'rgba(255,255,255,.3)',
white20: 'rgba(255,255,255,.2)',
white10: 'rgba(255,255,255,.1)',
white05: 'rgba(255,255,255,.05)',
white025: 'rgba(255,255,255,.025)',
white0125: 'rgba(255,255,255,.0125)',
// Colors
darkRed: '#e7040f',
red: '#ff4136',
lightRed: '#ff725c',
orange: '#ff6300',
gold: '#ffb700',
yellow: '#ffd700',
lightYellow: '#fbf1a9',
purple: '#5e2ca5',
lightPurple: '#a463f2',
darkPink: '#d5008f',
hotPink: '#ff41b4',
pink: '#ff80cc',
lightPink: '#ffa3d7',
darkGreen: '#137752',
green: '#19a974',
lightGreen: '#9eebcf',
navy: '#001b44',
darkBlue: '#00449e',
blue: '#357edd',
lightBlue: '#96ccff',
lightestBlue: '#cdecff',
washedBlue: '#f6fffe',
washedGreen: '#e8fdf5',
washedYellow: '#fffceb',
washedRed: '#ffdfdf'
}
Documentation
- todo