create-styles v0.0.5
š¼ Create Styles
Create dynamic style sheets and link them to functional components using the React hook
pattern.
- ā Fully featured TypeScript support
- ā
Build on top of
@emotion/react
: As fast and lightweight as emotion - ā Supports all emotion features: lazy evaluation, dynamic theming, etc.
- ā Server side rendering support: Next.js, Gatsby or any other environment
- ā
@emotion
cache support
const useStyles = createStyles()(({theme, params}) => ({
root: /* */,
container: /* */,
text: /* */,
}));
const MyComponent = () => {
const { classes } = useStyles({ color: 'red', fontSize: 10 });
return (
<div className={classes.root}>
{/* */}
</div>
);
}
š» Installation
$ yarn add create-styles @emotion/react
# or
$ npm install create-styles @emotion/react
ā³ļø Code Sandbox
- Basic example
- (more coming soon)
šØ Javascript support
Although create-styles
has been designed with Typescript in mind
it can, of course, be used in any vanilla JS project.
ā³ Quick Start
šŖ Basic usage
./styles.ts
First, instantiate the method createStyles()
using the makeCreateStyles()
method.
import { makeCreateStyles } from 'create-styles';
// Theme hook that returns the current theme of the app.
function useTheme(): Theme {
return {
primaryColor: "#32CD32",
};
}
// Initialization of the 'createStyles()' method using the 'makeCreateStyles()' method.
// The theme hook is given as the only parameter,
// and should expose the apps theme to the 'createStyles()' method.
export const createStyles = makeCreateStyles(useTheme);
./MyComponent.tsx
Use the instantiated createStyles()
method to create dynamic styles
and link them to functional components using the React hook pattern.
import React from 'react';
import { css } from '@emotion/react';
import { createStyles } from "./styles";
type MyComponentStyles = { color: "red" | "blue", fontSize: number }
// Specify dynamic styles and access them later with the returned 'useStyles()' hook.
// (Note: Double method ('createStyle()()') due to partial type inference of 'TStyles')
const useStyles = createStyles<MyComponentStyles>()(
({theme, params}) => ({
// The styles of the specified classes can be created with a css object
root: {
backgroundColor: params.color,
"&:hover": {
backgroundColor: theme.primaryColor,
},
},
// or with the common 'css()' method of '@emotion/react'
text: css`
font-weight: bold;
font-size: ${params.fontSize}px;
color: black;
`
}),
);
export const Demo: React.FC = (props) => {
const { className } = props;
const [color, setColor] = useState<"red" | "blue">("red");
// Use the created 'useStyles()' hook to access the specified stylesheet as classes
// and some utilities like 'cx()'.
const { classes, cx } = useStyles({ color, fontSize: 10 });
return (
<div className={cx(classes.root, className)}>
<p className={classes.text}>hello world</p>
</div>
);
}
š Classes merging with cx()
To merge class names, use the cx()
method.
It has the same API as the popular clsx package
but is optimized for the use with emotion.
The key advantage of cx()
is that it detects emotion generated class names
ensuring styles are overwritten in the correct order.
Emotion generated styles are applied from left to right.
Subsequent styles overwrite property values of previous styles.
import React from 'react';
import { createStyles } from "./styles";
const useStyles = createStyles(({theme}) => ({
button: {
backgroundColor: theme.colors.darkBlue,
border: 0,
color: theme.colors.white,
borderRadius: 5,
padding: '10px 20px',
cursor: 'pointer',
},
active: {
backgroundColor: theme.colors.lightBlue,
color: theme.colors.black,
},
bold: {
fontWeight: 'bold',
}
}));
const Demo: React.FC = () => {
const [active, setActive] = useState(0);
const { classes, cx } = useStyles();
return (
<div>
<button
// Merge class names with the 'cx()' method
className={cx(classes.button, { [classes.active]: active === 0 })}
onClick={() => setActive(0)}
type="button"
>
First
</button>
<button
// Merge class names with the 'cx()' method
className={cx(classes.button, classes.bold, { [classes.active]: active === 1 })}
onClick={() => setActive(1)}
type="button"
>
Second (Bold)
</button>
</div>
);
}
āļø Composition and nested selectors
Since createStyles()
generates scoped class names based on the specified stylesheet,
you have to create a reference to the selector (e.g. 'button') to get the static selector (e.g. 'prefix-ref_button_1').
The createStyles()
method receives the createRef()
method to handle the creation of static selectors.
import React from 'react';
import { createStyles } from "./styles";
const useStyles = createStyles(({theme, params, createRef, assignRef}) => {
// Create reference for future use
const button = createRef('button'); // Returns a static selector (e.g. 'prefix-ref_button_1')
return {
// Assign ref variant 1:
button: {
// Assign the reference to the selector via the 'ref' property
ref: button,
// and add any other style properties
backgroundColor: theme.colors.blue,
color: theme.colors.white,
padding: `10px 20px`,
borderRadius: 5,
cursor: 'pointer',
},
// Assign ref variant 2:
// Assign the reference to the selector via the 'assignRef()' method
button2: assignRef(button, {
// and add any other style properties
backgroundColor: theme.colors.blue,
color: theme.colors.white,
padding: `10px 20px`,
borderRadius: 5,
cursor: 'pointer',
}),
container: {
display: 'flex',
justifyContent: 'center',
backgroundColor: theme.colors.white,
padding: 10,
// Reference button with the previously created static selector
[`&:hover .${button}`]: {
backgroundColor: theme.colors.violet[6],
},
},
};
});
const Demo: React.FC = () => {
const { classes } = useStyles();
return (
<div className={classes.container}>
<button className={classes.button} type="button">
Hover container to change button color
</button>
</div>
);
}
š„ Keyframes
You can define animations using the keyframes
helper from @emotion/react
.
keyframes
takes in a css keyframe definition and returns an object you can use in styles.
You can use strings or objects just like css
.
import React from 'react';
import { keyframes } from '@emotion/react';
import { createStyles } from "./styles";
// Create keyframes with the 'keyframes()' method
const bounce = keyframes`
from, 20%, 53%, 80%, to {
transform: translate3d(0,0,0);
}
40%, 43% {
transform: translate3d(0, -30px, 0);
}
70% {
transform: translate3d(0, -15px, 0);
}
90% {
transform: translate3d(0,-4px,0);
}
`
const useStyles = createStyles(({theme}) => ({
container: {
textAlign: 'center',
// Use specified 'bounce' keyframes in the 'container' styles
animation: `${bounce} 3s ease-in-out infinite`,
},
}));
const Demo: React.FC = () => {
const { classes } = useStyles();
return <div className={classes.container}>Keyframes demo</div>;
}
š Global styles
Sometimes you might want to insert global css
styles. You can use the component to do this.
import React from 'react';
import { GlobalStyles } from 'create-styles';
export function App() {
return (
<>
<GlobalStyles
styles={{
'*, *::before, *::after': {
boxSizing: 'border-box',
},
body: {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark : theme.white,
color: theme.colorScheme === 'dark' ? theme.colors.dark : theme.black,
lineHeight: 20,
},
}}
/>
<YourApp />
</>
);
}
š normalize.css
The NormalizeCss
component sets the normalized styles specified in normalize.css globally.
import {NormalizeCSS} from 'create-styles';
function App() {
return (
<>
<NormalizeCSS />
<YourApp />
</>
);
}
āļø Inline styles
If you plan to style reusable components with the createStyles()
API,
you may want to customize the components later with, for example, inline styles.
You can easily achieve this specifying the styles
property (e.g. partial of specified stylesheet)
in the useStyles()
hook's configuration object.
Button.tsx
Create Button
component.
import React from 'react';
import { createStyles } from "./styles";
const useStyles = createStyles<ButtonStyles>()(({theme, params: { color, radius }}) => ({
root: {
color: theme.colors.white,
backgroundColor: color,
borderRadius: radius,
padding: '10px 20px',
cursor: 'pointer',
},
}));
type ButtonStyles = {
color: string;
radius: number;
};
// Create a type that represents the created stylesheet type (extracted from the 'useStyles()' hook).
// Can then be used to add a typesafe styles property to the Button component.
export type ExtractedStylesType = ExtractStylesType<typeof useStyles>;
export const Button: React.FC<ButtonProps> = (props) => {
const { color = 'blue', radius = 0, styles = {}, onClick } = props;
// Pass the 'styles' property to the 'useStyles()' hook
const { classes } = useStyles({ color, radius }, { styles, name: 'Button' });
return (
<button type="button" className={classes.root} onClick={onClick}>
{color} button with {radius}px radius
</button>
);
};
type ButtonProps = {
color?: string;
radius?: number;
styles?: ExtractedStylesType; // Complete typesafety based on the created stylesheet
onClick: () => void;
};
./MyComponent.tsx
Use the created Button
component and specify inline
styles
using the styles
property.
import React from 'react';
import { css } from '@emotion/react';
import { Button } from './Button';
const MyComponent: React.FC = () => {
return (
<div>
<Button
onClick={() => setToggled(!toggled)}
// Inline styles using the 'styles' property
styles={{
root: css`
background: rebeccapurple;
`,
}}
/>
</div>
);
};
šØ API documentation
coming soon