next-style 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +220 -452
- package/dist/compiler/index.d.ts +95 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +229 -0
- package/dist/index.d.ts +5 -317
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -3
- package/dist/postcss-plugin/index.d.ts +28 -0
- package/dist/postcss-plugin/index.d.ts.map +1 -0
- package/dist/postcss-plugin/index.js +48 -0
- package/dist/runtime/index.d.ts +104 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +62 -0
- package/dist/utils/index.d.ts +60 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +97 -0
- package/package.json +37 -49
package/README.md
CHANGED
|
@@ -1,555 +1,323 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Next Style
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Zero-Runtime CSS-in-JS** for Next.js with Turbopack support
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A lightweight CSS-in-JS library that extracts styles at build time, resulting in zero runtime overhead. Write styles in JavaScript with full TypeScript support while shipping only pure CSS.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Why next-style?
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
|
|
16
|
-
- **
|
|
17
|
-
https://github.com/kingslimes/next-style
|
|
18
|
-
- **Issue Tracker:**
|
|
19
|
-
https://github.com/kingslimes/next-style/issues
|
|
11
|
+
- **Zero Runtime** — All style extraction happens at build time. Ship pure CSS, not JavaScript.
|
|
12
|
+
- **Turbopack Ready** — Optimized for Next.js 15+ and Turbopack without additional configuration.
|
|
13
|
+
- **Type Safe** — Full TypeScript support powered by [csstype](https://github.com/frenic/csstype) for intelligent autocomplete on every CSS property and value.
|
|
14
|
+
- **Automatic Deduplication** — Identical style objects always produce the same class name.
|
|
15
|
+
- **Responsive First** — Built-in shorthand breakpoints (`@sm`, `@md`, `@lg`, `@xl`, `@2xl`), sorted mobile-first automatically.
|
|
16
|
+
- **Developer Experience** — Simple API. Just `css({})` and go.
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
## Quick Start
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
### Installation
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
- Deterministic class names (same style → same class)
|
|
27
|
-
- Pseudo selectors (`:hover`, `:focus`, `:active`)
|
|
28
|
-
- Responsive media queries (`sm` → `xxl`)
|
|
29
|
-
- Global styles
|
|
30
|
-
- `@keyframes` support
|
|
31
|
-
- `@font-face` support
|
|
32
|
-
- Built-in PostCSS + Autoprefixer
|
|
33
|
-
- Zero DOM dependency
|
|
34
|
-
- Tree-shakeable (`sideEffects: false`)
|
|
35
|
-
- Copy–paste friendly API
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Installation
|
|
40
|
-
|
|
41
|
-
``` bash
|
|
42
|
-
npm install next-style
|
|
43
|
-
# or
|
|
22
|
+
```bash
|
|
44
23
|
bun add next-style
|
|
45
24
|
```
|
|
46
25
|
|
|
47
|
-
|
|
26
|
+
### Setup
|
|
48
27
|
|
|
49
|
-
|
|
28
|
+
#### 1. Configure PostCSS
|
|
50
29
|
|
|
51
|
-
|
|
30
|
+
Create `postcss.config.js` in your project root:
|
|
52
31
|
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Make sure they are installed in your project.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Recommended Usage Pattern (Scoped)
|
|
64
|
-
|
|
65
|
-
The **recommended and official pattern** is to scope styles per page or per component using destructuring.
|
|
66
|
-
|
|
67
|
-
``` ts
|
|
68
|
-
const { css, StyleProvider } = new NextStyle("home")
|
|
32
|
+
```js
|
|
33
|
+
export default {
|
|
34
|
+
plugins: {
|
|
35
|
+
"next-style/plugin": {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
69
38
|
```
|
|
70
39
|
|
|
71
|
-
|
|
72
|
-
- Clear scope ownership
|
|
73
|
-
- No global side effects
|
|
74
|
-
- Easy to copy and reuse
|
|
75
|
-
- Matches React’s mental model
|
|
40
|
+
If you already have `postcss.config.js` (e.g. with Tailwind), add next-style first:
|
|
76
41
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
export default function HomePage() {
|
|
85
|
-
const { css, StyleProvider } = new NextStyle("home")
|
|
86
|
-
|
|
87
|
-
const title = css({
|
|
88
|
-
fontSize: "32px",
|
|
89
|
-
fontWeight: 700,
|
|
90
|
-
marginBottom: "16px"
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const button = css({
|
|
94
|
-
padding: "10px 20px",
|
|
95
|
-
borderRadius: "8px",
|
|
96
|
-
backgroundColor: "#2563eb",
|
|
97
|
-
color: "#fff",
|
|
98
|
-
|
|
99
|
-
_hover: {
|
|
100
|
-
backgroundColor: "#1d4ed8"
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<>
|
|
106
|
-
<StyleProvider />
|
|
107
|
-
<h1 className={title}>Home</h1>
|
|
108
|
-
<button className={button}>Click me</button>
|
|
109
|
-
</>
|
|
110
|
-
)
|
|
42
|
+
```js
|
|
43
|
+
export default {
|
|
44
|
+
plugins: {
|
|
45
|
+
"next-style/plugin": {},
|
|
46
|
+
tailwindcss: {},
|
|
47
|
+
autoprefixer: {}
|
|
48
|
+
}
|
|
111
49
|
}
|
|
112
50
|
```
|
|
113
51
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
## Styling API
|
|
117
|
-
|
|
118
|
-
### `resetStyle(): this as NextStyle`
|
|
52
|
+
> **Order matters** — next-style must come before other plugins.
|
|
119
53
|
|
|
120
|
-
|
|
54
|
+
#### 2. Import styles in `globals.css`
|
|
121
55
|
|
|
122
|
-
```
|
|
123
|
-
|
|
56
|
+
```css
|
|
57
|
+
@import "next-style";
|
|
58
|
+
/* your other imports/rules */
|
|
124
59
|
```
|
|
125
|
-
or
|
|
126
|
-
``` ts
|
|
127
|
-
const { resetStyle } = new NextStyle()
|
|
128
|
-
resetStyle()
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### `root(style): void`
|
|
132
60
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
``` ts
|
|
136
|
-
root({
|
|
137
|
-
"--color-base": "#fff"
|
|
138
|
-
})
|
|
139
|
-
```
|
|
61
|
+
The PostCSS plugin replaces this `@import` with the compiled CSS at build time.
|
|
140
62
|
|
|
141
|
-
|
|
63
|
+
#### 3. Use in components
|
|
142
64
|
|
|
143
|
-
|
|
65
|
+
```tsx
|
|
66
|
+
import { css } from "next-style"
|
|
144
67
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
68
|
+
const title = css({
|
|
69
|
+
fontSize: "32px",
|
|
70
|
+
fontWeight: 500,
|
|
71
|
+
"@md": { fontSize: "40px" },
|
|
72
|
+
":hover": { color: "#7F77DD" }
|
|
149
73
|
})
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
- Automatically converts camelCase → kebab-case
|
|
153
|
-
- Deduplicates styles using hashing
|
|
154
|
-
- Returns a stable class name
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Pseudo Selectors
|
|
159
|
-
|
|
160
|
-
Supported pseudo keys:
|
|
161
|
-
|
|
162
|
-
| Key | CSS Output |
|
|
163
|
-
|----|-----------|
|
|
164
|
-
| `_hover` | `:hover` |
|
|
165
|
-
| `_focus` | `:focus` |
|
|
166
|
-
| `_active` | `:active` |
|
|
167
74
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
``` ts
|
|
171
|
-
css({
|
|
172
|
-
color: "black",
|
|
173
|
-
_hover: {
|
|
174
|
-
color: "red"
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## Relation Selector API
|
|
182
|
-
|
|
183
|
-
`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.
|
|
184
|
-
|
|
185
|
-
This API is designed to be:
|
|
186
|
-
- Declarative and readable
|
|
187
|
-
- Type-safe (no string selectors)
|
|
188
|
-
- Fully compatible with runtime CSS-in-JS
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
### Basic Usage
|
|
193
|
-
|
|
194
|
-
``` ts
|
|
195
|
-
const { css, when } = new NextStyle()
|
|
196
|
-
|
|
197
|
-
const container = css({})
|
|
198
|
-
const button = css({})
|
|
199
|
-
|
|
200
|
-
when(container)
|
|
201
|
-
.hover()
|
|
202
|
-
.adjacent(button, {
|
|
203
|
-
backgroundColor: "red"
|
|
204
|
-
})
|
|
205
|
-
```
|
|
206
|
-
Equivalent CSS:
|
|
207
|
-
|
|
208
|
-
``` css
|
|
209
|
-
.container:hover + .button {
|
|
210
|
-
background-color: red;
|
|
75
|
+
export default function App() {
|
|
76
|
+
return <h1 className={title}>Hello World</h1>
|
|
211
77
|
}
|
|
212
78
|
```
|
|
213
79
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
## Supported States
|
|
217
|
-
|
|
218
|
-
You can define relationships based on the following pseudo states:
|
|
219
|
-
|
|
220
|
-
- `hover()` → `:hover`
|
|
221
|
-
- `focus()` → `:focus`
|
|
222
|
-
- `active()` → `:active`
|
|
223
|
-
|
|
224
|
-
Example:
|
|
80
|
+
## Features
|
|
225
81
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
82
|
+
| Feature | Status | Details |
|
|
83
|
+
|---------|--------|---------|
|
|
84
|
+
| Zero Runtime | ✅ | Styles extracted at build time |
|
|
85
|
+
| Turbopack | ✅ | Native support for Next.js 15+ |
|
|
86
|
+
| Type Safety | ✅ | Powered by csstype — full property + value autocomplete |
|
|
87
|
+
| Responsive Breakpoints | ✅ | `@sm` `@md` `@lg` `@xl` `@2xl` |
|
|
88
|
+
| Arbitrary Media Queries | ✅ | `'@media (min-width: 900px)'` |
|
|
89
|
+
| Pseudo-classes | ✅ | `:hover` `:focus` `:active` `:disabled` `:focus-visible` … |
|
|
90
|
+
| Pseudo-elements | ✅ | `::before` `::after` `::first-letter` … |
|
|
91
|
+
| Keyframe Animations | ✅ | `'@keyframes name'` inline with the style object |
|
|
92
|
+
| Container Queries | ✅ | `'@container sidebar (min-width: 300px)'` |
|
|
93
|
+
| `@supports` | ✅ | `'@supports (display: grid)'` |
|
|
94
|
+
| `@layer` | ✅ | `'@layer utilities'` |
|
|
95
|
+
| CSS Variables | ✅ | `var(--token)` as values |
|
|
96
|
+
| Deduplication | ✅ | Same object → same class, always |
|
|
97
|
+
| Global Styles | ✅ | `global()` for resets and base rules |
|
|
98
|
+
|
|
99
|
+
## API
|
|
100
|
+
|
|
101
|
+
### `css(styles: CSSObject): string`
|
|
102
|
+
|
|
103
|
+
Converts a style object into a unique, stable class name. Identical objects always return the same class (deduplication). Styles are collected at build time — zero cost at runtime.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
const button = css({
|
|
107
|
+
padding: "8px 16px",
|
|
108
|
+
borderRadius: "6px",
|
|
109
|
+
backgroundColor: "#7F77DD",
|
|
110
|
+
color: "#fff",
|
|
111
|
+
cursor: "pointer",
|
|
112
|
+
":hover": { backgroundColor: "#534AB7" },
|
|
113
|
+
"@md": { padding: "10px 20px" }
|
|
114
|
+
})
|
|
234
115
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
color: blue;
|
|
116
|
+
export function Button() {
|
|
117
|
+
return <button className={button}>Click me</button>
|
|
238
118
|
}
|
|
239
119
|
```
|
|
240
120
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
## Supported Relationships
|
|
121
|
+
### `global(styles: Record<string, CSSObject>): void`
|
|
244
122
|
|
|
245
|
-
|
|
123
|
+
Registers global CSS rules applied directly to selectors — no scoped class. Useful for resets, base typography, and third-party element overrides.
|
|
246
124
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
``` ts
|
|
250
|
-
when(div)
|
|
251
|
-
.hover()
|
|
252
|
-
.adjacent(p, {
|
|
253
|
-
color: "red"
|
|
254
|
-
})
|
|
255
|
-
```
|
|
256
|
-
CSS equivalent:
|
|
125
|
+
```tsx
|
|
126
|
+
import { global } from "next-style"
|
|
257
127
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
128
|
+
global({
|
|
129
|
+
"*": { boxSizing: "border-box", margin: "0" },
|
|
130
|
+
"body": { fontFamily: "Inter, sans-serif", lineHeight: "1.6" },
|
|
131
|
+
"h1, h2, h3": { fontWeight: 500, lineHeight: "1.2" }
|
|
132
|
+
})
|
|
262
133
|
```
|
|
263
134
|
|
|
264
|
-
|
|
135
|
+
## Examples
|
|
265
136
|
|
|
266
|
-
###
|
|
137
|
+
### Responsive design
|
|
267
138
|
|
|
268
|
-
|
|
139
|
+
Breakpoints expand to standard `min-width` media queries and are sorted mobile-first automatically.
|
|
269
140
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
CSS equivalent:
|
|
141
|
+
| Shorthand | Expands to |
|
|
142
|
+
|-----------|-----------|
|
|
143
|
+
| `@sm` | `@media (min-width: 640px)` |
|
|
144
|
+
| `@md` | `@media (min-width: 768px)` |
|
|
145
|
+
| `@lg` | `@media (min-width: 1024px)` |
|
|
146
|
+
| `@xl` | `@media (min-width: 1280px)` |
|
|
147
|
+
| `@2xl` | `@media (min-width: 1536px)` |
|
|
278
148
|
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
149
|
+
```tsx
|
|
150
|
+
const container = css({
|
|
151
|
+
width: "100%",
|
|
152
|
+
padding: "16px",
|
|
153
|
+
"@md": { width: "768px", padding: "24px" },
|
|
154
|
+
"@lg": { width: "1024px", padding: "32px" }
|
|
155
|
+
})
|
|
283
156
|
```
|
|
284
157
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
### Child (`>`)
|
|
288
|
-
|
|
289
|
-
Applies styles to **direct children only**.
|
|
158
|
+
Arbitrary media queries are also supported:
|
|
290
159
|
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
})
|
|
160
|
+
```tsx
|
|
161
|
+
const sidebar = css({
|
|
162
|
+
display: "none",
|
|
163
|
+
"@media (min-width: 900px)": { display: "block" }
|
|
164
|
+
})
|
|
297
165
|
```
|
|
298
|
-
CSS equivalent:
|
|
299
166
|
|
|
300
|
-
|
|
301
|
-
card:hover > icon {
|
|
302
|
-
transform: scale(1.1);
|
|
303
|
-
}
|
|
304
|
-
```
|
|
167
|
+
### Interactive states
|
|
305
168
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.descendant(item, {
|
|
316
|
-
backgroundColor: "#eee"
|
|
317
|
-
})
|
|
318
|
-
```
|
|
319
|
-
CSS equivalent:
|
|
320
|
-
|
|
321
|
-
``` css
|
|
322
|
-
menu:hover item {
|
|
323
|
-
background-color: #eee;
|
|
324
|
-
}
|
|
169
|
+
```tsx
|
|
170
|
+
const link = css({
|
|
171
|
+
color: "#3b82f6",
|
|
172
|
+
textDecoration: "none",
|
|
173
|
+
transition: "color 0.2s",
|
|
174
|
+
":hover": { color: "#1e40af" },
|
|
175
|
+
":focus-visible": { outline: "2px solid #3b82f6", outlineOffset: "2px" },
|
|
176
|
+
":active": { color: "#1e3a8a" }
|
|
177
|
+
})
|
|
325
178
|
```
|
|
326
179
|
|
|
327
|
-
|
|
180
|
+
### Keyframe animations
|
|
328
181
|
|
|
329
|
-
|
|
182
|
+
Declare `@keyframes` inline alongside the style that uses them:
|
|
330
183
|
|
|
331
|
-
|
|
184
|
+
```tsx
|
|
185
|
+
const spinner = css({
|
|
186
|
+
animationName: "spin",
|
|
187
|
+
animationDuration: "1s",
|
|
188
|
+
animationTimingFunction: "linear",
|
|
189
|
+
animationIterationCount: "infinite",
|
|
190
|
+
"@keyframes spin": {
|
|
191
|
+
to: { transform: "rotate(360deg)" }
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
```
|
|
332
195
|
|
|
333
|
-
|
|
334
|
-
- No need to manually concatenate class names
|
|
335
|
-
- Safer refactoring (class tokens, not strings)
|
|
336
|
-
- IDE autocomplete and JSDoc support
|
|
196
|
+
### Container queries
|
|
337
197
|
|
|
338
|
-
|
|
198
|
+
```tsx
|
|
199
|
+
const card = css({
|
|
200
|
+
fontSize: "14px",
|
|
201
|
+
"@container sidebar (min-width: 300px)": { fontSize: "16px" }
|
|
202
|
+
})
|
|
203
|
+
```
|
|
339
204
|
|
|
340
|
-
|
|
341
|
-
// global selector
|
|
342
|
-
global(`.${a}:hover + .${b}`, { color: "red" })
|
|
205
|
+
### CSS variables
|
|
343
206
|
|
|
344
|
-
|
|
345
|
-
|
|
207
|
+
```tsx
|
|
208
|
+
const card = css({
|
|
209
|
+
backgroundColor: "var(--bg-primary)",
|
|
210
|
+
color: "var(--text-primary)",
|
|
211
|
+
padding: "var(--spacing-4)",
|
|
212
|
+
borderRadius: "var(--radius-lg)"
|
|
213
|
+
})
|
|
346
214
|
```
|
|
347
215
|
|
|
348
|
-
|
|
216
|
+
## TypeScript
|
|
349
217
|
|
|
350
|
-
|
|
218
|
+
All CSS properties and values are fully typed via [csstype](https://github.com/frenic/csstype). Typos in property names are caught at compile time and your editor will autocomplete valid values.
|
|
351
219
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
- Media queries and nested styles are fully supported inside relation styles
|
|
220
|
+
```tsx
|
|
221
|
+
import { css, type CSSObject } from "next-style"
|
|
355
222
|
|
|
356
|
-
|
|
223
|
+
const myStyles: CSSObject = {
|
|
224
|
+
fontSize: "16px", // ✅ typed
|
|
225
|
+
colour: "red", // ❌ compile error — unknown property
|
|
226
|
+
"@md": { fontSize: "20px" },
|
|
227
|
+
":hover": { opacity: 0.8 }
|
|
228
|
+
}
|
|
357
229
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
``` ts
|
|
361
|
-
when(container)
|
|
362
|
-
.hover()
|
|
363
|
-
.sibling(text, {
|
|
364
|
-
color: "red",
|
|
365
|
-
_md: {
|
|
366
|
-
color: "blue"
|
|
367
|
-
}
|
|
368
|
-
})
|
|
230
|
+
const title = css(myStyles)
|
|
369
231
|
```
|
|
370
232
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
This API is intentionally minimal and composable, allowing you to express complex UI relationships without leaking CSS selector syntax into your application code.
|
|
374
|
-
|
|
375
|
-
---
|
|
233
|
+
### Exported types
|
|
376
234
|
|
|
377
|
-
|
|
235
|
+
| Type | Description |
|
|
236
|
+
|------|-------------|
|
|
237
|
+
| `CSSObject` | Style object accepted by `css()` and `global()` |
|
|
238
|
+
| `CSSProperties` | CSS properties only (no at-rules or pseudos) — backed by csstype |
|
|
378
239
|
|
|
379
|
-
|
|
240
|
+
## Advanced: `createTransformer`
|
|
380
241
|
|
|
381
|
-
|
|
382
|
-
|----|-------------|
|
|
383
|
-
| `_sm` | `(min-width: 640px)` |
|
|
384
|
-
| `_md` | `(min-width: 768px)` |
|
|
385
|
-
| `_lg` | `(min-width: 1024px)` |
|
|
386
|
-
| `_xl` | `(min-width: 1280px)` |
|
|
387
|
-
| `_xxl` | `(min-width: 1536px)` |
|
|
242
|
+
For SWC/Babel transforms and test harnesses that need an isolated collector independent of the shared runtime instance:
|
|
388
243
|
|
|
389
|
-
|
|
244
|
+
```ts
|
|
245
|
+
import { createTransformer } from "next-style"
|
|
390
246
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
_lg: {
|
|
395
|
-
fontSize: "18px"
|
|
396
|
-
}
|
|
397
|
-
})
|
|
247
|
+
const { collector, transformCssCall } = createTransformer()
|
|
248
|
+
const className = transformCssCall({ color: "red" }) // "ns-abc123"
|
|
249
|
+
const css = collector.getAllStyles() // ".ns-abc123 { color: red; }"
|
|
398
250
|
```
|
|
399
251
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
---
|
|
252
|
+
## Development
|
|
403
253
|
|
|
404
|
-
|
|
254
|
+
```bash
|
|
255
|
+
# Install dependencies
|
|
256
|
+
bun install
|
|
405
257
|
|
|
406
|
-
|
|
258
|
+
# Build
|
|
259
|
+
bun run build
|
|
407
260
|
|
|
408
|
-
|
|
261
|
+
# Watch for changes
|
|
262
|
+
bun run dev
|
|
409
263
|
|
|
410
|
-
|
|
411
|
-
|
|
264
|
+
# Type-check only
|
|
265
|
+
bunx tsc --noEmit
|
|
412
266
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
fontFamily: "system-ui"
|
|
416
|
-
})
|
|
267
|
+
# Lint
|
|
268
|
+
bun run lint
|
|
417
269
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
_hover: {
|
|
421
|
-
textDecoration: "underline"
|
|
422
|
-
}
|
|
423
|
-
})
|
|
270
|
+
# Format
|
|
271
|
+
bun run format
|
|
424
272
|
```
|
|
425
273
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
## Animations
|
|
429
|
-
|
|
430
|
-
### `keyframes(frames): string`
|
|
274
|
+
### Project structure
|
|
431
275
|
|
|
432
|
-
Creates a `@keyframes` rule and returns its name.
|
|
433
|
-
|
|
434
|
-
``` ts
|
|
435
|
-
const fadeIn = keyframes({
|
|
436
|
-
from: { opacity: 0 },
|
|
437
|
-
to: { opacity: 1 }
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
css({
|
|
441
|
-
animation: `${fadeIn} 300ms ease-in`
|
|
442
|
-
})
|
|
443
276
|
```
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
### `fontFace(font)`
|
|
450
|
-
|
|
451
|
-
Registers a `@font-face` rule.
|
|
452
|
-
|
|
453
|
-
``` ts
|
|
454
|
-
fontFace({
|
|
455
|
-
fontFamily: "MyFont",
|
|
456
|
-
src: "url(/fonts/myfont.woff2)",
|
|
457
|
-
fontWeight: 400,
|
|
458
|
-
fontStyle: "normal",
|
|
459
|
-
fontDisplay: "swap"
|
|
460
|
-
})
|
|
277
|
+
src/
|
|
278
|
+
├── runtime/ # css() · global() · CSSObject type
|
|
279
|
+
├── postcss-plugin/ # @import "next-style" → compiled CSS
|
|
280
|
+
├── compiler/ # StyleCollector · createTransformer
|
|
281
|
+
└── utils/ # camelToKebab · generateClassHash · BREAKPOINTS
|
|
461
282
|
```
|
|
462
283
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
## Rendering Styles
|
|
466
|
-
|
|
467
|
-
### `<StyleProvider />`
|
|
284
|
+
## Performance
|
|
468
285
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
<>
|
|
473
|
-
<StyleProvider />
|
|
474
|
-
<App />
|
|
475
|
-
</>
|
|
476
|
-
```
|
|
286
|
+
- **Bundle size** — ~2 KB minified + gzipped
|
|
287
|
+
- **Runtime cost** — 0 bytes (styles extracted at build time)
|
|
288
|
+
- **Build overhead** — negligible
|
|
477
289
|
|
|
478
|
-
|
|
479
|
-
- Should be rendered **once per scope**
|
|
290
|
+
## Browser support
|
|
480
291
|
|
|
481
|
-
|
|
292
|
+
All modern browsers (Chrome, Firefox, Safari, Edge).
|
|
482
293
|
|
|
483
|
-
|
|
294
|
+
## Troubleshooting
|
|
484
295
|
|
|
485
|
-
|
|
296
|
+
### Styles not appearing
|
|
486
297
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
-
|
|
298
|
+
1. Confirm `postcss.config.js` includes `"next-style/plugin": {}`
|
|
299
|
+
2. Confirm `@import "next-style";` is present in `globals.css`
|
|
300
|
+
3. Restart the dev server — PostCSS config changes require a restart
|
|
301
|
+
4. Clear the Next.js cache: `rm -rf .next` then restart
|
|
491
302
|
|
|
492
|
-
|
|
493
|
-
const cssText = toTextCss()
|
|
494
|
-
```
|
|
303
|
+
### Using alongside Tailwind / Autoprefixer
|
|
495
304
|
|
|
496
|
-
|
|
305
|
+
next-style must be listed **first** in the plugins object:
|
|
497
306
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
export function Card({ title, children }) {
|
|
506
|
-
const { css, StyleProvider } = new NextStyle("card")
|
|
507
|
-
|
|
508
|
-
const root = css({
|
|
509
|
-
padding: "16px",
|
|
510
|
-
borderRadius: "12px",
|
|
511
|
-
backgroundColor: "#fff",
|
|
512
|
-
boxShadow: "0 10px 25px rgba(0,0,0,.1)"
|
|
513
|
-
})
|
|
514
|
-
|
|
515
|
-
const heading = css({
|
|
516
|
-
fontSize: "18px",
|
|
517
|
-
fontWeight: 600,
|
|
518
|
-
marginBottom: "8px"
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
return (
|
|
522
|
-
<>
|
|
523
|
-
<StyleProvider />
|
|
524
|
-
<div className={root}>
|
|
525
|
-
<div className={heading}>{title}</div>
|
|
526
|
-
{children}
|
|
527
|
-
</div>
|
|
528
|
-
</>
|
|
529
|
-
)
|
|
307
|
+
```js
|
|
308
|
+
export default {
|
|
309
|
+
plugins: {
|
|
310
|
+
"next-style/plugin": {}, // ← first
|
|
311
|
+
tailwindcss: {},
|
|
312
|
+
autoprefixer: {}
|
|
313
|
+
}
|
|
530
314
|
}
|
|
531
315
|
```
|
|
532
316
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
## Best Practices
|
|
536
|
-
|
|
537
|
-
- Create **one NextStyle instance per page or component**
|
|
538
|
-
- Do **not** share instances globally
|
|
539
|
-
- Render `StyleProvider` only once per scope
|
|
540
|
-
- Use meaningful prefixes (`home`, `card`, `profile`)
|
|
541
|
-
|
|
542
|
-
---
|
|
543
|
-
|
|
544
|
-
## Design Intentions
|
|
317
|
+
## License
|
|
545
318
|
|
|
546
|
-
|
|
547
|
-
- No arbitrary selector nesting
|
|
548
|
-
- Predictable output over expressiveness
|
|
549
|
-
- Optimized for runtime and SSR safety
|
|
319
|
+
MIT © [TiwPhiraphan](https://github.com/TiwPhiraphan)
|
|
550
320
|
|
|
551
321
|
---
|
|
552
322
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
MIT © kingslimes
|
|
323
|
+
**Made with ❤️ for Next.js developers**
|