Lightweight runtime CSS-in-JS engine with nested pseudo selectors, media queries, global styles, keyframes, and font-face support.
npm install next-styleA lightweight runtime CSS-in-JS engine for React with deterministic class names, nested pseudo selectors, media queries, global styles, keyframes, and font-face support.
Designed for page-scoped and component-scoped usage without build-time tooling.
---
- Name: next-style
- Version: 1.2.1
- License: MIT
- Author: kingslimes
https://github.com/kingslimes
- Repository:
https://github.com/kingslimes/next-style
- Issue Tracker:
https://github.com/kingslimes/next-style/issues
---
- Object-based styling (TypeScript friendly)
- Deterministic class names (same style → same class)
- Pseudo selectors (:hover, :focus, :active)
- Responsive media queries (sm → xxl)
- Global styles
- @keyframes support
- @font-face support
- Built-in PostCSS + Autoprefixer
- Zero DOM dependency
- Tree-shakeable (sideEffects: false)
- Copy–paste friendly API
---
`` bash`
npm install next-styleor
bun add next-style
---
NextStyle relies on the following peer dependencies:
` txt`
react >= 18
postcss ^8
autoprefixer ^10
Make sure they are installed in your project.
---
The recommended and official pattern is to scope styles per page or per component using destructuring.
` ts`
const { css, StyleProvider } = new NextStyle("home")
Why this works well:
- Clear scope ownership
- No global side effects
- Easy to copy and reuse
- Matches React’s mental model
---
` tsx
import { NextStyle } from "next-style"
export default function HomePage() {
const { css, StyleProvider } = new NextStyle("home")
const title = css({
fontSize: "32px",
fontWeight: 700,
marginBottom: "16px"
})
const button = css({
padding: "10px 20px",
borderRadius: "8px",
backgroundColor: "#2563eb",
color: "#fff",
_hover: {
backgroundColor: "#1d4ed8"
}
})
return (
<>
---
Styling API
$3
Reset default style
` ts
const { css, ... } = new NextStyle().resetStyle()
`
or
` ts
const { resetStyle } = new NextStyle()
resetStyle()
`$3
Create root style from a style object.
` ts
root({
"--color-base": "#fff"
})
`$3
Creates a class name from a style object.
` ts
const className = css({
color: "red",
fontSize: "16px"
})
`- Automatically converts camelCase → kebab-case
- Deduplicates styles using hashing
- Returns a stable class name
---
Pseudo Selectors
Supported pseudo keys:
| Key | CSS Output |
|----|-----------|
|
_hover | :hover |
| _focus | :focus |
| _active | :active |Example:
` ts
css({
color: "black",
_hover: {
color: "red"
}
})
`---
Relation Selector API
next-style provides a relation-based selector API that allows you to define styles based on the state of one element affecting another element, without manually writing complex CSS selectors.This API is designed to be:
- Declarative and readable
- Type-safe (no string selectors)
- Fully compatible with runtime CSS-in-JS
---
$3
` ts
const { css, when } = new NextStyle()const container = css({})
const button = css({})
when(container)
.hover()
.adjacent(button, {
backgroundColor: "red"
})
`
Equivalent CSS:` css
.container:hover + .button {
background-color: red;
}
`---
Supported States
You can define relationships based on the following pseudo states:
-
hover() → :hover
- focus() → :focus
- active() → :activeExample:
` ts
when(input)
.focus()
.sibling(label, {
color: "blue"
})
`
Equivalent CSS:` css
input:focus ~ label {
color: blue;
}
`---
Supported Relationships
$3
Applies styles to the immediately following sibling.
` ts
when(div)
.hover()
.adjacent(p, {
color: "red"
})
`
CSS equivalent:` css
div:hover + p {
color: red;
}
`---
$3
Applies styles to all following siblings.
` ts
when(div)
.hover()
.sibling(p, {
color: "red"
})
`
CSS equivalent:` css
div:hover ~ p {
color: red;
}
`---
$3
Applies styles to direct children only.
` ts
when(card)
.hover()
.child(icon, {
transform: "scale(1.1)"
})
`
CSS equivalent:` css
card:hover > icon {
transform: scale(1.1);
}
`---
$3
Applies styles to any nested element.
` ts
when(menu)
.hover()
.descendant(item, {
backgroundColor: "#eee"
})
`
CSS equivalent:` css
menu:hover item {
background-color: #eee;
}
`---
Why Use
when() Instead of global()?While
global() allows full control over raw selectors, when() provides:- Clear intent and better readability
- No need to manually concatenate class names
- Safer refactoring (class tokens, not strings)
- IDE autocomplete and JSDoc support
Comparison:
` ts
// global selector
global(.${a}:hover + .${b}, { color: "red" })// relation API
when(a).hover().adjacent(b, { color: "red" })
`---
Notes
-
when() works with class names generated by css()
- Relation rules are injected via the same internal pipeline as global()
- Media queries and nested styles are fully supported inside relation styles---
Example With Media Query
` ts
when(container)
.hover()
.sibling(text, {
color: "red",
_md: {
color: "blue"
}
})
`---
This API is intentionally minimal and composable, allowing you to express complex UI relationships without leaking CSS selector syntax into your application code.
---
Responsive Media Queries
Built-in breakpoints:
| Key | Media Query |
|----|-------------|
|
_sm | (min-width: 640px) |
| _md | (min-width: 768px) |
| _lg | (min-width: 1024px) |
| _xl | (min-width: 1280px) |
| _xxl | (min-width: 1536px) |Example:
` ts
css({
fontSize: "14px",
_lg: {
fontSize: "18px"
}
})
`Media queries can be nested and merged automatically.
---
Global Styles
$3
Apply styles globally without generating a class.
` ts
const { global, StyleProvider } = new NextStyle("global")global("body", {
margin: 0,
fontFamily: "system-ui"
})
global("a", {
color: "inherit",
_hover: {
textDecoration: "underline"
}
})
`---
Animations
$3
Creates a
@keyframes rule and returns its name.` ts
const fadeIn = keyframes({
from: { opacity: 0 },
to: { opacity: 1 }
})css({
animation:
${fadeIn} 300ms ease-in
})
`---
Fonts
$3
Registers a
@font-face rule.` ts
fontFace({
fontFamily: "MyFont",
src: "url(/fonts/myfont.woff2)",
fontWeight: 400,
fontStyle: "normal",
fontDisplay: "swap"
})
`---
Rendering Styles
$3
Injects all generated CSS into a