2.7.0 • Published 5 years ago

stylething v2.7.0

Weekly downloads
2
License
MIT
Repository
github
Last release
5 years ago

A Style Thing

Style aware components on top of Better Style Sheets.

Build Status Version size MIT License

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

Related

MIT License

2.7.0

5 years ago

2.6.0

5 years ago

1.5.0

5 years ago

1.4.0

5 years ago

1.3.0

5 years ago

1.2.0

5 years ago

1.1.0

5 years ago