start-vibing-stacks 1.9.2 → 2.0.1
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/dist/ui.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Start Vibing Stacks — Terminal UI
|
|
3
3
|
*/
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
const VERSION = '
|
|
5
|
+
const VERSION = '2.0.1';
|
|
6
6
|
const gradient = (text) => {
|
|
7
7
|
const colors = [chalk.hex('#FF6B6B'), chalk.hex('#FF8E53'), chalk.hex('#FFBD2E'), chalk.hex('#48BB78'), chalk.hex('#4299E1'), chalk.hex('#9F7AEA')];
|
|
8
8
|
return text.split('').map((c, i) => colors[i % colors.length](c)).join('');
|
package/package.json
CHANGED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Preline UI — Component & Theme System for TailwindCSS 4
|
|
2
|
+
|
|
3
|
+
**ALWAYS invoke when using Preline components, creating themes, or customizing design tokens.**
|
|
4
|
+
|
|
5
|
+
## What is Preline
|
|
6
|
+
|
|
7
|
+
Preline is a **semantic token-based design system** built on TailwindCSS. It provides:
|
|
8
|
+
- 220+ CSS tokens for full UI consistency
|
|
9
|
+
- Pre-built components (navbar, sidebar, card, dropdown, overlay, etc.)
|
|
10
|
+
- Theme generator for custom color schemes
|
|
11
|
+
- Light + dark mode via `data-theme` + `.dark`
|
|
12
|
+
|
|
13
|
+
## Installation (Laravel + Inertia)
|
|
14
|
+
|
|
15
|
+
### Step 1: Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install preline @tailwindcss/forms
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Step 2: CSS Config
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
/* resources/css/app.css */
|
|
25
|
+
@import "tailwindcss";
|
|
26
|
+
|
|
27
|
+
/* Preline — MUST be in this order */
|
|
28
|
+
@source "./node_modules/preline/dist/*.js"; /* JS component scanning */
|
|
29
|
+
@import "./node_modules/preline/variants.css"; /* CSS variants */
|
|
30
|
+
@plugin "@tailwindcss/forms"; /* Forms plugin */
|
|
31
|
+
@import "./node_modules/preline/themes/theme.css"; /* Base theme */
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 3: Vite Config
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// vite.config.js
|
|
38
|
+
import { defineConfig } from 'vite';
|
|
39
|
+
import laravel from 'laravel-vite-plugin';
|
|
40
|
+
import react from '@vitejs/plugin-react';
|
|
41
|
+
|
|
42
|
+
export default defineConfig({
|
|
43
|
+
plugins: [
|
|
44
|
+
laravel({ input: ['resources/css/app.css', 'resources/js/app.tsx'], refresh: true }),
|
|
45
|
+
react(),
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 4: Init Preline in Inertia (MANDATORY)
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// resources/js/app.tsx
|
|
54
|
+
import { router } from '@inertiajs/react';
|
|
55
|
+
|
|
56
|
+
// Re-init Preline components after every SPA navigation
|
|
57
|
+
router.on('navigate', () => {
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
import('preline/preline').then(({ HSStaticMethods }) => {
|
|
60
|
+
HSStaticMethods.autoInit();
|
|
61
|
+
});
|
|
62
|
+
}, 100);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Rule:** Without `HSStaticMethods.autoInit()`, dropdowns, modals, and accordions will NOT work after Inertia navigation.
|
|
67
|
+
|
|
68
|
+
## Templates & Components (840+ free)
|
|
69
|
+
|
|
70
|
+
### Where to Find
|
|
71
|
+
|
|
72
|
+
| Source | URL | What |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| **Docs (components)** | https://preline.co/docs | Buttons, modals, forms, tables, navs |
|
|
75
|
+
| **Examples (blocks)** | https://preline.co/examples.html | 220+ UI blocks (hero, testimonials, pricing, etc.) |
|
|
76
|
+
| **Pro templates** | https://preline.co/pro/templates.html | 21 dashboard/app templates (paid) |
|
|
77
|
+
| **GitHub** | https://github.com/htmlstreamofficial/preline | Source + examples |
|
|
78
|
+
|
|
79
|
+
### How to Use Templates
|
|
80
|
+
|
|
81
|
+
1. Browse https://preline.co/examples.html
|
|
82
|
+
2. Click a block → copy the HTML/JSX
|
|
83
|
+
3. Adapt to React + Inertia:
|
|
84
|
+
- Replace `<a href>` with `<Link href>` (Inertia)
|
|
85
|
+
- Replace `class=` with `className=`
|
|
86
|
+
- Add Preline `data-*` attributes for interactive components
|
|
87
|
+
- Use `usePage().props` for dynamic data
|
|
88
|
+
|
|
89
|
+
### Example: Copy a Hero Block
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// From preline.co/examples.html → Hero sections
|
|
93
|
+
// Adapt HTML to React component:
|
|
94
|
+
export default function HeroSection() {
|
|
95
|
+
return (
|
|
96
|
+
<div className="relative overflow-hidden">
|
|
97
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
|
|
98
|
+
<div className="text-center">
|
|
99
|
+
<h1 className="text-4xl sm:text-6xl font-bold text-foreground">
|
|
100
|
+
Build your next idea
|
|
101
|
+
</h1>
|
|
102
|
+
<p className="mt-4 text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
103
|
+
Preline UI is an open-source set of prebuilt UI components.
|
|
104
|
+
</p>
|
|
105
|
+
<div className="mt-8 flex justify-center gap-3">
|
|
106
|
+
<Link href="/register"
|
|
107
|
+
className="px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary-hover font-medium transition-colors">
|
|
108
|
+
Get Started
|
|
109
|
+
</Link>
|
|
110
|
+
<Link href="/docs"
|
|
111
|
+
className="px-6 py-3 bg-layer border border-layer-line text-layer-foreground rounded-lg hover:bg-layer-hover font-medium transition-colors">
|
|
112
|
+
Documentation
|
|
113
|
+
</Link>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Key Component Categories (free)
|
|
123
|
+
|
|
124
|
+
| Category | Count | Examples |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| **Navigation** | 20+ | Navbar, sidebar, breadcrumb, pagination |
|
|
127
|
+
| **Hero** | 11 | Landing page headers |
|
|
128
|
+
| **Cards** | 15+ | Product, blog, profile, pricing |
|
|
129
|
+
| **Forms** | 20+ | Login, register, contact, checkout |
|
|
130
|
+
| **Tables** | 10+ | Sortable, paginated, striped |
|
|
131
|
+
| **Modals** | 8+ | Confirmation, form, full-screen |
|
|
132
|
+
| **Dropdowns** | 10+ | Menu, select, multi-select |
|
|
133
|
+
| **Testimonials** | 10+ | Quotes, carousel, grid |
|
|
134
|
+
| **Pricing** | 8+ | Monthly/yearly toggle, comparison |
|
|
135
|
+
| **Dashboard** | 5+ | Stats, charts, activity feed |
|
|
136
|
+
|
|
137
|
+
## Token Architecture
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Layer 1 — Tailwind primitives: var(--color-blue-600)
|
|
141
|
+
Layer 2 — Preline semantic: --primary: var(--color-blue-600)
|
|
142
|
+
Layer 3 — Component tokens: --navbar-nav-hover: var(--color-gray-100)
|
|
143
|
+
Layer 4 — Usage in HTML: className="bg-primary text-primary-foreground"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Core Token Groups
|
|
147
|
+
|
|
148
|
+
| Group | Example Tokens | Purpose |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| **Background** | `--background`, `--background-1`, `--background-2` | App surfaces |
|
|
151
|
+
| **Foreground** | `--foreground`, `--foreground-inverse` | Text colors |
|
|
152
|
+
| **Primary** | `--primary`, `--primary-hover`, `--primary-foreground`, `--primary-50`→`950` | Brand/action color |
|
|
153
|
+
| **Secondary** | `--secondary`, `--secondary-hover`, `--secondary-foreground` | Secondary emphasis |
|
|
154
|
+
| **Muted** | `--muted`, `--muted-foreground`, `--muted-foreground-1`, `--muted-foreground-2` | Subdued elements |
|
|
155
|
+
| **Destructive** | `--destructive`, `--destructive-hover`, `--destructive-foreground` | Danger actions |
|
|
156
|
+
| **Border** | `--border`, `--border-line-1`→`8` | Border scale (light→dark) |
|
|
157
|
+
| **Surface** | `--surface`, `--surface-1`→`5`, `--surface-foreground` | Elevated layers |
|
|
158
|
+
| **Layer** | `--layer`, `--layer-hover`, `--layer-foreground` | Stacked elements |
|
|
159
|
+
|
|
160
|
+
### Component Token Groups
|
|
161
|
+
|
|
162
|
+
| Component | Tokens | Notes |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| **Navbar** | `--navbar`, `--navbar-line`, `--navbar-nav-*` | 3 variants (base, `-1`, `-2`) |
|
|
165
|
+
| **Sidebar** | `--sidebar`, `--sidebar-line`, `--sidebar-nav-*` | 3 variants (base, `-1`, `-2`) |
|
|
166
|
+
| **Card** | `--card`, `--card-line`, `--card-divider`, `--card-header`, `--card-footer` | |
|
|
167
|
+
| **Dropdown** | `--dropdown`, `--dropdown-item-*` | hover, focus, active states |
|
|
168
|
+
| **Select** | `--select`, `--select-item-*` | Same state pattern |
|
|
169
|
+
| **Overlay** | `--overlay`, `--overlay-line`, `--overlay-header`, `--overlay-footer` | Modals |
|
|
170
|
+
| **Tooltip** | `--tooltip`, `--tooltip-foreground`, `--tooltip-line` | |
|
|
171
|
+
| **Popover** | `--popover`, `--popover-line` | |
|
|
172
|
+
| **Scrollbar** | `--scrollbar-track`, `--scrollbar-thumb` | + inverse variants |
|
|
173
|
+
|
|
174
|
+
## Creating Custom Themes
|
|
175
|
+
|
|
176
|
+
### Theme File Structure (MUST follow order)
|
|
177
|
+
|
|
178
|
+
```css
|
|
179
|
+
/* 1. Import base theme */
|
|
180
|
+
@import "./theme.css";
|
|
181
|
+
|
|
182
|
+
/* 2. Theme scoping block — custom palettes ONLY */
|
|
183
|
+
@theme theme-brand inline {
|
|
184
|
+
--color-brand-50: oklch(98% 0.003 250);
|
|
185
|
+
--color-brand-100: oklch(95% 0.01 250);
|
|
186
|
+
--color-brand-200: oklch(88% 0.03 250);
|
|
187
|
+
--color-brand-300: oklch(78% 0.06 250);
|
|
188
|
+
--color-brand-400: oklch(68% 0.10 250);
|
|
189
|
+
--color-brand-500: oklch(58% 0.14 250);
|
|
190
|
+
--color-brand-600: oklch(50% 0.15 250);
|
|
191
|
+
--color-brand-700: oklch(42% 0.13 250);
|
|
192
|
+
--color-brand-800: oklch(35% 0.10 250);
|
|
193
|
+
--color-brand-900: oklch(28% 0.08 250);
|
|
194
|
+
--color-brand-950: oklch(20% 0.05 250);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* 3. Light mode — semantic token overrides */
|
|
198
|
+
:root[data-theme="theme-brand"],
|
|
199
|
+
[data-theme="theme-brand"] {
|
|
200
|
+
--background: var(--color-white);
|
|
201
|
+
--foreground: var(--color-gray-800);
|
|
202
|
+
|
|
203
|
+
--primary: var(--color-brand-600);
|
|
204
|
+
--primary-foreground: var(--color-white);
|
|
205
|
+
--primary-hover: var(--color-brand-700);
|
|
206
|
+
--primary-focus: var(--color-brand-700);
|
|
207
|
+
--primary-active: var(--color-brand-700);
|
|
208
|
+
|
|
209
|
+
/* ... all 220+ tokens as needed */
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* 4. Dark mode */
|
|
213
|
+
[data-theme="theme-brand"].dark {
|
|
214
|
+
--background: var(--color-neutral-800);
|
|
215
|
+
--foreground: var(--color-neutral-200);
|
|
216
|
+
|
|
217
|
+
--primary: var(--color-brand-500);
|
|
218
|
+
--primary-foreground: var(--color-white);
|
|
219
|
+
--primary-hover: var(--color-brand-600);
|
|
220
|
+
|
|
221
|
+
/* ... dark overrides */
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Activation
|
|
226
|
+
|
|
227
|
+
```html
|
|
228
|
+
<html data-theme="theme-brand">
|
|
229
|
+
<!-- Or with dark mode: -->
|
|
230
|
+
<html data-theme="theme-brand" class="dark">
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// React theme switcher
|
|
235
|
+
function ThemeToggle() {
|
|
236
|
+
const [dark, setDark] = useState(false);
|
|
237
|
+
return (
|
|
238
|
+
<button onClick={() => {
|
|
239
|
+
document.documentElement.classList.toggle('dark');
|
|
240
|
+
setDark(!dark);
|
|
241
|
+
}}>
|
|
242
|
+
{dark ? '☀️' : '🌙'}
|
|
243
|
+
</button>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Using Components with React (Inertia)
|
|
249
|
+
|
|
250
|
+
### Navbar
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
<nav className="bg-navbar border-b border-navbar-line">
|
|
254
|
+
<div className="max-w-7xl mx-auto px-4 flex items-center justify-between h-16">
|
|
255
|
+
<Link href="/" className="text-foreground font-bold text-lg">Brand</Link>
|
|
256
|
+
<div className="flex items-center gap-1">
|
|
257
|
+
<Link href="/dashboard"
|
|
258
|
+
className="px-3 py-2 rounded-lg text-navbar-nav-foreground hover:bg-navbar-nav-hover transition-colors">
|
|
259
|
+
Dashboard
|
|
260
|
+
</Link>
|
|
261
|
+
<Link href="/leads"
|
|
262
|
+
className="px-3 py-2 rounded-lg text-navbar-nav-foreground hover:bg-navbar-nav-hover transition-colors">
|
|
263
|
+
Leads
|
|
264
|
+
</Link>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
</nav>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Card
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
<div className="bg-card border border-card-line rounded-xl overflow-hidden">
|
|
274
|
+
<div className="bg-card-header px-6 py-4 border-b border-card-divider">
|
|
275
|
+
<h3 className="text-foreground font-semibold">Title</h3>
|
|
276
|
+
</div>
|
|
277
|
+
<div className="px-6 py-4">
|
|
278
|
+
<p className="text-muted-foreground">Content</p>
|
|
279
|
+
</div>
|
|
280
|
+
<div className="bg-card-footer px-6 py-3 border-t border-card-divider">
|
|
281
|
+
<button className="bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-primary-hover transition-colors">
|
|
282
|
+
Action
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Sidebar
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
<aside className="w-64 bg-sidebar border-r border-sidebar-line h-screen">
|
|
292
|
+
<nav className="p-4 space-y-1">
|
|
293
|
+
{navItems.map(item => (
|
|
294
|
+
<Link key={item.href} href={item.href}
|
|
295
|
+
className={`flex items-center gap-3 px-3 py-2 rounded-lg transition-colors
|
|
296
|
+
${isActive(item.href)
|
|
297
|
+
? 'bg-sidebar-nav-active text-primary font-medium'
|
|
298
|
+
: 'text-sidebar-nav-foreground hover:bg-sidebar-nav-hover'}`}>
|
|
299
|
+
{item.icon}
|
|
300
|
+
{item.label}
|
|
301
|
+
</Link>
|
|
302
|
+
))}
|
|
303
|
+
</nav>
|
|
304
|
+
</aside>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Dropdown (with Preline JS)
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
<div className="hs-dropdown relative">
|
|
311
|
+
<button className="hs-dropdown-toggle px-4 py-2 bg-layer border border-layer-line rounded-lg text-layer-foreground hover:bg-layer-hover">
|
|
312
|
+
Options ▾
|
|
313
|
+
</button>
|
|
314
|
+
<div className="hs-dropdown-menu hidden bg-dropdown border border-dropdown-line rounded-lg shadow-lg mt-1 p-1 min-w-48">
|
|
315
|
+
<button className="w-full text-left px-3 py-2 rounded-md text-dropdown-item-foreground hover:bg-dropdown-item-hover">
|
|
316
|
+
Edit
|
|
317
|
+
</button>
|
|
318
|
+
<div className="border-t border-dropdown-divider my-1" />
|
|
319
|
+
<button className="w-full text-left px-3 py-2 rounded-md text-destructive hover:bg-dropdown-item-hover">
|
|
320
|
+
Delete
|
|
321
|
+
</button>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Theme Generator (CLI)
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# Generate theme from config
|
|
330
|
+
npx preline-theme-generator /tmp/config.json ./resources/css/themes/brand.css
|
|
331
|
+
|
|
332
|
+
# Config format:
|
|
333
|
+
{
|
|
334
|
+
"name": "brand",
|
|
335
|
+
"hue": 250,
|
|
336
|
+
"style": "professional",
|
|
337
|
+
"useCustomDarkGray": true,
|
|
338
|
+
"tailwindGray": "neutral"
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Chart Tokens (Apexcharts)
|
|
343
|
+
|
|
344
|
+
```css
|
|
345
|
+
/* Charts require HEX for gradients — no oklch! */
|
|
346
|
+
--chart-colors-primary-hex: #2563eb;
|
|
347
|
+
--chart-colors-chart-1-hex: #8b5cf6;
|
|
348
|
+
--chart-colors-chart-2-hex: #06b6d4;
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## FORBIDDEN
|
|
352
|
+
|
|
353
|
+
| ❌ Don't | ✅ Do |
|
|
354
|
+
|---|---|
|
|
355
|
+
| Modify `theme.css` (base) | Create separate theme file |
|
|
356
|
+
| Put tokens inside `@theme` block | Tokens in selector blocks only |
|
|
357
|
+
| Use raw colors (`bg-blue-500`) | Use semantic tokens (`bg-primary`) |
|
|
358
|
+
| `oklch()` in chart `-hex` tokens | Use hex format for charts |
|
|
359
|
+
| Force HTML class changes | Theme activation via `data-theme` only |
|
|
360
|
+
| Invent token names | Follow Preline's naming system |
|
|
361
|
+
| `@apply` for component styles | React components with token classes |
|
|
362
|
+
| Skip `HSStaticMethods.autoInit()` | Always re-init after Inertia navigation |
|