Skip to content

Commit afc9655

Browse files
committed
update: updated readme, added feat to init next.js layout
1 parent d58ec1a commit afc9655

File tree

8 files changed

+248
-139
lines changed

8 files changed

+248
-139
lines changed

README.md

Lines changed: 127 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,205 @@
1-
# React Zero-UI (Beta)
1+
# React ZeroUI (Beta)
22

3-
**Instant UI state updates. ZERO React re-renders. ZERO runtime overhead.** Update the UI instantly, manage global UI state from anywhere. No prop drilling. get started with one command in your existing React app. `npx create-zero-ui`
3+
**Instant UI state updates. ZERO React rerenders. <1 KB runtime.**
44

5-
[![npm version](https://img.shields.io/npm/v/@austinserb/react-zero-ui)](https://www.npmjs.com/package/@austinserb/react-zero-ui)
6-
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7-
![CI](https://github.com/austin1serb/react-zero-ui/actions/workflows/ci.yml/badge.svg?branch=main)
5+
Pre‑render your UI once, flip a `data-*` attribute to update — that's it.
6+
7+
<a href="https://www.npmjs.com/package/@austinserb/react-zero-ui" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/bundlephobia/min/@austinserb/[email protected]" alt="npm version" />
8+
</a>
89

9-
## Why Zero-UI?
10+
<a href="https://www.npmjs.com/package/@austinserb/react-zero-ui" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/npm/v/@austinserb/react-zero-ui" alt="npm version" /></a> <a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a> ![CI](https://github.com/austin1serb/react-zero-ui/actions/workflows/ci.yml/badge.svg?branch=main)
1011

11-
Traditional React state triggers RE-renders for every UI change. Switching themes? That's every component RE-rendering. Opening a menu? Same story.
12+
---
13+
14+
## 🚀 Live Demo
1215

13-
**Enter "PRE-rendering"**
14-
Zero-UI bypasses React entirely for pure UI state. Instead of re-renders, it:
16+
| Example | Link | What it shows | Link to Code |
17+
| --------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |------------------------------------------------------------------------------ |
18+
| Interactive menu with render tracker | <a href="https://react-zero-ui.vercel.app/" target="_blank" rel="noopener noreferrer"><strong>Main Demo↗</strong></a> | Compare Zero‑UI vs. React side‑by‑side while toggling a menu. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo" target="_blank" rel="noopener noreferrer">Github</a> |
19+
| React benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/react" target="_blank" rel="noopener noreferrer"><strong>React 10k↗</strong></a> | How long the traditional React render path takes. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/react" target="_blank" rel="noopener noreferrer">Github</a> |
20+
| Zero‑UI benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/zero-ui" target="_blank" rel="noopener noreferrer"><strong style="text-align: nowrap;">Zero‑UI 10k↗</strong></a> | Identical DOM, but powered by Zero‑UI's `data-*` switch. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/zero-ui" target="_blank" rel="noopener noreferrer">Github</a> |
21+
22+
---
1523

16-
- Pre-renders CSS styles and keeps them in the DOM
17-
- For state changes it flips a `data-*` attribute key.
18-
- Thats it.
24+
## 🧐 Why Zero‑UI?
1925

20-
**Result:** UI updates that are 10-50x faster.
26+
Every `setState` in React triggers the full VDOM → Diff → Reconciliation → Paint pipeline. For *pure UI state* (themes, menus, toggles) that work is wasted.
2127

22-
### Performance Comparison
28+
**Zero‑UI introduces "*PRE‑rendering*":**
2329

24-
Apple M1
30+
1. Tailwind variants for every state are **generated at build‑time**.
31+
2. The app **pre‑renders once**.
32+
3. Runtime state changes only **flip a `data-*` attribute on `<body>`**.
2533

26-
| Nodes | React State | Zero-UI | Improvement |
27-
| ------ | ----------- | ------- | ----------- |
28-
| 1,000 | ~50ms | ~5ms | 10x faster |
29-
| 5,000 | ~180ms | ~15ms | 12x faster |
30-
| 10,000 | ~400ms | ~20ms | 20x faster |
34+
Result → **5-10× faster visual updates** with **ZERO additional bundle cost**.
3135

32-
## Quick Start
36+
### 📊 Micro‑benchmarks (Apple M1)
3337

34-
**Prerequisites:** Tailwind v4 must be initialized
38+
| Nodes updated | React state | Zero‑UI | Speed‑up |
39+
| ------------- | ----------- | ------- | -------- |
40+
| 10,000 | \~50 ms | \~5 ms | **10×** |
41+
| 25,000 | \~180 ms | \~15 ms | **12×** |
42+
| 50,000 | \~300 ms | \~20 ms | **15×** |
43+
44+
Re‑run these numbers yourself via the links above.
45+
46+
---
3547

36-
CLI script - in your existing Next or Vite App's root:
48+
## ⚡️ Quick Start
49+
50+
> **Prerequisite:** Tailwind CSS v4 must already be initialized in your project.
3751
3852
```bash
53+
# Inside an existing *Next.js (App Router)* or *Vite* repo
3954
npx create-zero-ui
4055
```
4156

42-
## Manual Installation
57+
That's it — the CLI patch‑installs the required Babel & PostCSS plugins and updates `configs` for you.
58+
59+
### Manual Install
4360

4461
```bash
4562
npm install @austinserb/react-zero-ui
4663
```
4764

48-
### Setup
65+
Then follow **Setup →** for your bundler.
66+
67+
---
4968

50-
**Prerequisites:** Tailwind v4 must be initialized. [tailwind set up ]("tailwind.com")
69+
## 🔧 Setup
5170

52-
#### Vite
71+
### Vite
5372

5473
```js
5574
// vite.config.*
5675
import { zeroUIPlugin } from '@austinserb/react-zero-ui/vite';
5776

5877
export default {
59-
//*REMOVE TAILWIND PLUGIN* Zero-UI extends tailwinds plug-in
60-
plugins: [zeroUIPlugin()],
78+
// ❗️Remove the default `tailwindcss()` plugin — ZeroUI extends it internally
79+
plugins: [zeroUIPlugin()],
6180
};
6281
```
6382

64-
#### Next.js
83+
### Next.js (App Router)
6584

66-
#### 1. Spread bodyAttributes on `<body>` in Layout
85+
1. **Spread `bodyAttributes` on `<body>`** in your root layout.
6786

68-
```jsx
69-
import { bodyAttributes } from '@zero-ui/attributes';
70-
//or
71-
import { bodyAttributes } from '../.zero-ui/attributes';
72-
73-
export default function RootLayout({ children }) {
74-
return (
75-
<html>
76-
<body {...bodyAttributes}>{children}</body>
77-
</html>
78-
);
79-
}
80-
```
87+
```tsx
88+
// app/layout.tsx
89+
import { bodyAttributes } from '@austinserb/react-zero-ui/attributes';
90+
// or: import { bodyAttributes } from '../.zero-ui/attributes';
8191

82-
#### 2. Add PostCSS Plugin
92+
export default function RootLayout({ children }) {
93+
return (
94+
<html lang="en">
95+
<body {...bodyAttributes}>{children}</body>
96+
</html>
97+
);
98+
}
99+
```
83100

84-
```js
85-
// postcss.config.js
86-
module.exports = {
87-
plugins: {
88-
['@austinserb/react-zero-ui/postcss']
89-
//*tailwindcss MUST come AFTER Zero-UI
90-
['@tailwindcss']
91-
}
92-
}
93-
```
101+
2. **Add the PostCSS plugin (must come *before* Tailwind).**
94102

95-
## Usage
103+
```js
104+
// postcss.config.js
105+
module.exports = {
106+
plugins: {
107+
'@austinserb/react-zero-ui/postcss': {},
108+
tailwindcss: {},
109+
},
110+
};
111+
```
96112

97-
**Basic Theme Switching**
113+
---
114+
115+
## 🏄‍♂️ Usage
116+
117+
### Theme toggle example
98118

99119
```tsx
100120
import { useUI } from '@austinserb/react-zero-ui';
101121

102-
function ThemeToggle() {
103-
const [, setTheme] = useUI<"light | dark">('theme', 'light');
122+
export function ThemeToggle() {
123+
const [, setTheme] = useUI<'light' | 'dark'>('theme', 'light');
104124

105-
<button onClick={() => setTheme('dark')}>
106-
Switch Theme
107-
</button>
125+
return (
126+
<button onClick={() => setTheme('dark')}>Switch to dark</button>
127+
);
128+
}
108129
```
109130

110-
**Consume the state in any component with tailwind!**
131+
Consume the state anywhere with Tailwind variants:
111132

112133
```jsx
113-
className = 'theme-light:bg-white theme-dark:bg-black';
134+
<div className="theme-light:bg-white theme-dark:bg-black" />
114135
```
115136

116-
**Mutate the state in any component!**
137+
---
117138

118-
```jsx
139+
## 🛠 API
119140

120-
function UnrelatedPage()
121-
const [, setTheme] = useUI('theme', 'light');
141+
### `useUI(key, defaultValue)`
122142

123-
<button onClick={setTheme("dark")}>
124-
</button>
143+
```ts
144+
const [staleValue, setValue] = useUI<'open' | 'closed'>(
145+
'sidebar',
146+
'closed',
147+
);
125148
```
126149

127-
**Use with complex Tailwind Variants**
150+
* `key` → becomes `data-{key}` on `<body>`.
151+
* `defaultValue` → optional, prevents FOUC.
152+
* **Note:** the returned `staleValue` does **not** update (`useUI` is write‑only).
153+
154+
### Tailwind variants
128155

129156
```jsx
130-
clasName = 'md:theme-dark:bg-black md:peer-checked:theme-light:hidden';
157+
<div className="sidebar-open:translate-x-0 sidebar-closed:-translate-x-full" />
131158
```
132159

133-
## How It Works
134-
135-
1. **State Store**: The `useUI` hook writes to `data-*` attributes on the `<body>` tag instead of React state
136-
137-
```html
138-
<body
139-
data-theme="dark"
140-
data-accent="blue"
141-
data-sidebar="open"></body>
142-
```
143-
144-
2. **Babel Transform**: Automatically detects all `useUI` variants in your code during build
145-
146-
3. **PostCSS Plugin**: Generates Tailwind variant classes for every detected state
147-
148-
```css
149-
.theme-dark\:bg-gray-900 {
150-
background-color: rgb(17 24 39);
151-
}
152-
```
153-
154-
4. **Instant Updates**: When you call a setter, only the data attribute changes. The browser doesn't have to create a VDOM, and compare it to the current HTML tree (Diffing), and then determine if an update is needed (Reconciliation), and then apply that update (Re-render) by injecting into the html tree. Then the browser has to compile the new injected Html and Css, then compute style, THEN paint the pixels.
155-
156-
## API
160+
Any `data-{key}="{value}"` pair becomes a variant: `{key}-{value}:`.
157161

158-
### `useUI(key: string, SSRValue: string)`
159-
160-
Returns a tuple similar to `useState`, but the first value is intentionally stale.
162+
---
161163

162-
```jsx
163-
const [staleValue, setValue] = useUI('theme', 'light');
164-
```
164+
## 🧬 How it works
165165

166-
- `key`: The UI state key (becomes `data-{key}` on body)
167-
- `defaultValue`: Initial value if not set
168-
- Returns: `[staleValue, setValue]`
166+
1. **`useUI`** → writes to `data-*` attributes on `<body>`.
167+
2. **Babel plugin** → scans code, finds every `key/value`, injects them into **PostCSS**.
168+
3. **PostCSS plugin** → generates static Tailwind classes **at build‑time**.
169+
4. **Runtime** → changing state only touches the attribute — no VDOM, no reconciliation, no re‑paint.
169170

170-
**note:** normaly used as const `const [,setValue]=useUI()` to denote that the value is stale and will not update
171+
---
171172

172-
### Tailwind Variants
173+
## ✅ Features
173174

174-
Use the pattern `{key}-{value}:` as a Tailwind variant:
175+
* **Zero React re‑renders** for UI‑only state.
176+
* **Global setters** — call from any component or util.
177+
* **Tiny**: < 1 KB gzipped runtime.
178+
* **TypeScript‑first**.
179+
* **SSR‑friendly** (Next.js & Vite SSR).
180+
* **Framework‑agnostic CSS** — generated classes work in plain HTML / Vue / Svelte as well.
175181

176-
```jsx
177-
<div className="theme-light:bg-white theme-dark:bg-black" />
178-
<div className="accent-red:text-red-500 accent-blue:text-blue-500" />
179-
<div className="sidebar-open:translate-x-0 sidebar-closed:-translate-x-full" />
180-
```
182+
---
181183

182-
## Features
184+
## 🏗 Best Practices
183185

184-
- ✅ **Zero React re-renders** for UI state changes
185-
- ✅ **No Context providers** needed
186-
- ✅ **Works globally** - call setters from anywhere
187-
- ✅ **TypeScript support** out of the box
188-
- ✅ **SSR compatible** with Next.js
189-
- ✅ **Tiny bundle size** (954bytes)
190-
- ✅ **Framework agnostic CSS** - the generated CSS works everywhere
186+
1. **UI state only** → themes, layout toggles, feature flags.
187+
2. **Business logic stays in React** → fetching, data mutation, etc.
188+
3. **Kebab‑case keys** → e.g. `sidebar-open`.
189+
4. **Provide defaults** to avoid Flash‑Of‑Unstyled‑Content.
191190

192-
## Best Practices
191+
---
193192

194-
1. **Use for UI-only state**: Themes, sidebar states, UI flags
195-
2. **Not for business logic**: Keep using React state for data that affects logic
196-
3. **Consistent naming**: Prefer kebab-case for keys (`sidebar-state`, not `sidebarState`)
197-
4. **Default values**: Always provide default value for
198-
`useUI('key', 'value')` to avoid FOUC.
193+
## 🤝 Contributing
199194

200-
## Contributing
195+
PRs & issues welcome! Please read the [Contributing Guide](CONTRIBUTING.md).
201196

202-
We love contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
197+
---
203198

204-
## License
199+
## 📜 License
205200

206-
MIT © Austin Serb
201+
[MIT](LICENSE) © Austin Serb
207202

208203
---
209204

210-
Built with ❤️ for the React community. If Zero-UI helps your app feel snappier, consider [starring the repo](https://github.com/austinserb/zero-ui)!
205+
Built with ❤️ for the React community. If ZeroUI makes your app feel snappier, please ⭐️ the repo!
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/* AUTO-GENERATED - DO NOT EDIT */
2-
export declare const bodyAttributes: { 'data-theme': 'dark' | 'light'; 'data-theme-2': 'dark' | 'light'; 'data-theme-three': 'dark' | 'light' };
2+
export declare const bodyAttributes: {
3+
"data-theme": "dark" | "light";
4+
"data-theme-2": "dark" | "light";
5+
"data-theme-three": "dark" | "light";
6+
};
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/* AUTO-GENERATED - DO NOT EDIT */
2-
export const bodyAttributes = { 'data-theme': 'light', 'data-theme-2': 'light', 'data-theme-three': 'light' };
2+
export const bodyAttributes = {
3+
"data-theme": "light",
4+
"data-theme-2": "light",
5+
"data-theme-three": "light"
6+
};
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import './globals.css';
2-
import { bodyAttributes } from '@zero-ui/attributes';
32

43
export default function RootLayout({ children }) {
54
return (
65
<html lang="en">
7-
<body {...bodyAttributes}>{children}</body>
6+
<body>{children}</body>
87
</html>
98
);
109
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/* AUTO-GENERATED - DO NOT EDIT */
2-
export declare const bodyAttributes: { 'data-theme': 'dark' | 'light'; 'data-theme-2': 'dark' | 'light'; 'data-theme-three': 'dark' | 'light' };
2+
export declare const bodyAttributes: {
3+
"data-theme": "dark" | "light";
4+
"data-theme-2": "dark" | "light";
5+
"data-theme-three": "dark" | "light";
6+
};
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/* AUTO-GENERATED - DO NOT EDIT */
2-
export const bodyAttributes = { 'data-theme': 'light', 'data-theme-2': 'light', 'data-theme-three': 'light' };
2+
export const bodyAttributes = {
3+
"data-theme": "light",
4+
"data-theme-2": "light",
5+
"data-theme-three": "light"
6+
};

packages/core/src/cli/postInstall.cjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// scripts/postInstall.cjs
22
const { processVariants, generateAttributesFile, patchConfigAlias, patchPostcssConfig, patchViteConfig, hasViteConfig } = require('../postcss/helpers.cjs');
3+
const { patchNextBodyTag } = require('../postcss/ast.cjs');
34

45
async function runZeroUiInit() {
56
try {
@@ -25,6 +26,10 @@ async function runZeroUiInit() {
2526

2627
console.log(`[Zero-UI] ✅ Initialized with ${finalVariants.length} variants from ${sourceFiles.length} files`);
2728

29+
if (!hasViteConfig()) {
30+
await patchNextBodyTag();
31+
}
32+
2833
if (finalVariants.length === 0) {
2934
console.log('[Zero-UI] ℹ️ No useUI hooks found yet. Files will be updated when you add them.');
3035
}

0 commit comments

Comments
 (0)