Making CSS type safe.
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.
- 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)
This project is powered by github ๐s ^ go ahead and star it please.
Checkout the awesome list of reviews ๐น.
- Quickstart
- Server side
- Pseudo Classes, Animations, Media Queries
- Fallbacks
- CSS Replacement
- CSX
- Book
- How
- Performance
- Help
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 {}
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 */
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
&>*
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({
'&>*': {
marginBottom: spacing,
},
'&>*: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>
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. nofooBar
=>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:
- Code bloat, runtime performance, you might want more control (we don't make choices that you might need to undo).
- Vendor prefixing has no future: https://webkit.org/blog/6131/updating-our-prefixing-policy/
You can even use any raw CSS selector as well using cssRule
e.g.
- To setup a application style layout:
/** Use full window size for application */
cssRule('html, body', {
height: '100%',
width: '100%',
padding: 0,
margin: 0
});
- Font faces:
cssRule('@font-face', {
fontFamily: '"Bitstream Vera Serif Bold"',
src: 'url("https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf")'
});
- Page level media queries:
/** Save ink with a white background */
cssRule('@media print', {
body: {
background: 'white'
}
});
Advantage:
cssRule(selector,properties)
works seemlessly in a nodejs enviroment (for testing) whereasrequire('./someCss.css')
does not without additional setup.
We understand that its difficult to get started with CSS in JS without additional guidance. So we also provide a lot of utility style objects in typestyle/csx
to decrease you rampup. e.g. flexbox:
import * as csx from 'typestyle/lib/csx';
import {style} from 'typestyle';
const horizontal = style(csx.horizontal);
/** Sample usage with React */
var Demo = () =>
<div className={horizontal}>
<div>One</div>
<div>Two</div>
<div>Three</div>
</div>;
Of course you can compose styles easily:
import * as csx from 'typestyle/lib/csx';
import {style} from 'typestyle';
const flexHorizontalGreen = style(
csx.flex,
csx.horizontal,
{ backgroundColor: 'green' }
);
/** Sample usage with React */
const Demo = () =>
<div className={flexHorizontalGreen}>
<div>One</div>
<div>Two</div>
<div>Three</div>
</div>;
To make it easier to work with colors, TypeStyle has a lot of the same color functions as SASS or LESS built-in with type definitions. This means that writing CSS in JS is even easier than ever!
Here is an example of some of the ways colors can be used together.
import { rgb, black } from 'typestyle/lib/csx';
const primaryColor = rgb(0, 0, 255);
const primaryBgColor = primaryColor.darken('30%').desaturate('10%');
const primaryBoldColor = primaryColor.mix(black);
const invertedColor = black.lighten('20%');
const invertedBgColor = primaryBgColor.invert();
const buttonStyles = style({
color: primaryColor,
backgroundColor: primaryBgColor
});
const invertedButtonStyles = style({
color: invertedColor,
backgroundColor: invertedBgColor
});
If you need to do testing with these colors, you can even print them to console by calling the .toString() method.
console.log(red.toString()); // prints 'rgb(255,0,0)'
We really really want to make CSS maintainable and simple. So we even wrote a free and open source book for how to use the utility styles in typestyle/lib/csx
๐น. Jump to the book
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.
- 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.
Really apprecate as many PRs for css.d.ts
CSSProperties
as you can throw at us ๐น
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.