Near-zero runtime CSS-in-JS library
Write modular CSS within your JavaScript code with built-in themes and SSR support.
-
Near-Zero Runtime ⚡
Styles are generated when the module is initialized, rather than during component rendering. This eliminates runtime style generation, improving performance and reducing complexity. -
Component-Scoped Styles ✨
CSSFUN scopes styles to the component, preventing style leakage and promoting modularity. It keeps both logic and styling in the same file for easier management. -
Framework-Agnostic and Lightweight 🌐
CSSFUN is compatible with any environment. At just 1.7KB, it adds minimal overhead to your projects. -
No Build Tools Required 🛠️
CSSFUN can be used directly in the browser, eliminating the need for complex build tools or configurations. -
Server-Side Rendering (SSR) Support 🚀
CSSFUN supports server-side rendering out of the box, optimizing initial load times without duplicating styles. -
Built-in Theme Management 🎨
With built-in theme support, CSSFUN uses CSS variables to manage light and dark color schemes. Themes update automatically based on user preferences, no re-renders needed.
$ npm install cssfunimport { css } from 'cssfun';import { css } from 'https://esm.run/cssfun';<script src="https://cdn.jsdelivr.net/npm/cssfun"></script>const { css } = CSSFUN;const { classes } = css({
button : {
backgroundColor : 'blue',
color : 'white',
padding : '10px',
borderRadius : '5px'
}
});const Button = () => <button className={classes.button}>Click me</button>;When you call css(), CSSFUN automatically generates unique, scoped class names for each top-level selector in your styles object. These class names are created at module initialization, ensuring near-zero runtime overhead.
-
When: Classes are generated immediately when
css()is called, during the StyleSheet instance creation. -
Which selectors: Only top-level selectors that match valid class name patterns (alphanumeric characters, no special characters) get generated classes.
-
Format: The format differs between development and production modes:
Development Mode (readable, for debugging):
{prefix}-{uid}-{className}Example:
.fun-9qkk9s-button(prefixfun+ unique ID9qkk9s+ original class namebutton)Production Mode (optimized, smaller bundle):
{prefix[0]}-{uid}-{index}Example:
.f-9qkk9s-1(first letter of prefixf+ unique ID9qkk9s+ sequential index1) -
Access: Generated class names are available via the
classesobject returned bycss():const { classes } = css({ button: { color: 'red' }, link: { color: 'blue' } }); // classes.button → "fun-9qkk9s-button" (dev) or "f-9qkk9s-1" (prod) // classes.link → "fun-9qkk9s-link" (dev) or "f-9qkk9s-2" (prod)
Note: All examples in this documentation show class names in development mode for clarity.
In production, class names are automatically optimized for smaller bundle size.
You can customize class name generation viaoptions.generateClassNameor by extending the class.
Renderers are functions that transform style objects into CSS strings. They are applied in sequence, with each renderer receiving the output of the previous one.
CSSFUN uses two built-in renderers by default:
parseStyles: Transforms the style object (expands nested selectors, replaces class references, converts camelCase to dashed-case, handles global styles)renderStyles: Converts the processed object into a CSS string
The final renderer in the chain outputs the CSS string that gets injected into the DOM.
These are the built-in renderers transformations:
css({
root : {
backgroundColor : 'black',
fontSize : '16px',
paddingTop : '10px'
}
}).toString();<style data-fun-uid="uwitok">
.fun-uwitok-root {
background-color: black;
font-size: 16px;
padding-top: 10px;
}
</style>-
Use
&to reference the selector of the parent rulecss({ button : { backgroundColor : 'white', '&:hover' : { backgroundColor : 'black' }, '& span' : { color : 'blue' } } }).toString();
<style data-fun-uid="1pxyvx7"> .fun-1pxyvx7-button { background-color: white; } .fun-1pxyvx7-button:hover { background-color: black; } .fun-1pxyvx7-button span { color: blue; } </style>
-
Deep nesting
css({ button : { backgroundColor : 'white', '&:active' : { backgroundColor : 'black', '&:hover' : { backgroundColor : 'blue' } } } }).toString();
<style data-fun-uid="169vukw"> .fun-169vukw-button { background-color: white; } .fun-169vukw-button:active { background-color: black; } .fun-169vukw-button:active:hover { background-color: blue; } </style>
-
Use
$to reference a local class within the sameStyleSheetinstancecss({ button : { backgroundColor : 'white' }, '$button:hover' : { backgroundColor : 'black' }, '$button span' : { color : 'blue' } }).toString();
<style data-fun-uid="2xfpy0"> .fun-2xfpy0-button { background-color: white; } .fun-2xfpy0-button:hover { background-color: black; } .fun-2xfpy0-button span { color: blue; } </style>
-
Global block
css({ '@global' : { body : { backgroundColor : 'black' } } }).toString();
<style data-fun-uid="ml03n3"> body { background-color: black; } </style>
-
Nested global block
css({ root : { '@global' : { a : { color : 'black' } } } }).toString();
<style data-fun-uid="1eia2eq"> .fun-1eia2eq-root a { color: black; } </style>
-
Global prefix
css({ '@global body' : { backgroundColor : 'black' } }).toString();
<style data-fun-uid="1p1av20"> body { background-color: black; } </style>
-
Nested global prefix
css({ root : { '@global a' : { color : 'black' } } }).toString();
<style data-fun-uid="xvd6jj"> .fun-xvd6jj-root a { color: black; } </style>
When composed, the first renderer receives the styles object, and the final one outputs the resulting CSS string. The renderers are applied in sequence: each renderer receives the output of the previous one.
Example flow:
Input styles object
↓
[parseStyles] → Transforms object (expands nested, replaces references, converts camelCase)
↓
[renderStyles] → Converts object to CSS string
↓
Output CSS string
You can customize the renderers by setting the renderers array on the StyleSheet instance.
If passed via options.renderers, they will be automatically added to the instance.
Elements in the renderers array can be either functions or strings that reference methods of the StyleSheet instance. These
methods will be bound to the instance before they are invoked.
By default, StyleSheet instances are rendered using the built-in renderers: [this.renderStyles, this.parseStyles].
Note: The order matters! Renderers are composed, so they execute in reverse order. With [renderStyles, parseStyles], parseStyles executes first (transforms the object), then renderStyles (converts to CSS string).
A theme is a StyleSheet that provides access to CSS variables
for consistent styling across your application. It supports multiple color schemes,
including light, dark, light dark (default, adapts to system preferences), and normal.
Themes allow your components to automatically adapt to changes in the user's system preferences
or use a fixed color scheme.
The createTheme function generates a theme StyleSheet instance.
It accepts a themes object, which defines variables for the specified color schemes, and an
options object to customize the theme generation.
Each key in the themes object corresponds to a color scheme (light, dark, normal),
and its value is an object of key-value pairs that will be converted into CSS variables.
Define styles for light and dark color schemes using the createTheme function.
const theme = createTheme({
light : {
colorPrimary : 'black',
backgroundLevel1 : 'white'
},
dark : {
colorPrimary : 'white',
backgroundLevel1 : 'black'
}
});The options.colorScheme parameter specifies which color scheme(s) to use. Possible values are:
light: Uses thelighttheme only.dark: Uses thedarktheme only.light dark(default): Supports bothlightanddarkthemes, adapting to system preferences. You can override the system preference by setting thedata-color-schemeattribute tolightordarkon a parent element.normal: Uses thenormaltheme only.
The options.cssVarsPrefix parameter allows you to customize the prefix for the generated CSS variables.
By default, the prefix is fun. For example, a key colorPrimary in the theme will generate a CSS variable
like --fun-colorPrimary.
The generated theme includes a root class, which exposes all the theme's CSS variables to any element
that uses this class and its descendants. You can apply this class to the body element to style the
entire application, or to the root element of a specific component to apply the theme to just part of your UI.
// Add theme class to the body
document.body.classList.add(theme.classes.root);The themes object is automatically converted into CSS variables. For example:
{ backgroundLevel1 : 'black' }is converted into the CSS variable --fun-backgroundLevel1.
Nested structures like:
{
palette : {
common : {
black : '#000'
}
}
}are converted into --fun-palette-common-black.
You can use these variables in your component styles, even before the theme is applied. Your components will automatically update when the theme or system color scheme changes.
const { classes } = css({
button : {
color : 'var(--fun-colorPrimary)',
backgroundColor : 'var(--fun-backgroundLevel1)'
},
});
const Button = ({ label }) => <button className={classes.button}>{label}</button>;Easily add your styles to the server-rendered HTML by embedding the StyleSheets as a
string within the <head> of your page.
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StyleSheet, createTheme } from 'cssfun';
import App from './App.js';
// Create a theme with light and dark modes
const theme = createTheme({
light : {
bg : '#fff',
color : '#000'
},
dark : {
bg : '#000',
color : '#fff'
}
});
const app = express();
app.get('*', (req, res) => {
// Render the app
const html = renderToString(<App />);
// Get generated styles as string
const styles = StyleSheet.toString();
// Get theme root class
const cls = theme.classes.root;
const template = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSR App</title>
${styles}
</head>
<body class="${cls}">
<div id="root">${html}</div>
<script src="/bundle.js"></script>
</body>
</html>
`;
res.send(template);
});When the app is hydrated on the client side, the styles are preserved and won’t be recreated.
Complete API documentation can be found here.
For those working with LLMs, there is an AI Agents reference guide that provides API patterns, style syntax, theme management, and best practices, optimized for LLM context. You can share this guide with AI assistants to help them understand CSSFUN's architecture and styling APIs.
The examples folder contains various sample projects demonstrating how to use CSSFUN in
different environments and frameworks. Each example is a standalone project that you can run locally
to see CSSFUN in action.
- React Example: A basic React application demonstrating the use of CSSFUN for styling React components. Try it.
- Rasti Example: A simple Rasti application illustrating how to apply CSSFUN to style Rasti components. Try it.
- Vanilla JS Example: A straightforward JavaScript example showing how to use CSSFUN for styling HTML components. Try it.
CSSFUN is open-source and available under the MIT License.
Contributions are welcome! Share feature ideas or report bugs on our GitHub Issues page.