next-style 1.2.0 → 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 -429
- 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 -297
- 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,532 +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
|
-
|
|
8
|
-
|
|
9
|
-
## Package Information
|
|
10
|
-
|
|
11
|
-
- **Name:** next-style
|
|
12
|
-
- **Version:** 1.1.5
|
|
13
|
-
- **License:** MIT
|
|
14
|
-
- **Author:** kingslimes
|
|
15
|
-
https://github.com/kingslimes
|
|
16
|
-
- **Repository:**
|
|
17
|
-
https://github.com/kingslimes/next-style
|
|
18
|
-
- **Issue Tracker:**
|
|
19
|
-
https://github.com/kingslimes/next-style/issues
|
|
20
|
-
|
|
21
|
-
---
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
8
|
|
|
23
|
-
##
|
|
9
|
+
## Why next-style?
|
|
24
10
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
- `@font-face` support
|
|
32
|
-
- Built-in PostCSS + Autoprefixer
|
|
33
|
-
- Zero DOM dependency
|
|
34
|
-
- Tree-shakeable (`sideEffects: false`)
|
|
35
|
-
- Copy–paste friendly API
|
|
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.
|
|
36
17
|
|
|
37
|
-
|
|
18
|
+
## Quick Start
|
|
38
19
|
|
|
39
|
-
|
|
20
|
+
### Installation
|
|
40
21
|
|
|
41
|
-
```
|
|
42
|
-
npm install next-style
|
|
43
|
-
# or
|
|
22
|
+
```bash
|
|
44
23
|
bun add next-style
|
|
45
24
|
```
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
## Peer Dependencies
|
|
50
|
-
|
|
51
|
-
NextStyle relies on the following peer dependencies:
|
|
52
|
-
|
|
53
|
-
``` txt
|
|
54
|
-
react >= 18
|
|
55
|
-
postcss ^8
|
|
56
|
-
autoprefixer ^10
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Make sure they are installed in your project.
|
|
60
|
-
|
|
61
|
-
---
|
|
26
|
+
### Setup
|
|
62
27
|
|
|
63
|
-
|
|
28
|
+
#### 1. Configure PostCSS
|
|
64
29
|
|
|
65
|
-
|
|
30
|
+
Create `postcss.config.js` in your project root:
|
|
66
31
|
|
|
67
|
-
```
|
|
68
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
---
|
|
40
|
+
If you already have `postcss.config.js` (e.g. with Tailwind), add next-style first:
|
|
78
41
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
### `css(style): string`
|
|
52
|
+
> **Order matters** — next-style must come before other plugins.
|
|
119
53
|
|
|
120
|
-
|
|
54
|
+
#### 2. Import styles in `globals.css`
|
|
121
55
|
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fontSize: "16px"
|
|
126
|
-
})
|
|
56
|
+
```css
|
|
57
|
+
@import "next-style";
|
|
58
|
+
/* your other imports/rules */
|
|
127
59
|
```
|
|
128
60
|
|
|
129
|
-
|
|
130
|
-
- Deduplicates styles using hashing
|
|
131
|
-
- Returns a stable class name
|
|
61
|
+
The PostCSS plugin replaces this `@import` with the compiled CSS at build time.
|
|
132
62
|
|
|
133
|
-
|
|
63
|
+
#### 3. Use in components
|
|
134
64
|
|
|
135
|
-
|
|
65
|
+
```tsx
|
|
66
|
+
import { css } from "next-style"
|
|
136
67
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
| `_focus` | `:focus` |
|
|
143
|
-
| `_active` | `:active` |
|
|
144
|
-
|
|
145
|
-
Example:
|
|
146
|
-
|
|
147
|
-
``` ts
|
|
148
|
-
css({
|
|
149
|
-
color: "black",
|
|
150
|
-
_hover: {
|
|
151
|
-
color: "red"
|
|
152
|
-
}
|
|
68
|
+
const title = css({
|
|
69
|
+
fontSize: "32px",
|
|
70
|
+
fontWeight: 500,
|
|
71
|
+
"@md": { fontSize: "40px" },
|
|
72
|
+
":hover": { color: "#7F77DD" }
|
|
153
73
|
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Relation Selector API
|
|
159
74
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
This API is designed to be:
|
|
163
|
-
- Declarative and readable
|
|
164
|
-
- Type-safe (no string selectors)
|
|
165
|
-
- Fully compatible with runtime CSS-in-JS
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
### Basic Usage
|
|
170
|
-
|
|
171
|
-
``` ts
|
|
172
|
-
const { css, when } = new NextStyle()
|
|
173
|
-
|
|
174
|
-
const container = css({})
|
|
175
|
-
const button = css({})
|
|
176
|
-
|
|
177
|
-
when(container)
|
|
178
|
-
.hover()
|
|
179
|
-
.adjacent(button, {
|
|
180
|
-
backgroundColor: "red"
|
|
181
|
-
})
|
|
182
|
-
```
|
|
183
|
-
Equivalent CSS:
|
|
184
|
-
|
|
185
|
-
``` css
|
|
186
|
-
.container:hover + .button {
|
|
187
|
-
background-color: red;
|
|
75
|
+
export default function App() {
|
|
76
|
+
return <h1 className={title}>Hello World</h1>
|
|
188
77
|
}
|
|
189
78
|
```
|
|
190
79
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## Supported States
|
|
194
|
-
|
|
195
|
-
You can define relationships based on the following pseudo states:
|
|
196
|
-
|
|
197
|
-
- `hover()` → `:hover`
|
|
198
|
-
- `focus()` → `:focus`
|
|
199
|
-
- `active()` → `:active`
|
|
200
|
-
|
|
201
|
-
Example:
|
|
80
|
+
## Features
|
|
202
81
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
+
})
|
|
211
115
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
color: blue;
|
|
116
|
+
export function Button() {
|
|
117
|
+
return <button className={button}>Click me</button>
|
|
215
118
|
}
|
|
216
119
|
```
|
|
217
120
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
## Supported Relationships
|
|
121
|
+
### `global(styles: Record<string, CSSObject>): void`
|
|
221
122
|
|
|
222
|
-
|
|
123
|
+
Registers global CSS rules applied directly to selectors — no scoped class. Useful for resets, base typography, and third-party element overrides.
|
|
223
124
|
|
|
224
|
-
|
|
125
|
+
```tsx
|
|
126
|
+
import { global } from "next-style"
|
|
225
127
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
})
|
|
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
|
+
})
|
|
232
133
|
```
|
|
233
|
-
CSS equivalent:
|
|
234
134
|
|
|
235
|
-
|
|
236
|
-
div:hover + p {
|
|
237
|
-
color: red;
|
|
238
|
-
}
|
|
239
|
-
```
|
|
135
|
+
## Examples
|
|
240
136
|
|
|
241
|
-
|
|
137
|
+
### Responsive design
|
|
242
138
|
|
|
243
|
-
|
|
139
|
+
Breakpoints expand to standard `min-width` media queries and are sorted mobile-first automatically.
|
|
244
140
|
|
|
245
|
-
|
|
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)` |
|
|
246
148
|
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
CSS equivalent:
|
|
255
|
-
|
|
256
|
-
``` css
|
|
257
|
-
div:hover ~ p {
|
|
258
|
-
color: red;
|
|
259
|
-
}
|
|
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
|
+
})
|
|
260
156
|
```
|
|
261
157
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
### Child (`>`)
|
|
265
|
-
|
|
266
|
-
Applies styles to **direct children only**.
|
|
158
|
+
Arbitrary media queries are also supported:
|
|
267
159
|
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
})
|
|
274
|
-
```
|
|
275
|
-
CSS equivalent:
|
|
276
|
-
|
|
277
|
-
``` css
|
|
278
|
-
card:hover > icon {
|
|
279
|
-
transform: scale(1.1);
|
|
280
|
-
}
|
|
160
|
+
```tsx
|
|
161
|
+
const sidebar = css({
|
|
162
|
+
display: "none",
|
|
163
|
+
"@media (min-width: 900px)": { display: "block" }
|
|
164
|
+
})
|
|
281
165
|
```
|
|
282
166
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
### Descendant (space)
|
|
167
|
+
### Interactive states
|
|
286
168
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
CSS equivalent:
|
|
297
|
-
|
|
298
|
-
``` css
|
|
299
|
-
menu:hover item {
|
|
300
|
-
background-color: #eee;
|
|
301
|
-
}
|
|
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
|
+
})
|
|
302
178
|
```
|
|
303
179
|
|
|
304
|
-
|
|
180
|
+
### Keyframe animations
|
|
305
181
|
|
|
306
|
-
|
|
182
|
+
Declare `@keyframes` inline alongside the style that uses them:
|
|
307
183
|
|
|
308
|
-
|
|
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
|
+
```
|
|
309
195
|
|
|
310
|
-
|
|
311
|
-
- No need to manually concatenate class names
|
|
312
|
-
- Safer refactoring (class tokens, not strings)
|
|
313
|
-
- IDE autocomplete and JSDoc support
|
|
196
|
+
### Container queries
|
|
314
197
|
|
|
315
|
-
|
|
198
|
+
```tsx
|
|
199
|
+
const card = css({
|
|
200
|
+
fontSize: "14px",
|
|
201
|
+
"@container sidebar (min-width: 300px)": { fontSize: "16px" }
|
|
202
|
+
})
|
|
203
|
+
```
|
|
316
204
|
|
|
317
|
-
|
|
318
|
-
// global selector
|
|
319
|
-
global(`.${a}:hover + .${b}`, { color: "red" })
|
|
205
|
+
### CSS variables
|
|
320
206
|
|
|
321
|
-
|
|
322
|
-
|
|
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
|
+
})
|
|
323
214
|
```
|
|
324
215
|
|
|
325
|
-
|
|
216
|
+
## TypeScript
|
|
326
217
|
|
|
327
|
-
|
|
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.
|
|
328
219
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
- Media queries and nested styles are fully supported inside relation styles
|
|
220
|
+
```tsx
|
|
221
|
+
import { css, type CSSObject } from "next-style"
|
|
332
222
|
|
|
333
|
-
|
|
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
|
+
}
|
|
334
229
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
``` ts
|
|
338
|
-
when(container)
|
|
339
|
-
.hover()
|
|
340
|
-
.sibling(text, {
|
|
341
|
-
color: "red",
|
|
342
|
-
_md: {
|
|
343
|
-
color: "blue"
|
|
344
|
-
}
|
|
345
|
-
})
|
|
230
|
+
const title = css(myStyles)
|
|
346
231
|
```
|
|
347
232
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
This API is intentionally minimal and composable, allowing you to express complex UI relationships without leaking CSS selector syntax into your application code.
|
|
233
|
+
### Exported types
|
|
351
234
|
|
|
352
|
-
|
|
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 |
|
|
353
239
|
|
|
354
|
-
##
|
|
240
|
+
## Advanced: `createTransformer`
|
|
355
241
|
|
|
356
|
-
|
|
242
|
+
For SWC/Babel transforms and test harnesses that need an isolated collector independent of the shared runtime instance:
|
|
357
243
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
| `_sm` | `(min-width: 640px)` |
|
|
361
|
-
| `_md` | `(min-width: 768px)` |
|
|
362
|
-
| `_lg` | `(min-width: 1024px)` |
|
|
363
|
-
| `_xl` | `(min-width: 1280px)` |
|
|
364
|
-
| `_xxl` | `(min-width: 1536px)` |
|
|
244
|
+
```ts
|
|
245
|
+
import { createTransformer } from "next-style"
|
|
365
246
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
css({
|
|
370
|
-
fontSize: "14px",
|
|
371
|
-
_lg: {
|
|
372
|
-
fontSize: "18px"
|
|
373
|
-
}
|
|
374
|
-
})
|
|
247
|
+
const { collector, transformCssCall } = createTransformer()
|
|
248
|
+
const className = transformCssCall({ color: "red" }) // "ns-abc123"
|
|
249
|
+
const css = collector.getAllStyles() // ".ns-abc123 { color: red; }"
|
|
375
250
|
```
|
|
376
251
|
|
|
377
|
-
|
|
252
|
+
## Development
|
|
378
253
|
|
|
379
|
-
|
|
254
|
+
```bash
|
|
255
|
+
# Install dependencies
|
|
256
|
+
bun install
|
|
380
257
|
|
|
381
|
-
|
|
258
|
+
# Build
|
|
259
|
+
bun run build
|
|
382
260
|
|
|
383
|
-
|
|
261
|
+
# Watch for changes
|
|
262
|
+
bun run dev
|
|
384
263
|
|
|
385
|
-
|
|
264
|
+
# Type-check only
|
|
265
|
+
bunx tsc --noEmit
|
|
386
266
|
|
|
387
|
-
|
|
388
|
-
|
|
267
|
+
# Lint
|
|
268
|
+
bun run lint
|
|
389
269
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
fontFamily: "system-ui"
|
|
393
|
-
})
|
|
394
|
-
|
|
395
|
-
global("a", {
|
|
396
|
-
color: "inherit",
|
|
397
|
-
_hover: {
|
|
398
|
-
textDecoration: "underline"
|
|
399
|
-
}
|
|
400
|
-
})
|
|
270
|
+
# Format
|
|
271
|
+
bun run format
|
|
401
272
|
```
|
|
402
273
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
## Animations
|
|
406
|
-
|
|
407
|
-
### `keyframes(frames): string`
|
|
408
|
-
|
|
409
|
-
Creates a `@keyframes` rule and returns its name.
|
|
410
|
-
|
|
411
|
-
``` ts
|
|
412
|
-
const fadeIn = keyframes({
|
|
413
|
-
from: { opacity: 0 },
|
|
414
|
-
to: { opacity: 1 }
|
|
415
|
-
})
|
|
274
|
+
### Project structure
|
|
416
275
|
|
|
417
|
-
css({
|
|
418
|
-
animation: `${fadeIn} 300ms ease-in`
|
|
419
|
-
})
|
|
420
276
|
```
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
### `fontFace(font)`
|
|
427
|
-
|
|
428
|
-
Registers a `@font-face` rule.
|
|
429
|
-
|
|
430
|
-
``` ts
|
|
431
|
-
fontFace({
|
|
432
|
-
fontFamily: "MyFont",
|
|
433
|
-
src: "url(/fonts/myfont.woff2)",
|
|
434
|
-
fontWeight: 400,
|
|
435
|
-
fontStyle: "normal",
|
|
436
|
-
fontDisplay: "swap"
|
|
437
|
-
})
|
|
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
|
|
438
282
|
```
|
|
439
283
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
## Rendering Styles
|
|
284
|
+
## Performance
|
|
443
285
|
|
|
444
|
-
|
|
286
|
+
- **Bundle size** — ~2 KB minified + gzipped
|
|
287
|
+
- **Runtime cost** — 0 bytes (styles extracted at build time)
|
|
288
|
+
- **Build overhead** — negligible
|
|
445
289
|
|
|
446
|
-
|
|
290
|
+
## Browser support
|
|
447
291
|
|
|
448
|
-
|
|
449
|
-
<>
|
|
450
|
-
<StyleProvider />
|
|
451
|
-
<App />
|
|
452
|
-
</>
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
- Returns `null` if no styles exist
|
|
456
|
-
- Should be rendered **once per scope**
|
|
457
|
-
|
|
458
|
-
---
|
|
292
|
+
All modern browsers (Chrome, Firefox, Safari, Edge).
|
|
459
293
|
|
|
460
|
-
|
|
294
|
+
## Troubleshooting
|
|
461
295
|
|
|
462
|
-
|
|
296
|
+
### Styles not appearing
|
|
463
297
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
-
|
|
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
|
|
468
302
|
|
|
469
|
-
|
|
470
|
-
const cssText = toTextCss()
|
|
471
|
-
```
|
|
303
|
+
### Using alongside Tailwind / Autoprefixer
|
|
472
304
|
|
|
473
|
-
|
|
305
|
+
next-style must be listed **first** in the plugins object:
|
|
474
306
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
export function Card({ title, children }) {
|
|
483
|
-
const { css, StyleProvider } = new NextStyle("card")
|
|
484
|
-
|
|
485
|
-
const root = css({
|
|
486
|
-
padding: "16px",
|
|
487
|
-
borderRadius: "12px",
|
|
488
|
-
backgroundColor: "#fff",
|
|
489
|
-
boxShadow: "0 10px 25px rgba(0,0,0,.1)"
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
const heading = css({
|
|
493
|
-
fontSize: "18px",
|
|
494
|
-
fontWeight: 600,
|
|
495
|
-
marginBottom: "8px"
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
return (
|
|
499
|
-
<>
|
|
500
|
-
<StyleProvider />
|
|
501
|
-
<div className={root}>
|
|
502
|
-
<div className={heading}>{title}</div>
|
|
503
|
-
{children}
|
|
504
|
-
</div>
|
|
505
|
-
</>
|
|
506
|
-
)
|
|
307
|
+
```js
|
|
308
|
+
export default {
|
|
309
|
+
plugins: {
|
|
310
|
+
"next-style/plugin": {}, // ← first
|
|
311
|
+
tailwindcss: {},
|
|
312
|
+
autoprefixer: {}
|
|
313
|
+
}
|
|
507
314
|
}
|
|
508
315
|
```
|
|
509
316
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
## Best Practices
|
|
513
|
-
|
|
514
|
-
- Create **one NextStyle instance per page or component**
|
|
515
|
-
- Do **not** share instances globally
|
|
516
|
-
- Render `StyleProvider` only once per scope
|
|
517
|
-
- Use meaningful prefixes (`home`, `card`, `profile`)
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
## Design Intentions
|
|
317
|
+
## License
|
|
522
318
|
|
|
523
|
-
|
|
524
|
-
- No arbitrary selector nesting
|
|
525
|
-
- Predictable output over expressiveness
|
|
526
|
-
- Optimized for runtime and SSR safety
|
|
319
|
+
MIT © [TiwPhiraphan](https://github.com/TiwPhiraphan)
|
|
527
320
|
|
|
528
321
|
---
|
|
529
322
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
MIT © kingslimes
|
|
323
|
+
**Made with ❤️ for Next.js developers**
|