start-vibing-stacks 1.9.2 → 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/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.0';
|
|
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,273 @@
|
|
|
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
|
+
```bash
|
|
16
|
+
npm install preline
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// vite.config.js
|
|
21
|
+
export default defineConfig({
|
|
22
|
+
plugins: [
|
|
23
|
+
laravel({ input: ['resources/css/app.css', 'resources/js/app.tsx'] }),
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
/* resources/css/app.css */
|
|
30
|
+
@import "tailwindcss";
|
|
31
|
+
@import "preline/themes/theme.css";
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
// resources/js/app.tsx — init Preline after Inertia navigation
|
|
36
|
+
import { router } from '@inertiajs/react';
|
|
37
|
+
|
|
38
|
+
router.on('navigate', () => {
|
|
39
|
+
// Re-init Preline components after SPA navigation
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
import('preline/preline').then(({ HSStaticMethods }) => {
|
|
42
|
+
HSStaticMethods.autoInit();
|
|
43
|
+
});
|
|
44
|
+
}, 100);
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Token Architecture
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Layer 1 — Tailwind primitives: var(--color-blue-600)
|
|
52
|
+
Layer 2 — Preline semantic: --primary: var(--color-blue-600)
|
|
53
|
+
Layer 3 — Component tokens: --navbar-nav-hover: var(--color-gray-100)
|
|
54
|
+
Layer 4 — Usage in HTML: className="bg-primary text-primary-foreground"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Core Token Groups
|
|
58
|
+
|
|
59
|
+
| Group | Example Tokens | Purpose |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| **Background** | `--background`, `--background-1`, `--background-2` | App surfaces |
|
|
62
|
+
| **Foreground** | `--foreground`, `--foreground-inverse` | Text colors |
|
|
63
|
+
| **Primary** | `--primary`, `--primary-hover`, `--primary-foreground`, `--primary-50`→`950` | Brand/action color |
|
|
64
|
+
| **Secondary** | `--secondary`, `--secondary-hover`, `--secondary-foreground` | Secondary emphasis |
|
|
65
|
+
| **Muted** | `--muted`, `--muted-foreground`, `--muted-foreground-1`, `--muted-foreground-2` | Subdued elements |
|
|
66
|
+
| **Destructive** | `--destructive`, `--destructive-hover`, `--destructive-foreground` | Danger actions |
|
|
67
|
+
| **Border** | `--border`, `--border-line-1`→`8` | Border scale (light→dark) |
|
|
68
|
+
| **Surface** | `--surface`, `--surface-1`→`5`, `--surface-foreground` | Elevated layers |
|
|
69
|
+
| **Layer** | `--layer`, `--layer-hover`, `--layer-foreground` | Stacked elements |
|
|
70
|
+
|
|
71
|
+
### Component Token Groups
|
|
72
|
+
|
|
73
|
+
| Component | Tokens | Notes |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| **Navbar** | `--navbar`, `--navbar-line`, `--navbar-nav-*` | 3 variants (base, `-1`, `-2`) |
|
|
76
|
+
| **Sidebar** | `--sidebar`, `--sidebar-line`, `--sidebar-nav-*` | 3 variants (base, `-1`, `-2`) |
|
|
77
|
+
| **Card** | `--card`, `--card-line`, `--card-divider`, `--card-header`, `--card-footer` | |
|
|
78
|
+
| **Dropdown** | `--dropdown`, `--dropdown-item-*` | hover, focus, active states |
|
|
79
|
+
| **Select** | `--select`, `--select-item-*` | Same state pattern |
|
|
80
|
+
| **Overlay** | `--overlay`, `--overlay-line`, `--overlay-header`, `--overlay-footer` | Modals |
|
|
81
|
+
| **Tooltip** | `--tooltip`, `--tooltip-foreground`, `--tooltip-line` | |
|
|
82
|
+
| **Popover** | `--popover`, `--popover-line` | |
|
|
83
|
+
| **Scrollbar** | `--scrollbar-track`, `--scrollbar-thumb` | + inverse variants |
|
|
84
|
+
|
|
85
|
+
## Creating Custom Themes
|
|
86
|
+
|
|
87
|
+
### Theme File Structure (MUST follow order)
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
/* 1. Import base theme */
|
|
91
|
+
@import "./theme.css";
|
|
92
|
+
|
|
93
|
+
/* 2. Theme scoping block — custom palettes ONLY */
|
|
94
|
+
@theme theme-brand inline {
|
|
95
|
+
--color-brand-50: oklch(98% 0.003 250);
|
|
96
|
+
--color-brand-100: oklch(95% 0.01 250);
|
|
97
|
+
--color-brand-200: oklch(88% 0.03 250);
|
|
98
|
+
--color-brand-300: oklch(78% 0.06 250);
|
|
99
|
+
--color-brand-400: oklch(68% 0.10 250);
|
|
100
|
+
--color-brand-500: oklch(58% 0.14 250);
|
|
101
|
+
--color-brand-600: oklch(50% 0.15 250);
|
|
102
|
+
--color-brand-700: oklch(42% 0.13 250);
|
|
103
|
+
--color-brand-800: oklch(35% 0.10 250);
|
|
104
|
+
--color-brand-900: oklch(28% 0.08 250);
|
|
105
|
+
--color-brand-950: oklch(20% 0.05 250);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* 3. Light mode — semantic token overrides */
|
|
109
|
+
:root[data-theme="theme-brand"],
|
|
110
|
+
[data-theme="theme-brand"] {
|
|
111
|
+
--background: var(--color-white);
|
|
112
|
+
--foreground: var(--color-gray-800);
|
|
113
|
+
|
|
114
|
+
--primary: var(--color-brand-600);
|
|
115
|
+
--primary-foreground: var(--color-white);
|
|
116
|
+
--primary-hover: var(--color-brand-700);
|
|
117
|
+
--primary-focus: var(--color-brand-700);
|
|
118
|
+
--primary-active: var(--color-brand-700);
|
|
119
|
+
|
|
120
|
+
/* ... all 220+ tokens as needed */
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* 4. Dark mode */
|
|
124
|
+
[data-theme="theme-brand"].dark {
|
|
125
|
+
--background: var(--color-neutral-800);
|
|
126
|
+
--foreground: var(--color-neutral-200);
|
|
127
|
+
|
|
128
|
+
--primary: var(--color-brand-500);
|
|
129
|
+
--primary-foreground: var(--color-white);
|
|
130
|
+
--primary-hover: var(--color-brand-600);
|
|
131
|
+
|
|
132
|
+
/* ... dark overrides */
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Activation
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<html data-theme="theme-brand">
|
|
140
|
+
<!-- Or with dark mode: -->
|
|
141
|
+
<html data-theme="theme-brand" class="dark">
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// React theme switcher
|
|
146
|
+
function ThemeToggle() {
|
|
147
|
+
const [dark, setDark] = useState(false);
|
|
148
|
+
return (
|
|
149
|
+
<button onClick={() => {
|
|
150
|
+
document.documentElement.classList.toggle('dark');
|
|
151
|
+
setDark(!dark);
|
|
152
|
+
}}>
|
|
153
|
+
{dark ? '☀️' : '🌙'}
|
|
154
|
+
</button>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Using Components with React (Inertia)
|
|
160
|
+
|
|
161
|
+
### Navbar
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
<nav className="bg-navbar border-b border-navbar-line">
|
|
165
|
+
<div className="max-w-7xl mx-auto px-4 flex items-center justify-between h-16">
|
|
166
|
+
<Link href="/" className="text-foreground font-bold text-lg">Brand</Link>
|
|
167
|
+
<div className="flex items-center gap-1">
|
|
168
|
+
<Link href="/dashboard"
|
|
169
|
+
className="px-3 py-2 rounded-lg text-navbar-nav-foreground hover:bg-navbar-nav-hover transition-colors">
|
|
170
|
+
Dashboard
|
|
171
|
+
</Link>
|
|
172
|
+
<Link href="/leads"
|
|
173
|
+
className="px-3 py-2 rounded-lg text-navbar-nav-foreground hover:bg-navbar-nav-hover transition-colors">
|
|
174
|
+
Leads
|
|
175
|
+
</Link>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</nav>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Card
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<div className="bg-card border border-card-line rounded-xl overflow-hidden">
|
|
185
|
+
<div className="bg-card-header px-6 py-4 border-b border-card-divider">
|
|
186
|
+
<h3 className="text-foreground font-semibold">Title</h3>
|
|
187
|
+
</div>
|
|
188
|
+
<div className="px-6 py-4">
|
|
189
|
+
<p className="text-muted-foreground">Content</p>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="bg-card-footer px-6 py-3 border-t border-card-divider">
|
|
192
|
+
<button className="bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-primary-hover transition-colors">
|
|
193
|
+
Action
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Sidebar
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
<aside className="w-64 bg-sidebar border-r border-sidebar-line h-screen">
|
|
203
|
+
<nav className="p-4 space-y-1">
|
|
204
|
+
{navItems.map(item => (
|
|
205
|
+
<Link key={item.href} href={item.href}
|
|
206
|
+
className={`flex items-center gap-3 px-3 py-2 rounded-lg transition-colors
|
|
207
|
+
${isActive(item.href)
|
|
208
|
+
? 'bg-sidebar-nav-active text-primary font-medium'
|
|
209
|
+
: 'text-sidebar-nav-foreground hover:bg-sidebar-nav-hover'}`}>
|
|
210
|
+
{item.icon}
|
|
211
|
+
{item.label}
|
|
212
|
+
</Link>
|
|
213
|
+
))}
|
|
214
|
+
</nav>
|
|
215
|
+
</aside>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Dropdown (with Preline JS)
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<div className="hs-dropdown relative">
|
|
222
|
+
<button className="hs-dropdown-toggle px-4 py-2 bg-layer border border-layer-line rounded-lg text-layer-foreground hover:bg-layer-hover">
|
|
223
|
+
Options ▾
|
|
224
|
+
</button>
|
|
225
|
+
<div className="hs-dropdown-menu hidden bg-dropdown border border-dropdown-line rounded-lg shadow-lg mt-1 p-1 min-w-48">
|
|
226
|
+
<button className="w-full text-left px-3 py-2 rounded-md text-dropdown-item-foreground hover:bg-dropdown-item-hover">
|
|
227
|
+
Edit
|
|
228
|
+
</button>
|
|
229
|
+
<div className="border-t border-dropdown-divider my-1" />
|
|
230
|
+
<button className="w-full text-left px-3 py-2 rounded-md text-destructive hover:bg-dropdown-item-hover">
|
|
231
|
+
Delete
|
|
232
|
+
</button>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Theme Generator (CLI)
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Generate theme from config
|
|
241
|
+
npx preline-theme-generator /tmp/config.json ./resources/css/themes/brand.css
|
|
242
|
+
|
|
243
|
+
# Config format:
|
|
244
|
+
{
|
|
245
|
+
"name": "brand",
|
|
246
|
+
"hue": 250,
|
|
247
|
+
"style": "professional",
|
|
248
|
+
"useCustomDarkGray": true,
|
|
249
|
+
"tailwindGray": "neutral"
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Chart Tokens (Apexcharts)
|
|
254
|
+
|
|
255
|
+
```css
|
|
256
|
+
/* Charts require HEX for gradients — no oklch! */
|
|
257
|
+
--chart-colors-primary-hex: #2563eb;
|
|
258
|
+
--chart-colors-chart-1-hex: #8b5cf6;
|
|
259
|
+
--chart-colors-chart-2-hex: #06b6d4;
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## FORBIDDEN
|
|
263
|
+
|
|
264
|
+
| ❌ Don't | ✅ Do |
|
|
265
|
+
|---|---|
|
|
266
|
+
| Modify `theme.css` (base) | Create separate theme file |
|
|
267
|
+
| Put tokens inside `@theme` block | Tokens in selector blocks only |
|
|
268
|
+
| Use raw colors (`bg-blue-500`) | Use semantic tokens (`bg-primary`) |
|
|
269
|
+
| `oklch()` in chart `-hex` tokens | Use hex format for charts |
|
|
270
|
+
| Force HTML class changes | Theme activation via `data-theme` only |
|
|
271
|
+
| Invent token names | Follow Preline's naming system |
|
|
272
|
+
| `@apply` for component styles | React components with token classes |
|
|
273
|
+
| Skip `HSStaticMethods.autoInit()` | Always re-init after Inertia navigation |
|