A lightweight and performant atomic CSS-in-JS library.
$ yarn add @swan-io/css
# --- or ---
$ npm install --save @swan-io/css
import { css, cx } from "@swan-io/css";
const sheet = css.make({
box: {
backgroundColor: "blue",
padding: 16,
},
large: {
padding: 24,
},
text: {
color: "white",
fontSize: 20,
":hover": {
color: "gray",
},
},
});
const Component = ({ large }: { large: boolean }) => (
<div className={cx(sheet.box, large && sheet.large)}>
<span className={sheet.text}>Hello world</span>
</div>
);
Create a new sheet object and inject the associated styles.
const sheet = css.make({
box: {
backgroundColor: "hotpink",
paddingHorizontal: 16,
// supports :hover, :focus and :active
":hover": { color: "red" },
":focus": { color: "green" },
":active": { color: "blue" },
},
});
console.log(sheet.box); // space-separated string of class names
const sheet = css.make(({ keyframes }) => ({
box: {
animationDuration: "300ms",
// inject a keyframes rule and generate a unique name for it
animationName: keyframes({
"0%": { opacity: 0 },
"100%": { opacity: 1 },
}),
},
}));
Note
Styles prefixed with $
will be inserted as non-atomic CSS-in-JS, which is particularly useful for resetting the styles of an HTML element.
const sheet = css.make({
// generates a single class, inserted before the rest
$reset: {
margin: 0,
padding: 0,
},
// generates multiple classes
input: {
color: "grey",
display: "flex",
},
});
Extend css.make
input with custom tokens (e.g. colors, spacing, fonts) / utils.
// theme.ts
import { css } from "@swan-io/css";
const input = css.extend({
colors: {
red: "#fa2c37",
blue: "#2c7fff",
green: "#00c950",
},
});
type CustomInput = typeof input;
declare module "@swan-io/css" {
export interface Input extends CustomInput {}
}
// main.ts
// import theme.ts before anything else
import "./theme";
import { createRoot } from "react-dom/client";
// …
const sheet = css.make(({ colors }) => ({
box: { backgroundColor: colors.blue },
}));
Concatenate the generated classes from left to right, with subsequent styles overwriting the property values of earlier ones.
const sheet = css.make({
box: {
display: "flex",
color: "red",
},
inline: {
display: "inline-flex",
},
});
// with inline={false}, applied style will be display: flex; color: red;
// with inline={true}, applied style will be display: inline-flex; color: red;
const Component = ({ inline }: { inline: boolean }) => (
<div className={cx(sheet.box, inline && sheet.inline)} />
);
For production, we recommend enabling the Vite extraction plugin to generate a static CSS file. This removes the runtime style injection, reduces bundle size, and improves performance.
import swanCss from "@swan-io/css/vite-plugin";
import react from "@vitejs/plugin-react-swc";
import { defineConfig } from "vite";
export default defineConfig(({ command }) => ({
plugins: [react(), command === "build" && swanCss()],
}));
- ⚖️ License