Fully fledged library to provide best DX for writing styles. In core it uses library fela
which seems like a good choice for quality, simplicity, performance. I strongly recommend reading Fela docs, especially sections Motivation and Principles. Big thanks to wafflepie for birth of this lib and inspiration.
- Platform and framework agnostic - This lib is platform agnostic and can be used with small effort nearly everywhere and with anything.
- Easy setup - There is no additional setup or plugin needed for your editor, compiler, linter...
- Strongly typed in TypeScript - Whole library + package
@trezor/theme
is strongly typed, so it will prevent many bugs (forgetting units, invalid property names...). Another benefit of strong types is perfectly working autocomplete and IntelliSense. - Natural syntax - Based on standard JS object syntax, very similar to React Native. This allows library to use full potential of CSS-in-JS without compromises (like ugly mixins etc.).
- Fast and small - Minimal bundle size and very high performance.
- Great developer experience - Created with DX as main focus point. Perfectly working IntelliSense, every util just one param away with minimum need of extra imports, clean declarative syntax for dynamic conditional styles, RTL support, code snippets...
import { createRenderer, StylesProvider } from '@trezor/styles';
import { defaultTheme } from '@trezor/theme';
import MyApp from './MyApp';
const renderer = createRenderer();
export default () => (
<StylesProvider renderer={renderer} theme={defaultTheme}>
<MyApp />
</StylesProvider>
);
const myButtonStyle = prepareStyle(() => ({
cursor: 'pointer',
width: '100px',
}));
export const MyButton = () => {
const { applyStyle } = useStyles();
return <div className={applyStyle(myButtonStyle)}>Hello world</div>;
};
const myButtonStyle = prepareStyle(utils => ({
cursor: 'pointer',
width: '100px',
color: utils.colors.red,
}));
export const MyButton = () => {
const { applyStyle } = useStyles();
return <div className={applyStyle(myButtonStyle)}>Hello world</div>;
};
You can pass any value you need to your styles.
type MyButtonProps = {
buttonColor: string;
};
type MyButtonStyleProps = MyButtonProps;
const myButtonStyle = prepareStyle<MyButtonStyleProps>((utils, { buttonColor }) => ({
cursor: 'pointer',
width: '100px',
color: buttonColor,
}));
export const MyButton = ({ buttonColor }: MyButtonProps) => {
const { applyStyle } = useStyles();
return <div className={applyStyle(myButtonStyle, { buttonColor })}>Hello world</div>;
};
const myButtonStyle = prepareStyle(utils => ({
cursor: 'pointer',
width: '100px',
selectors: {
'&:hover': {
color: utils.colors.red,
},
},
}));
First approach is useful when you need to change only a single value. Following code will set color
to red when going from xs
to md
. From md
higher the color will stay the same.
const myButtonStyle = prepareStyle(utils => ({
cursor: 'pointer',
width: '100px',
color: { xs: utils.colors.red, md: utils.colors.blue },
}));
Second approach is useful when you want to set more than one value. Following code will work exactly same as previous example, but you can define more than one property.
const myButtonStyle = prepareStyle(utils => ({
cursor: 'pointer',
width: '100px',
color: utils.colors.red
selectors: {
[utils.breakpoints.md]: {
color: utils.colors.blue,
width: '200px'
},
},
}));
There is plenty of options for conditional dynamic styling. First and simplest approach is to use plain inline condition, useful when you need to modify just one property.
type MyButtonStyleProps = {
isBigBlueButton: boolean;
};
const myButtonStyle = prepareStyle<MyButtonStyleProps>((utils, { isBigBlueButton }) => ({
cursor: 'pointer',
width: '100px',
color: isBigBlueButton ? colors.blue : colors.red,
}));
export const MyButton = () => {
const { applyStyle } = useStyles();
const isBigBlueButton = true;
return <div className={applyStyle(myButtonStyle, { isBigBlueButton })}>Hello world</div>;
};
Second most common approach is special declarative extend
syntax. Useful when need to conditionally declare multiple properties.
type MyButtonStyleProps = {
isBigBlueButton: boolean;
};
const myButtonStyle = prepareStyle<MyButtonStyleProps>((utils, { isBigBlueButton }) => ({
cursor: 'pointer',
width: '100px',
color: colors.red,
extend: {
condition: isBigBlueButton,
style: {
color: utils.colors.blue,
},
},
}));
Extend property could be single object of array of objects:
type MyButtonStyleProps = {
isBlueButton: boolean;
buttonSize: 'big' | 'small';
};
const myButtonStyle = prepareStyle<MyButtonStyleProps>((utils, { isBlueButton, buttonSize }) => ({
cursor: 'pointer',
width: '100px',
color: colors.red,
extend: [
{
condition: isBlueButton,
style: {
color: utils.colors.blue,
},
},
{
condition: buttonSize === 'big',
style: {
width: '200px',
},
},
],
}));
Also don't forget that you style is plain JS function that returns object so feel free to do any JS magic here. Only avoid heavy calculations here, because it could affect performance.
type MyButtonStyleProps = {
isBigBlueButton: boolean;
};
const myButtonStyle = prepareStyle<MyButtonStyleProps>((utils, { isBigBlueButton }) => {
if (isBigBlueButton) {
return {
color: utils.colors.blue,
};
}
return {
color: utils.colors.red,
};
});
// TODO prepareStyleFactory
Some useful functions are available in utils. Utils is first param of prepareStyle
or they are returned from useStyles
hook.
const { utils } = useStyles();
const [value, unit] = getValueAndUnit('20px'); // => [20, 'px']
multiply(0.5, '100%'); // => 50%
sum(['2rem', '3rem']); // => 5rem
negative(5); // => -5
Some of the utils like darken
, lighten
, transparentize
are just reexported from polished for best experience a to have everything in one place like our custom utils mentioned before.
All examples here were for web usage, but there is only few differences:
- Use
prepareNativeStyle
instead ofprepareStyle
- this will provide you with the best TS experience because it will autocomplete only properties that are available for RN - Use
applyNativeStyle
instead ofapplyStyle
- Do not use selectors or media queries - these are not supported in React Native and TS won't allow you to do that anyway