/typestyle

Making CSS Typesafe

Primary LanguageTypeScriptMIT LicenseMIT

TypeStyle

Making CSS type safe.

Build Status NPM version

Writing CSS with TypeStyle will be just as fluent as writing JavaScript with TypeScript.

There are quite a few css in js frameworks out there. This one is different:

  • Provides great TypeScript developer experience (rad idea!).
  • No custom AST transform or module loader support needed.
  • Works with any framework (react, angular2, cyclejs, whatever, doesn't matter).
  • Zero config. Just use.
  • super small (~1k)

Overview

Quickstart

Use it like you would use CSS modules or CSS in general with webpack etc, but this time you get to use TypeScript / JavaScript!

Install npm install typestyle --save

Use

/** Import */
import {style} from "typestyle";

/** convert a style object to a CSS class name */
const className = style({color: 'red'});

/** Use the class name in a framework of choice */
//  e.g. React
const MyButton =
  ({onClick,children})
    => <button className={className} onClick={onClick}>
        {children}
      </button>
// or Angular2
@Component({
  selector: 'my-component',
  template: `<div class="${className}">Tada</div>`
})
export class MyComponent {}

Server Side

Just get the styles as CSS at any point and render it in a style tag yourself. e.g.

/** Import */
import {style, css} from "typestyle";

/** convert a style object to a CSS class name */
const className = style({color: 'red'});

/** Render to CSS style tag */
const styleTag = `<style>${css()}</style>`
/** ^ send this as a part of your HTML response */

Advanced

Pseudo States &:hover, &:active, &:focus, &:disabled as you would expect e.g.

/** Import */
import {style} from "typestyle";

/** convert a style object to a CSS class name */
const className = style({
  color: 'blue',
  '&:hover': {
    color: 'red'
  }
});

Child selectors &:first-child, &:last-child, &>* etc work too e.g. use it to design a vertical layout:

/** Import */
import {style} from "typestyle";

/** Share constants in TS! */
const spacing = '5px';

/** style -> className :) */
const className = style({
  '&>*': {
    marginTop: spacing,
    marginBottom: spacing
  },
  '&:first-child': {
    marginTop: '0px',
  }
  '&:last-child': {
    marginBottom: '0px',
  }
});

Media Queries

const colorChangingClass = style({
  backgroundColor: 'red',
  '@media (min-width: 400px)': {
    backgroundColor: 'pink'
  }
})

Merge Objects Pass as many style objects to style and they merge just right.

const redMaker = {color:'red'};
const alwaysRedClass = style(redMaker);
const greyOnHoverClass = style(
  redMaker,
  {'&:hover':{color: 'grey'}}
);

Compose Classes You can easily compose class names using classes

const tallClass = style({height:'100px'});
const redClass = style({color:'red'});

/** Compose classes */
const tallRedClass = typestyle.classes(tallClass, redClass);

/** Even conditionally (any falsy parameters are ignored in the composed class name) */
const mightBeRed = typestyle.classes(tallClass, hasError && redClass);

Animations Use keyframes to define an animation and get the animation name

const colorAnimationName = typestyle.keyframes({
  from: { color: 'red' },
  to: { color: 'blue' }
})

const ooooClass = typestyle.style({
  animationName: colorAnimationName,
  animationDuration: '1s'
});

TypeScript Protip: namespace

/** Think of it like an inline stylesheet */
namespace MyStyles {
  const color = 'red';

  export const alwaysRedClass = style({color});
  export const onlyRedOnHoverClass = style({'&:hover':{color});
}

/** Use e.g. with React */
const AlwaysRed = ({text}) => <div className={MyStyles.alwaysRedClass}>{text}</div>
const OnlyRedOnHover = ({text}) => <div className={MyStyles.onlyRedOnHoverClass}>{text}</div>

Fallbacks

There are two kinds of fallbacks in CSS and both are supported:

  • Same key multiple values: Just use an array for the value e.g. background colors
const fallBackBackground = style({
  backgroundColor: [
    /* The fallback */
    'rgb(200, 54, 54)',
    /** Graceful upgrade */
    'rgba(200, 54, 54, 0.5)'
  ]
});
  • Vendor prefixing: Anything that starts with - is not case renamed (i.e. no fooBar => foo-bar) e.g. for smooth scroll:
const scroll = style({
  '-webkit-overflow-scrolling': 'touch',
  overflow: 'auto'
});

Protip: Big fan of flexbox? Use csx as it provides the necessary vendor prefixes so you don't need to worry about them.

Note: We don't do automatic vendor prefixing for a few reasons:

How

This works very much in the same principle as CSS modules in that it takes a style object and generates a non conflicting generated class name.

Really How

  • FreeStyle converts a JS style object to a CSS className using hashing
  • We keep a single style sheet updated as you register styles.
  • Provide css.d.ts to help with autocomplete + error reporting.

Help

Really apprecate as many PRs for css.d.ts CSSProperties as you can throw at us 🌹

Performance

Same as FreeStyle which is super simple and does the absolute minimum but necessary, so faster than other CSS in JS frameworks for sure. We'd love to be told otherwise.