next-style 2.2.2 → 2.2.3

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.
Files changed (2) hide show
  1. package/README.md +431 -11
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
  Write styles in TypeScript. Ship pure CSS. Zero overhead.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/next-style?color=7F77DD&labelColor=000)](https://www.npmjs.com/package/next-style)
10
+ [![npm downloads](https://img.shields.io/npm/dm/next-style?color=7F77DD&labelColor=000)](https://www.npmjs.com/package/next-style)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-ready-7F77DD?labelColor=000)](https://www.typescriptlang.org/)
10
12
  [![License: MIT](https://img.shields.io/badge/license-MIT-7F77DD?labelColor=000)](https://opensource.org/licenses/MIT)
11
13
  [![Turbopack](https://img.shields.io/badge/Turbopack-ready-7F77DD?labelColor=000)](https://turbo.build/pack)
12
14
 
@@ -14,8 +16,102 @@ Write styles in TypeScript. Ship pure CSS. Zero overhead.
14
16
 
15
17
  ---
16
18
 
19
+ ## Overview
20
+
17
21
  **next-style** extracts all styles at build time through a PostCSS plugin — no style injection, no hydration cost, no runtime. The compiled CSS lands in your `globals.css` exactly once.
18
22
 
23
+ ## Table of Contents
24
+
25
+ - [Overview](#overview)
26
+ - [Quick Start](#quick-start)
27
+ - [Features](#features)
28
+ - [Installation](#installation)
29
+ - [Setup](#setup)
30
+ - [API](#api)
31
+ - [Responsive Design](#responsive-design)
32
+ - [TypeScript](#typescript)
33
+ - [Advanced](#advanced)
34
+ - [Performance](#performance)
35
+ - [Best Practices](#best-practices--common-patterns)
36
+ - [Troubleshooting](#troubleshooting)
37
+ - [Browser Support](#browser-support)
38
+ - [How It Works](#how-it-works)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
41
+
42
+ ## Quick Start
43
+
44
+ 1. **Install the package:**
45
+ ```bash
46
+ npm install next-style
47
+ ```
48
+
49
+ 2. **Configure PostCSS** (`postcss.config.js`):
50
+ ```js
51
+ export default {
52
+ plugins: {
53
+ "next-style/plugin": {},
54
+ },
55
+ }
56
+ ```
57
+
58
+ 3. **Import in `globals.css`:**
59
+ ```css
60
+ @import "next-style";
61
+ ```
62
+
63
+ 4. **Use in your components:**
64
+ ```tsx
65
+ import { css } from "next-style"
66
+
67
+ const button = css({
68
+ padding: "8px 16px",
69
+ borderRadius: "6px",
70
+ backgroundColor: "#7F77DD",
71
+ ":hover": { backgroundColor: "#534AB7" },
72
+ })
73
+
74
+ export function Button() {
75
+ return <button className={button}>Click me</button>
76
+ }
77
+ ```
78
+
79
+ That's it! No providers, wrappers, or complex configuration. All styles are extracted at build time.
80
+
81
+ ### App Router & Server Components
82
+
83
+ next-style works seamlessly with Next.js 13+ App Router and Server Components:
84
+
85
+ ```tsx
86
+ // app/components/Button.tsx (Server Component)
87
+ import { css } from "next-style"
88
+
89
+ const button = css({ /* styles */ })
90
+
91
+ export function Button() {
92
+ return <button className={button}>Click</button>
93
+ }
94
+ ```
95
+
96
+ ```tsx
97
+ // app/components/Counter.tsx (Client Component)
98
+ 'use client'
99
+
100
+ import { css } from "next-style"
101
+ import { useState } from "react"
102
+
103
+ const counter = css({ /* styles */ })
104
+
105
+ export function Counter() {
106
+ const [count, setCount] = useState(0)
107
+ return <div className={counter}>{count}</div>
108
+ }
109
+ ```
110
+
111
+ Both work identically — the `css()` call is evaluated at build time regardless of component type.
112
+
113
+ ## Example
114
+
19
115
  ```tsx
20
116
  import { css } from "next-style"
21
117
 
@@ -23,7 +119,10 @@ const button = css({
23
119
  padding: "8px 16px",
24
120
  borderRadius: "6px",
25
121
  backgroundColor: "#7F77DD",
122
+ cursor: "pointer",
123
+ transition: "background-color 0.2s",
26
124
  ":hover": { backgroundColor: "#534AB7" },
125
+ ":active": { transform: "scale(0.98)" },
27
126
  "@md": { padding: "10px 20px" },
28
127
  })
29
128
 
@@ -37,16 +136,26 @@ export function Button() {
37
136
  | | |
38
137
  |---|---|
39
138
  | ⚡ **Zero runtime** | All styles extracted at build time — 0 bytes of style JS shipped |
40
- | 🔷 **Turbopack ready** | Works out of the box with Next.js 15+ and Turbopack |
139
+ | 🔷 **Turbopack ready** | Works out of the box with Next.js 16+ and Turbopack |
41
140
  | 🔒 **Fully typed** | Every CSS property and value typed via [csstype](https://github.com/frenic/csstype) |
42
141
  | 📱 **Responsive first** | Shorthand breakpoints (`@sm` → `@2xl`) sorted mobile-first automatically |
43
142
  | ♻️ **Deduplication** | Identical style objects always hash to the same class name |
44
143
  | 🌍 **Global styles** | `global()` for resets, base typography, and third-party overrides |
45
144
  | 🎞️ **Keyframes** | Declare `@keyframes` inline next to the style that uses them |
46
145
  | 📦 **Tiny** | ~2 KB minified + gzipped |
146
+ | 🚀 **Fast builds** | Negligible build-time overhead — simple hashing and string concatenation |
147
+ | 🎨 **CSS-in-JS power** | All CSS features: pseudo-classes, container queries, `@supports`, `@layer`, CSS variables |
47
148
 
48
149
  Full support for pseudo-classes, pseudo-elements, media queries, container queries, `@supports`, `@layer`, and CSS variables.
49
150
 
151
+ ### Next.js Version Support
152
+
153
+ | Next.js Version | Supported |
154
+ |-----------------|-----------|
155
+ | 16+ | ✅ Full support with Turbopack |
156
+ | 15.x | ✅ Supported |
157
+ | < 15.0 | ❌ Not supported |
158
+
50
159
  ## Installation
51
160
 
52
161
  ```bash
@@ -60,7 +169,11 @@ pnpm add next-style
60
169
  bun add next-style
61
170
  ```
62
171
 
63
- > **Peer dependency:** `postcss >= 8.0.0` is required. Most Next.js projects already include it.
172
+ **Peer dependencies required:**
173
+ - `next >= 15.0.0`
174
+ - `postcss >= 8.0.0`
175
+
176
+ > Most Next.js projects already include PostCSS. If you're unsure, check your `package.json` or run `npm list postcss`.
64
177
 
65
178
  ## Setup
66
179
 
@@ -350,22 +463,329 @@ Styles are emitted in this order to ensure correct cascade:
350
463
 
351
464
  Because styles are extracted at build time, there is no style recalculation, no `<style>` injection, and no FOUC. The output is a single static CSS file.
352
465
 
466
+ ## Best Practices & Common Patterns
467
+
468
+ ### Reusable Style Objects
469
+
470
+ Create reusable style definitions by storing them in separate files:
471
+
472
+ ```tsx
473
+ // styles/components.ts
474
+ import { type CSSObject } from "next-style"
475
+
476
+ export const buttonBase: CSSObject = {
477
+ padding: "8px 16px",
478
+ borderRadius: "6px",
479
+ fontWeight: 500,
480
+ cursor: "pointer",
481
+ transition: "all 0.2s",
482
+ ":active": { transform: "scale(0.98)" },
483
+ }
484
+
485
+ export const buttonPrimary: CSSObject = {
486
+ ...buttonBase,
487
+ backgroundColor: "#7F77DD",
488
+ color: "white",
489
+ ":hover": { backgroundColor: "#534AB7" },
490
+ }
491
+ ```
492
+
493
+ ```tsx
494
+ // components/Button.tsx
495
+ import { css } from "next-style"
496
+ import { buttonPrimary } from "@/styles/components"
497
+
498
+ export function Button({ children }: { children: React.ReactNode }) {
499
+ return <button className={css(buttonPrimary)}>{children}</button>
500
+ }
501
+ ```
502
+
503
+ ### Design Tokens with CSS Variables
504
+
505
+ Centralize your design system using CSS variables:
506
+
507
+ ```tsx
508
+ // app/globals.ts
509
+ import { global } from "next-style"
510
+
511
+ global({
512
+ ":root": {
513
+ // Colors
514
+ "--color-primary": "#7F77DD",
515
+ "--color-secondary": "#534AB7",
516
+ "--color-text": "#1a1a1a",
517
+ "--color-bg": "#ffffff",
518
+
519
+ // Spacing
520
+ "--spacing-xs": "4px",
521
+ "--spacing-sm": "8px",
522
+ "--spacing-md": "16px",
523
+ "--spacing-lg": "24px",
524
+ "--spacing-xl": "32px",
525
+
526
+ // Radius
527
+ "--radius-sm": "4px",
528
+ "--radius-md": "8px",
529
+ "--radius-lg": "12px",
530
+ },
531
+ "body": {
532
+ backgroundColor: "var(--color-bg)",
533
+ color: "var(--color-text)",
534
+ },
535
+ })
536
+ ```
537
+
538
+ ```tsx
539
+ // Use in components
540
+ const card = css({
541
+ backgroundColor: "var(--color-bg)",
542
+ padding: "var(--spacing-md)",
543
+ borderRadius: "var(--radius-md)",
544
+ })
545
+ ```
546
+
547
+ ### Variant Patterns
548
+
549
+ Create component variants by conditionally merging styles:
550
+
551
+ ```tsx
552
+ import { css, type CSSObject } from "next-style"
553
+
554
+ interface ButtonProps {
555
+ variant?: "primary" | "secondary" | "ghost"
556
+ size?: "sm" | "md" | "lg"
557
+ children: React.ReactNode
558
+ }
559
+
560
+ export function Button({ variant = "primary", size = "md", children }: ButtonProps) {
561
+ const baseStyles: CSSObject = {
562
+ fontWeight: 500,
563
+ borderRadius: "6px",
564
+ cursor: "pointer",
565
+ transition: "all 0.2s",
566
+ }
567
+
568
+ const variantStyles: Record<string, CSSObject> = {
569
+ primary: {
570
+ backgroundColor: "var(--color-primary)",
571
+ color: "white",
572
+ ":hover": { backgroundColor: "var(--color-secondary)" },
573
+ },
574
+ secondary: {
575
+ backgroundColor: "var(--color-secondary)",
576
+ color: "white",
577
+ },
578
+ ghost: {
579
+ backgroundColor: "transparent",
580
+ color: "var(--color-primary)",
581
+ ":hover": { backgroundColor: "rgba(127, 119, 221, 0.1)" },
582
+ },
583
+ }
584
+
585
+ const sizeStyles: Record<string, CSSObject> = {
586
+ sm: { padding: "4px 12px", fontSize: "14px" },
587
+ md: { padding: "8px 16px", fontSize: "16px" },
588
+ lg: { padding: "12px 24px", fontSize: "18px" },
589
+ }
590
+
591
+ const className = css({
592
+ ...baseStyles,
593
+ ...variantStyles[variant],
594
+ ...sizeStyles[size],
595
+ })
596
+
597
+ return <button className={className}>{children}</button>
598
+ }
599
+ ```
600
+
601
+ ### Dark Mode Support
602
+
603
+ Use CSS variables to implement dark mode:
604
+
605
+ ```tsx
606
+ // app/globals.ts
607
+ import { global } from "next-style"
608
+
609
+ global({
610
+ ":root": {
611
+ "--bg-primary": "#ffffff",
612
+ "--text-primary": "#1a1a1a",
613
+ },
614
+
615
+ "[data-theme='dark']": {
616
+ "--bg-primary": "#1a1a1a",
617
+ "--text-primary": "#ffffff",
618
+ },
619
+ })
620
+ ```
621
+
622
+ ```tsx
623
+ // components/ThemeToggle.tsx
624
+ 'use client'
625
+
626
+ import { useEffect, useState } from "react"
627
+
628
+ export function ThemeToggle() {
629
+ const [theme, setTheme] = useState("light")
630
+
631
+ useEffect(() => {
632
+ const current = document.documentElement.getAttribute("data-theme") || "light"
633
+ setTheme(current)
634
+ }, [])
635
+
636
+ const toggle = () => {
637
+ const newTheme = theme === "light" ? "dark" : "light"
638
+ document.documentElement.setAttribute("data-theme", newTheme)
639
+ setTheme(newTheme)
640
+ }
641
+
642
+ return <button onClick={toggle}>Toggle Theme</button>
643
+ }
644
+ ```
645
+
353
646
  ## Troubleshooting
354
647
 
355
- **Styles not appearing**
648
+ ### Styles not appearing
649
+
650
+ 1. ✅ Check that `postcss.config.js` includes `"next-style/plugin": {}`
651
+ 2. ✅ Check that `@import "next-style";` is at the **top** of `globals.css` (before other styles)
652
+ 3. ✅ Verify you imported `css` from the correct package:
653
+ ```tsx
654
+ import { css } from "next-style" // ✓ correct
655
+ import { css } from "next-style/plugin" // ✗ incorrect
656
+ ```
657
+ 4. ✅ Restart the dev server after any PostCSS config change
658
+ 5. ✅ Clear the Next.js cache: `rm -rf .next` and restart
659
+
660
+ ### First cold boot shows no styles
661
+
662
+ On the very first build, no `css()` calls have been evaluated yet so the cache file doesn't exist. Run the dev server once to populate the cache. Subsequent builds will include all styles. This is expected behavior on cold starts.
663
+
664
+ **For production/CI builds:** The PostCSS plugin automatically scans your source files to extract styles if the cache file is missing, so cold builds on GitHub Actions, Vercel, or other CI systems work without extra setup.
665
+
666
+ ### Build errors after adding PostCSS plugins
667
+
668
+ Ensure `next-style` is listed **first** in the plugins object — it must run before other transformations:
669
+
670
+ ```js
671
+ // ✓ Correct order
672
+ export default {
673
+ plugins: {
674
+ "next-style/plugin": {},
675
+ autoprefixer: {},
676
+ "postcss-preset-env": {},
677
+ },
678
+ }
679
+
680
+ // ✗ Wrong order (next-style must be first)
681
+ export default {
682
+ plugins: {
683
+ autoprefixer: {},
684
+ "next-style/plugin": {},
685
+ },
686
+ }
687
+ ```
688
+
689
+ ### Styles working in dev but not in production
690
+
691
+ 1. Check that the PostCSS plugin is configured in your build environment
692
+ 2. Verify `NODE_ENV=production` is set during build
693
+ 3. Look for CSS minification errors in the build logs
694
+
695
+ ### Breakpoints not working
696
+
697
+ Ensure you're using the correct shorthand:
698
+
699
+ ```tsx
700
+ // ✓ Correct
701
+ css({ "@md": { fontSize: "20px" } })
702
+
703
+ // ✗ Incorrect (use @md, not md)
704
+ css({ "md": { fontSize: "20px" } })
705
+ ```
706
+
707
+ ### TypeScript errors
708
+
709
+ Install type definitions (usually automatic):
710
+
711
+ ```bash
712
+ npm install --save-dev @types/node
713
+ ```
714
+
715
+ If types are still missing, ensure `tsconfig.json` includes:
716
+
717
+ ```json
718
+ {
719
+ "compilerOptions": {
720
+ "types": ["node"],
721
+ "strict": true
722
+ }
723
+ }
724
+ ```
725
+
726
+ ### Performance concerns
727
+
728
+ next-style is extremely lightweight:
729
+ - **Runtime JS:** 0 bytes (all styles extracted at build time)
730
+ - **Build time:** Negligible (simple hashing and string operations)
731
+ - **CSS output:** Automatically deduplicated and minified in production
732
+
733
+ No additional optimizations are needed.
734
+
735
+ ## Browser Support
736
+
737
+ **next-style** outputs standard CSS and supports all modern browsers:
738
+
739
+ | Browser | Minimum Version |
740
+ |---------|-----------------|
741
+ | Chrome / Chromium | 90+ |
742
+ | Firefox | 87+ |
743
+ | Safari | 14+ |
744
+ | Edge | 90+ |
356
745
 
357
- 1. Check that `postcss.config.js` includes `"next-style/plugin": {}`
358
- 2. Check that `@import "next-style";` is at the top of `globals.css`
359
- 3. Restart the dev server after any PostCSS config change
360
- 4. Clear the Next.js cache: `rm -rf .next` and restart
746
+ Older browsers are supported through standard CSS features used. All advanced CSS (container queries, `@supports`, etc.) gracefully degrade in unsupported browsers.
747
+
748
+ ## How It Works
749
+
750
+ ### Architecture Overview
751
+
752
+ 1. **Build Time (Development & Production)**
753
+ - Your `css()` and `global()` calls are evaluated
754
+ - Each unique style object is hashed and assigned a class name (e.g., `ns-abc123`)
755
+ - Compiled CSS is written to `node_modules/.cache/next-style/styles.css`
756
+
757
+ 2. **PostCSS Processing**
758
+ - The PostCSS plugin reads the cache file
759
+ - Replaces `@import "next-style"` in your `globals.css` with the collected CSS
760
+ - Minifies CSS in production using `cssnano`
761
+
762
+ 3. **Runtime (Production)**
763
+ - Zero JavaScript overhead — styles are already in the static CSS file
764
+ - No style injection, no hydration cost, no runtime recalculation
765
+
766
+ ### Deduplication
767
+
768
+ Identical style objects always hash to the same class name:
769
+
770
+ ```tsx
771
+ // These produce the same class name and CSS rule
772
+ const style1 = css({ color: "red", fontSize: "16px" })
773
+ const style2 = css({ color: "red", fontSize: "16px" })
774
+
775
+ // style1 === style2 → true
776
+ // Output: only one `.ns-xyz { color: red; font-size: 16px; }` rule
777
+ ```
361
778
 
362
- **First cold boot shows no styles**
779
+ ### File-Based Bridge
363
780
 
364
- On the very first build, no `css()` calls have been evaluated yet so the cache file doesn't exist. Run the dev server once to populate the cache, then styles will appear on reload. This is expected behaviour on cold starts.
781
+ Since PostCSS runs in a separate process, next-style uses a file-based bridge:
782
+ - Every `css()` / `global()` call writes compiled CSS to a cache file
783
+ - The PostCSS plugin reads that file and injects it into your CSS
784
+ - This enables dev-server hot-reloading and zero-setup cold builds
365
785
 
366
- **Build errors after adding PostCSS plugins**
786
+ ## Contributing
367
787
 
368
- Ensure next-style is listed **first** in the plugins object it must run before any other transformations.
788
+ Contributions are welcome! Please feel free to submit a Pull Request.
369
789
 
370
790
  ## License
371
791
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-style",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Zero-Runtime CSS-in-JS for Next.js + Turbopack",
5
5
  "type": "module",
6
6
  "repository": {
@@ -47,8 +47,8 @@
47
47
  "csstype": "latest"
48
48
  },
49
49
  "peerDependencies": {
50
- "next": ">=16.0.0",
51
- "postcss": ">=8.0.0"
50
+ "next": ">=15",
51
+ "postcss": ">=8"
52
52
  },
53
53
  "keywords": [
54
54
  "css-in-js",