@deskpro/react-stylesheet

React components with predefined styles


Keywords
css, react, style, css-in-js, react-components
License
MIT
Install
npm install @deskpro/react-stylesheet@1.0.3

Documentation

React Stylesheet

Build Status NPM Version

Component based styling approach for React applications.

Table of Contents

Motivation

This library implements a components-based approach for styling React applications. Stying with components means styling with JS code.

To re-iterate on CSS-in-JS advantages:

  • Single language to define UI and to style it—JavaScript.

  • Existing tooling for JavaScript can be reused for stylesheets: linters, type checkers, formatters, ...

  • A lot of features CSS is missing are present in JavaScript: modules, functions, variables, ...

For more info on CSS-in-JS and its advantages see the excellent talk by Vjeux.

What makes React Stylesheet special:

  • React centric approach: there's no separate abstractions for styles, React Stylesheet produces React components directly. You don't need to pass className or style props around. The units of reusability are React components.

  • Type safety: React Stylesheet is fully typesafe. That can help you catch typos and invalid style values.

  • React Stylesheet compiles to CSS classes under the hood: that means hover, focus states are supported.

Installation

% npm install react-stylesheet

Usage

<Element />

<Element /> component is a basic building block for styling:

import {Element} from 'react-stylesheet'

<Element
  background="red"
  color="yellow"
  padding={10}>
  I'm styled!
</Element>

Styling based on state (hover, focus, ...)

For each prop like color, background, ... there are versions with suffixes *OnHover, *onActive, *onActive, and *onDisabled which activate its style values when the corresponding state is being active.

For example there's an <Element /> which changes its background and text color on hover:

import {Element} from 'react-stylesheet'

<Element
  background="red"
  backgroundOnHover="yellow"
  color="yellow"
  colorOnHover="red"
  padding={10}>
  I'm styled!
</Element>

Overriding component

By default <Element /> renders into <div /> DOM component but you can override this with Component prop:

<Element
  Component="button"
  padding={10}>
  click me!
</Element>

It can be a composite component but the requirement is that it takes style and className props.

<VBox /> and <HBox />

<VBox /> and <HBox /> are thin wrappers on top of <Element /> which implement flexbox layout mechanism.

<VBox /> corresponds to a flex container with flex-direction: column and <HBox />flex-direction: row.

All properties which are supported by <Element /> are also supported by <VBox /> and <HBox />.

import {VBox, HBox} from 'react-stylesheet'

<VBox justifyContent="space-around">
  <HBox flexGrow={1}>Block 1</HBox>
  <HBox>Block 2</HBox>
</VBox>

Note that the following defaults are applied:

HBox, VBox {
  position: relative;

  overflow: hidden;

  margin: 0;
  padding: 0;

  display: flex;
  align-items: stretch;
  flex-basis: auto;
  flex-shrink: 0;

  min-height: 0;
  min-width: 0;
}

Styled component factories

There's a way to produce styled components out of common components using style(Component, stylesheet) function:

import {style} from 'react-stylesheet'

let Label = style('span', {
  base: {
    fontWeight: 'bold',
    fontSize: '12pt',
  }
})

Now Label is a regular React component styled with fontWeight and fontSize. You can render into DOM and use as a part of React element tree:

<Label />

Styling based on state (hover, focus, ...)

You can specify styling for states (hover, focus, ...):

let Label = style('span', {
  base: {
    fontWeight: 'bold',
    fontSize: '12pt',
    hover: {
      textDecoration: 'underline'
    }
  }
})

Now on hover you can see the underline appears.

Variants

Sometimes you want a set of style variants and toggle them via JS:

let Label = style('span', {
  base: {
    fontWeight: 'bold',
    fontSize: '12pt',
  },
  emphasis: {
    textDecoration: 'underline'
  },
})

Now to toggle any particular variant you need to pass a component a specially constructed variant prop:

<Label variant={{emphasis: true}} />

Type safety

React DOM Stylesheet comes with Flow typings which precisely describe available API.

Some examples of the type errors you can get:

style('span', {
  display: 'oops' // display can only be "none" | "block" | ...
})

style('span', {
  isplay: 'block' // unknown property "isplay"
})

CSS helpers

There's helpers for producing CSS values:

import {css} from 'react-stylesheet'

let Label = style('span', {
  base: {
    border: css.border(1, css.rgb(167)),
  }
})

Test utilities

React Stylesheet comes with snapshot serializers for Jest test framework.

The example test setup looks like this:

import React from 'react';
import renderer from 'react-test-renderer';

import {Element} from 'react-stylesheet';
import * as TestUtils from 'react-stylesheet/testutils';

expect.addSnapshotSerializer(TestUtils.snapshotSerializer);

function Hello() {
  return <Element color="red" colorOnHover="black">HEllo!</Element>
}

test('rendering <Hello />', function() {
  const tree = renderer.create(<Hello />).toJSON();
  expect(tree).toMatchSnapshot();
});

Which produces the following snapshot:

<div
  className={
    StyleJoin [
      PrecompiledCSS {
        "boxSizing": "border-box",
      },
      DynamicallyGeneratedCSS {
        "hover": Object {
          "color": "black",
        },
      },
    ]
  }
  style={Object {
    "hover": "red",
  }}
>
  Hello
</div>

Alternatively if you don't want to call expect.addSnapshotSerializer(..) line in each of your test files you can the following config to your package.json:

"jest": {
  "snapshotSerializers": ["react-stylesheet/testutils-snapshot-serializer"]
}

This will enable snapshot serializers for each of your test files.