@expo-forge/forge-ui 0.0.47
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/README.md +385 -0
- package/babel-plugin.js +3137 -0
- package/bin/forge.js +52 -0
- package/bin/install.js +87 -0
- package/bin/native/forge-win-x64.exe +0 -0
- package/bin/prepare-publish.js +77 -0
- package/package.json +77 -0
- package/theme.js +379 -0
- package/types/index.d.ts +140 -0
- package/types/theme.d.ts +93 -0
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# ForgeUI
|
|
2
|
+
|
|
3
|
+
ForgeUI is a utility-first styling engine for:
|
|
4
|
+
|
|
5
|
+
- Web apps (Forge scans classes and generates CSS)
|
|
6
|
+
- React Native / Expo apps (Forge uses a Babel plugin, no CSS file)
|
|
7
|
+
|
|
8
|
+
> **v0.0.10** — Babel plugin now automatically injects `useTheme()` from @expo-forge/forge-ui into all components with `dark:` classes, ensuring they automatically re-render across all pages when theme changes (no manual hook call needed).
|
|
9
|
+
|
|
10
|
+
> **v0.0.9** — Theme preference is now persisted across app restarts using AsyncStorage (React Native) and localStorage (web). The theme will remember your toggle choice.
|
|
11
|
+
|
|
12
|
+
> **v0.0.6** — Native theme detection now uses useColorScheme as the primary source, which fixes Expo system theme detection.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
1. Install:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @expo-forge/forge-ui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Choose setup path:
|
|
23
|
+
|
|
24
|
+
- Web: see [Web Setup](#web-setup)
|
|
25
|
+
- React Native / Expo: see [React Native / Expo Setup](#react-native--expo-setup)
|
|
26
|
+
|
|
27
|
+
## Web Setup
|
|
28
|
+
|
|
29
|
+
### Step 1: Create forge.config.toml
|
|
30
|
+
|
|
31
|
+
Create this file in your project root:
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
[general]
|
|
35
|
+
input = [
|
|
36
|
+
"src/**/*.html",
|
|
37
|
+
"src/**/*.jsx",
|
|
38
|
+
"src/**/*.tsx",
|
|
39
|
+
"src/**/*.vue",
|
|
40
|
+
"src/**/*.svelte",
|
|
41
|
+
]
|
|
42
|
+
# CSS only (JS goes next to it):
|
|
43
|
+
output = "dist/forge.css"
|
|
44
|
+
# — or — separate dirs for CSS and JS:
|
|
45
|
+
# output = "public/css, public/js"
|
|
46
|
+
|
|
47
|
+
[options]
|
|
48
|
+
important = false
|
|
49
|
+
minify = false
|
|
50
|
+
|
|
51
|
+
[components]
|
|
52
|
+
enabled = true
|
|
53
|
+
js = true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> When `components.enabled = true`, Forge only emits CSS for components whose class names appear in your scanned source files. Unused component blocks (button, card, modal, etc.) produce no output.
|
|
57
|
+
|
|
58
|
+
### Step 2: Build or watch
|
|
59
|
+
|
|
60
|
+
One-time build:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx forge build
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Dev watch mode:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx forge dev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 3: Load generated files
|
|
73
|
+
|
|
74
|
+
Required CSS:
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<link rel="stylesheet" href="/forge.css" />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Optional runtime (needed for interactive Forge components and dev auto-refresh):
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<script src="/forge-runtime.js" defer></script>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Web Setup By Platform
|
|
87
|
+
|
|
88
|
+
### Plain HTML
|
|
89
|
+
|
|
90
|
+
1. Include `src/**/*.html` in `general.input`.
|
|
91
|
+
2. Run `npx forge build` or `npx forge dev`.
|
|
92
|
+
3. Load generated files:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<link rel="stylesheet" href="/dist/forge.css" />
|
|
96
|
+
<script src="/dist/forge-runtime.js" defer></script>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### React / Vite
|
|
100
|
+
|
|
101
|
+
1. Include `src/**/*.jsx` and/or `src/**/*.tsx` in `general.input`.
|
|
102
|
+
2. Run `npx forge dev`.
|
|
103
|
+
3. Import generated CSS in app entry file:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import "../dist/forge.css";
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If using interactive Forge components, include `forge-runtime.js` in `index.html`.
|
|
110
|
+
|
|
111
|
+
### Next.js
|
|
112
|
+
|
|
113
|
+
Use this config:
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[general]
|
|
117
|
+
input = ["app/**/*.tsx", "app/**/*.ts", "components/**/*.tsx"]
|
|
118
|
+
output = "public/forge.css"
|
|
119
|
+
|
|
120
|
+
[options]
|
|
121
|
+
important = false
|
|
122
|
+
minify = false
|
|
123
|
+
|
|
124
|
+
[components]
|
|
125
|
+
enabled = true
|
|
126
|
+
js = true
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Load in root layout:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<head>
|
|
133
|
+
<link rel="stylesheet" href="/forge.css" />
|
|
134
|
+
</head>
|
|
135
|
+
<body>
|
|
136
|
+
{children}
|
|
137
|
+
<script src="/forge-runtime.js" defer></script>
|
|
138
|
+
</body>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Vue
|
|
142
|
+
|
|
143
|
+
1. Include `src/**/*.vue` in `general.input`.
|
|
144
|
+
2. Run `npx forge dev`.
|
|
145
|
+
3. Import generated CSS in app entry file:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import "../dist/forge.css";
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Svelte
|
|
152
|
+
|
|
153
|
+
1. Include `src/**/*.svelte` in `general.input`.
|
|
154
|
+
2. Run `npx forge dev`.
|
|
155
|
+
3. Import generated CSS in root entry/layout file:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import "../dist/forge.css";
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## React Native / Expo Setup
|
|
162
|
+
|
|
163
|
+
### Important
|
|
164
|
+
|
|
165
|
+
React Native does not use:
|
|
166
|
+
|
|
167
|
+
- forge.config.toml
|
|
168
|
+
- forge.css
|
|
169
|
+
- forge-runtime.js
|
|
170
|
+
|
|
171
|
+
### Step 1: Initialize
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npx forge init --native
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Step 2: Enable Babel plugin
|
|
178
|
+
|
|
179
|
+
Use:
|
|
180
|
+
|
|
181
|
+
```txt
|
|
182
|
+
@expo-forge/forge-ui/babel-plugin
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Step 3: Wrap app with ThemeProvider
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import { ThemeProvider } from "@expo-forge/forge-ui";
|
|
189
|
+
|
|
190
|
+
export default function RootLayout() {
|
|
191
|
+
return <ThemeProvider>{/* app */}</ThemeProvider>;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Step 4: Start Expo
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npx expo start
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Required app.json settings
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"expo": {
|
|
206
|
+
"userInterfaceStyle": "automatic",
|
|
207
|
+
"experiments": {
|
|
208
|
+
"reactCompiler": false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Step 5: Use Forge color palettes in native
|
|
215
|
+
|
|
216
|
+
You can now use all Forge color palettes in your native components:
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { useColors } from "@expo-forge/forge-ui";
|
|
220
|
+
|
|
221
|
+
export default function MyComponent() {
|
|
222
|
+
const colors = useColors();
|
|
223
|
+
return <View style={{ backgroundColor: colors.blue[5] }} />;
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Dynamic Classes (Web)
|
|
228
|
+
|
|
229
|
+
Literal class strings are supported:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const card = {
|
|
233
|
+
color: "bg-gradient-to-r from-blue-1 to-green-1",
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
For fully runtime-generated class names (example: `bg-${tone}-5`), use:
|
|
238
|
+
|
|
239
|
+
1. `general.safelist`
|
|
240
|
+
2. `general.inject_colors = true` (larger CSS output)
|
|
241
|
+
|
|
242
|
+
## Output Optimizations
|
|
243
|
+
|
|
244
|
+
Forge automatically tree-shakes generated CSS — no extra config needed.
|
|
245
|
+
|
|
246
|
+
### Selective `:root` color variables
|
|
247
|
+
|
|
248
|
+
The `:root` block only contains variables for color palettes you actually use. A project that only uses `bg-blue-5 text-red-3` gets `--blue-*` and `--red-*` variables only — not all 22 palettes.
|
|
249
|
+
|
|
250
|
+
### Lazy component CSS
|
|
251
|
+
|
|
252
|
+
When `components.enabled = true`, Forge only emits CSS for components whose class names appear in scanned files:
|
|
253
|
+
|
|
254
|
+
| Component block | Emitted when you use… |
|
|
255
|
+
| --------------- | --------------------------------------------------------------------------------------------------------- |
|
|
256
|
+
| Button | `button`, `button-primary`, `button-secondary`, `button-ghost`, `button-danger`, `button-sm`, `button-lg` |
|
|
257
|
+
| Card | `card`, `card-header`, `card-body`, `card-footer` |
|
|
258
|
+
| Modal | `modal-content`, `data-fg-modal`, `data-fg-toggle` |
|
|
259
|
+
| Utility | `avatar`, `dot`, `spark`, `bar`, `table-wrap` |
|
|
260
|
+
| Visibility | `hidden` |
|
|
261
|
+
|
|
262
|
+
## Advanced Utilities
|
|
263
|
+
|
|
264
|
+
### Fluid text sizing
|
|
265
|
+
|
|
266
|
+
Use `text-clamp-*` for responsive font sizes with CSS `clamp(...)`:
|
|
267
|
+
|
|
268
|
+
```html
|
|
269
|
+
<h1 class="text-clamp-4xl">Fluid heading</h1>
|
|
270
|
+
<p class="text-clamp-sm">Fluid body text</p>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Supported presets: `sx`/`xs`, `sm`, `base`, `lg`, `xl`, `2xl` ... `9xl`.
|
|
274
|
+
|
|
275
|
+
### Class-driven at-rules
|
|
276
|
+
|
|
277
|
+
Forge can emit custom at-rules directly from class tokens:
|
|
278
|
+
|
|
279
|
+
```html
|
|
280
|
+
<div class="media-[(min-width:900px)]:text-clamp-xl">Custom media query</div>
|
|
281
|
+
|
|
282
|
+
<div class="layer-[components]:rounded-lg">Wrapped in @layer components</div>
|
|
283
|
+
|
|
284
|
+
<div
|
|
285
|
+
class="keyframes-[wiggle:0%{transform:rotate(-3deg)}100%{transform:rotate(3deg)}] animate-[wiggle_1.2s_ease-in-out_infinite]"
|
|
286
|
+
>
|
|
287
|
+
Custom keyframes + animation
|
|
288
|
+
</div>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Notes:
|
|
292
|
+
|
|
293
|
+
- Replace spaces with underscores inside bracket syntax when needed.
|
|
294
|
+
- These tokens work with existing variants like `md:`, `hover:`, and `dark:`.
|
|
295
|
+
|
|
296
|
+
## Config Reference
|
|
297
|
+
|
|
298
|
+
| Key | Type | Default | Purpose |
|
|
299
|
+
| ------------------------------- | ---------- | --------------------- | ------------------------------------------------ |
|
|
300
|
+
| `general.input` | `string[]` | common `src/**` globs | Files to scan for classes |
|
|
301
|
+
| `general.output` | `string` | `forge.css` | CSS output (and optional JS output) — see below |
|
|
302
|
+
| `general.safelist` | `string[]` | `[]` | Always include these classes |
|
|
303
|
+
| `general.inject_colors` | `bool` | `false` | Pre-generate full color and gradient utility set |
|
|
304
|
+
| `general.inject_color_variants` | `string[]` | `[]` | Variant prefixes for injected classes |
|
|
305
|
+
| `options.important` | `bool` | `false` | Append `!important` to declarations |
|
|
306
|
+
| `options.minify` | `bool` | `false` | Minify output CSS |
|
|
307
|
+
| `components.enabled` | `bool` | `true` | Include built-in component CSS |
|
|
308
|
+
| `components.js` | `bool` | `true` | Emit forge-runtime.js |
|
|
309
|
+
|
|
310
|
+
### output format
|
|
311
|
+
|
|
312
|
+
```toml
|
|
313
|
+
# CSS only — JS written next to it
|
|
314
|
+
output = "dist/forge.css"
|
|
315
|
+
|
|
316
|
+
# CSS in one dir, JS in another
|
|
317
|
+
output = "public/css, public/js"
|
|
318
|
+
|
|
319
|
+
# Bare directories — filenames appended automatically
|
|
320
|
+
# public/css/forge.css + public/js/forge-runtime.js
|
|
321
|
+
output = "public/css, public/js"
|
|
322
|
+
|
|
323
|
+
# Explicit filenames also work
|
|
324
|
+
output = "public/css/styles.css, public/js/runtime.js"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Common Mistakes
|
|
328
|
+
|
|
329
|
+
### No styles applied
|
|
330
|
+
|
|
331
|
+
- Run `npx forge build`
|
|
332
|
+
- Confirm file exists at `general.output`
|
|
333
|
+
- Confirm app loads the same file path
|
|
334
|
+
|
|
335
|
+
### Some classes missing
|
|
336
|
+
|
|
337
|
+
- Your file globs likely miss some folders
|
|
338
|
+
- Add those folders to `general.input`
|
|
339
|
+
|
|
340
|
+
### Browser does not update during dev
|
|
341
|
+
|
|
342
|
+
- Use `npx forge dev`
|
|
343
|
+
- Ensure `/forge-runtime.js` is loaded
|
|
344
|
+
|
|
345
|
+
### React Native styles do not apply
|
|
346
|
+
|
|
347
|
+
- Run `npx forge init --native`
|
|
348
|
+
- Ensure Babel plugin is configured
|
|
349
|
+
- Restart Metro
|
|
350
|
+
|
|
351
|
+
## Command Summary
|
|
352
|
+
|
|
353
|
+
Same commands on Windows, macOS, and Linux:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
npx forge build
|
|
357
|
+
npx forge dev
|
|
358
|
+
npx forge init --native
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Repository
|
|
362
|
+
|
|
363
|
+
https://github.com/semsakadanupol/forge-ui
|
|
364
|
+
|
|
365
|
+
## Web-like first: and last: pseudo-classes in React Native
|
|
366
|
+
|
|
367
|
+
With Forge-UI's experimental Babel plugin support, you can use `first:` and `last:` pseudo-classes directly in `className` inside a `.map` loop—just like on the web!
|
|
368
|
+
|
|
369
|
+
**Example:**
|
|
370
|
+
|
|
371
|
+
```jsx
|
|
372
|
+
{
|
|
373
|
+
items.map((item) => (
|
|
374
|
+
<View key={item.id} className="p-4 first:bg-red-1 last:rounded-b-xl">
|
|
375
|
+
{item.text}
|
|
376
|
+
</View>
|
|
377
|
+
));
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- `first:bg-red-1` will only apply to the first child.
|
|
382
|
+
- `last:rounded-b-xl` will only apply to the last child.
|
|
383
|
+
- No manual index checks or helpers needed—just write your classes as you would for web.
|
|
384
|
+
|
|
385
|
+
This works for any dynamic array mapping in JSX in React Native.
|