create-omg 0.4.30
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 +14 -0
- package/dist/index.mjs +100 -0
- package/dist/template/AGENTS.md +49 -0
- package/dist/template/README.md +18 -0
- package/dist/template/_gitignore +24 -0
- package/dist/template/bun.lock +898 -0
- package/dist/template/eslint.config.js +23 -0
- package/dist/template/functions/.gitkeep +0 -0
- package/dist/template/index.html +20 -0
- package/dist/template/package.json +48 -0
- package/dist/template/public/favicon.svg +1 -0
- package/dist/template/public/icons/apple-touch-icon.png +0 -0
- package/dist/template/public/icons/pwa-192x192.png +0 -0
- package/dist/template/public/icons/pwa-512x512-maskable.png +0 -0
- package/dist/template/public/icons/pwa-512x512.png +0 -0
- package/dist/template/public/icons.svg +24 -0
- package/dist/template/schema.ts +5 -0
- package/dist/template/server/db.ts +103 -0
- package/dist/template/src/App.tsx +128 -0
- package/dist/template/src/components/ui/animated-number.tsx +102 -0
- package/dist/template/src/components/ui/animated-text.tsx +109 -0
- package/dist/template/src/components/ui/bottom-nav.tsx +131 -0
- package/dist/template/src/components/ui/button.tsx +84 -0
- package/dist/template/src/components/ui/card.tsx +70 -0
- package/dist/template/src/components/ui/otp-input.tsx +179 -0
- package/dist/template/src/components/ui/scroll-affordance.tsx +106 -0
- package/dist/template/src/components/ui/sound.ts +182 -0
- package/dist/template/src/components/ui/transitions.tsx +419 -0
- package/dist/template/src/components/ui/vibes-ui-styles.ts +388 -0
- package/dist/template/src/db.drizzle.ts +5 -0
- package/dist/template/src/index.css +1 -0
- package/dist/template/src/lib/utils.ts +6 -0
- package/dist/template/src/main.tsx +10 -0
- package/dist/template/tsconfig.app.json +30 -0
- package/dist/template/tsconfig.json +10 -0
- package/dist/template/tsconfig.node.json +24 -0
- package/dist/template/vite.config.ts +23 -0
- package/package.json +37 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
// Delight-primitive stylesheet — injected once at first mount.
|
|
2
|
+
//
|
|
3
|
+
// Vendored source (was @omg-dev/ui). These primitives ship plain, namespaced
|
|
4
|
+
// (`.vui-*`) CSS in a single injected `<style>` tag rather than Tailwind
|
|
5
|
+
// utility classes. That's deliberate and survives `shadcn init` rewriting
|
|
6
|
+
// `index.css` at bake time: the styles live here in a `.ts` module nothing
|
|
7
|
+
// else touches, so there's no `@source` hack and no reliance on Tailwind's
|
|
8
|
+
// content scan. The text/number primitives INHERIT the surrounding text color
|
|
9
|
+
// (so they sit correctly on any surface — a dark card, a colored hero — and
|
|
10
|
+
// honor a text-* utility you put on them). The standalone scroll cue reads
|
|
11
|
+
// --muted-foreground with a neutral prefers-color-scheme fallback for a bare,
|
|
12
|
+
// unthemed app. Edit the CSS below freely; it's yours now.
|
|
13
|
+
|
|
14
|
+
export const STYLE_ID = "vibes-ui-styles"
|
|
15
|
+
|
|
16
|
+
export function ensureStyles() {
|
|
17
|
+
if (typeof document === "undefined") return
|
|
18
|
+
if (document.getElementById(STYLE_ID)) return
|
|
19
|
+
const el = document.createElement("style")
|
|
20
|
+
el.id = STYLE_ID
|
|
21
|
+
el.textContent = CSS
|
|
22
|
+
document.head.appendChild(el)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CSS = `
|
|
26
|
+
.vui {
|
|
27
|
+
/* Only the scroll cue still needs a bare-app fallback (--muted-foreground).
|
|
28
|
+
The text/number primitives inherit their color now, so no --vui-fg. */
|
|
29
|
+
--vui-muted: #71717a;
|
|
30
|
+
/* Shared transition tokens for the <Transitions> family (.vui-t-*). Soft,
|
|
31
|
+
fast-out eases; tune these in one place. */
|
|
32
|
+
--vui-ease-soft: cubic-bezier(0.22, 1, 0.36, 1);
|
|
33
|
+
--vui-ease-bounce: cubic-bezier(0.34, 1.36, 0.64, 1);
|
|
34
|
+
font-family: inherit;
|
|
35
|
+
}
|
|
36
|
+
@media (prefers-color-scheme: dark) {
|
|
37
|
+
.vui {
|
|
38
|
+
--vui-muted: #a1a1aa;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ── AnimatedNumber — odometer / slot-text rolling digits ──────────────── */
|
|
43
|
+
.vui-num {
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
align-items: baseline;
|
|
46
|
+
/* No \`color\` here on purpose: inherit the surrounding text color (or honor a
|
|
47
|
+
text-* utility on the element) like normal inline text. Pinning this to
|
|
48
|
+
var(--foreground) made the number render page-foreground-dark even on a
|
|
49
|
+
dark card / colored surface, where its siblings (which inherit) sit light —
|
|
50
|
+
a dark-on-dark theme conflict. */
|
|
51
|
+
font-variant-numeric: tabular-nums;
|
|
52
|
+
line-height: 1;
|
|
53
|
+
}
|
|
54
|
+
.vui-num-reel {
|
|
55
|
+
position: relative;
|
|
56
|
+
display: inline-block;
|
|
57
|
+
height: 1em;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
vertical-align: bottom;
|
|
60
|
+
}
|
|
61
|
+
.vui-num-track {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
/* The roll itself. Long, soft ease so digits settle like an odometer.
|
|
65
|
+
transform is set inline per-digit; the per-position stagger (delay) is
|
|
66
|
+
set inline too so significant digits cascade in after the small ones. */
|
|
67
|
+
transition: transform 0.85s cubic-bezier(0.22, 1, 0.36, 1);
|
|
68
|
+
will-change: transform;
|
|
69
|
+
}
|
|
70
|
+
.vui-num-cell {
|
|
71
|
+
height: 1em;
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
}
|
|
76
|
+
.vui-num-sym {
|
|
77
|
+
display: inline-block;
|
|
78
|
+
}
|
|
79
|
+
@media (prefers-reduced-motion: reduce) {
|
|
80
|
+
.vui-num-track { transition: none; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ── AnimatedText — split-flap / slot-text rolling letters ────────────── */
|
|
84
|
+
.vui-text {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: baseline;
|
|
87
|
+
/* Inherit the surrounding text color (or honor a text-* utility) — see the
|
|
88
|
+
note on .vui-num. Don't pin to var(--foreground): it overrode the color of
|
|
89
|
+
whatever surface the text sits on. */
|
|
90
|
+
line-height: 1;
|
|
91
|
+
white-space: pre;
|
|
92
|
+
}
|
|
93
|
+
.vui-text-reel {
|
|
94
|
+
position: relative;
|
|
95
|
+
display: inline-block;
|
|
96
|
+
/* Fixed character cell — every reel is as wide as the alphabet's widest
|
|
97
|
+
glyph, so the row reads like a split-flap board and never reflows mid-roll. */
|
|
98
|
+
height: 1em;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
vertical-align: bottom;
|
|
101
|
+
}
|
|
102
|
+
.vui-text-track {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
/* The roll itself. Long, soft ease so letters settle like a flap board.
|
|
106
|
+
transform is set inline per-letter; the per-position stagger (delay) is
|
|
107
|
+
set inline too so the word reveals left-to-right. */
|
|
108
|
+
transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
|
|
109
|
+
will-change: transform;
|
|
110
|
+
}
|
|
111
|
+
.vui-text-cell {
|
|
112
|
+
height: 1em;
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
white-space: pre;
|
|
117
|
+
}
|
|
118
|
+
.vui-text-sym {
|
|
119
|
+
display: inline-block;
|
|
120
|
+
white-space: pre;
|
|
121
|
+
}
|
|
122
|
+
@media (prefers-reduced-motion: reduce) {
|
|
123
|
+
.vui-text-track { transition: none; }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ── ScrollAffordance — "there's more below" cue ──────────────────────── */
|
|
127
|
+
.vui-scroll {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
align-items: center;
|
|
131
|
+
gap: 6px;
|
|
132
|
+
color: var(--muted-foreground, var(--vui-muted));
|
|
133
|
+
font-size: 12px;
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
letter-spacing: 0.02em;
|
|
136
|
+
/* Fade + lift away the instant the user starts scrolling — the cue has
|
|
137
|
+
done its job, lingering would be noise. */
|
|
138
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
139
|
+
pointer-events: none;
|
|
140
|
+
user-select: none;
|
|
141
|
+
}
|
|
142
|
+
.vui-scroll[data-hidden="true"] {
|
|
143
|
+
opacity: 0;
|
|
144
|
+
transform: translateY(6px);
|
|
145
|
+
}
|
|
146
|
+
.vui-scroll-chevron {
|
|
147
|
+
width: 22px;
|
|
148
|
+
height: 22px;
|
|
149
|
+
animation: vui-bob 1.8s ease-in-out infinite;
|
|
150
|
+
}
|
|
151
|
+
@keyframes vui-bob {
|
|
152
|
+
0%, 100% { transform: translateY(0); opacity: 0.65; }
|
|
153
|
+
50% { transform: translateY(4px); opacity: 1; }
|
|
154
|
+
}
|
|
155
|
+
@media (prefers-reduced-motion: reduce) {
|
|
156
|
+
.vui-scroll-chevron { animation: none; }
|
|
157
|
+
.vui-scroll { transition: none; }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ── Transitions (transitions.dev, vendored) ──────────────────────────────
|
|
161
|
+
Dependency-free, CSS-only micro-interactions. Only transform/opacity/filter
|
|
162
|
+
animate (never color/size/shadow), close is faster than open, and every
|
|
163
|
+
family carries a prefers-reduced-motion guard. Driven by the React wrappers
|
|
164
|
+
in transitions.tsx via .is-open / .is-closing / data-* state. */
|
|
165
|
+
|
|
166
|
+
/* Modal — dialog open/close (+ optional fading backdrop) */
|
|
167
|
+
.vui-t-modal {
|
|
168
|
+
transform-origin: center;
|
|
169
|
+
transform: scale(0.95);
|
|
170
|
+
opacity: 0;
|
|
171
|
+
pointer-events: none;
|
|
172
|
+
transition:
|
|
173
|
+
transform 300ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
174
|
+
opacity 300ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
175
|
+
will-change: transform, opacity;
|
|
176
|
+
}
|
|
177
|
+
.vui-t-modal.is-open { transform: scale(1); opacity: 1; pointer-events: auto; }
|
|
178
|
+
.vui-t-modal.is-closing {
|
|
179
|
+
transform: scale(0.96);
|
|
180
|
+
opacity: 0;
|
|
181
|
+
pointer-events: none;
|
|
182
|
+
transition:
|
|
183
|
+
transform 150ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
184
|
+
opacity 150ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
185
|
+
}
|
|
186
|
+
.vui-t-backdrop {
|
|
187
|
+
opacity: 0;
|
|
188
|
+
transition: opacity 300ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
189
|
+
}
|
|
190
|
+
.vui-t-backdrop.is-open { opacity: 1; }
|
|
191
|
+
.vui-t-backdrop.is-closing {
|
|
192
|
+
opacity: 0;
|
|
193
|
+
transition: opacity 150ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Menu — popover/dropdown anchored to its trigger (set data-origin) */
|
|
197
|
+
.vui-t-menu {
|
|
198
|
+
transform-origin: top left;
|
|
199
|
+
transform: scale(0.97);
|
|
200
|
+
opacity: 0;
|
|
201
|
+
pointer-events: none;
|
|
202
|
+
transition:
|
|
203
|
+
transform 250ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
204
|
+
opacity 250ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
205
|
+
will-change: transform, opacity;
|
|
206
|
+
}
|
|
207
|
+
.vui-t-menu[data-origin="top-right"] { transform-origin: top right; }
|
|
208
|
+
.vui-t-menu[data-origin="top-center"] { transform-origin: top center; }
|
|
209
|
+
.vui-t-menu[data-origin="bottom-left"] { transform-origin: bottom left; }
|
|
210
|
+
.vui-t-menu[data-origin="bottom-center"] { transform-origin: bottom center; }
|
|
211
|
+
.vui-t-menu[data-origin="bottom-right"] { transform-origin: bottom right; }
|
|
212
|
+
.vui-t-menu.is-open { transform: scale(1); opacity: 1; pointer-events: auto; }
|
|
213
|
+
.vui-t-menu.is-closing {
|
|
214
|
+
transform: scale(0.99);
|
|
215
|
+
opacity: 0;
|
|
216
|
+
pointer-events: none;
|
|
217
|
+
transition:
|
|
218
|
+
transform 150ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
219
|
+
opacity 150ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Panel — inline reveal / drawer / expander (stays mounted, toggles data-open).
|
|
223
|
+
Override --vui-t-panel-y with ~half the panel height for a fuller open. */
|
|
224
|
+
.vui-t-panel {
|
|
225
|
+
transform: translateY(var(--vui-t-panel-y, 16px));
|
|
226
|
+
opacity: 0;
|
|
227
|
+
filter: blur(2px);
|
|
228
|
+
pointer-events: none;
|
|
229
|
+
transition:
|
|
230
|
+
transform 350ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
231
|
+
opacity 350ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
232
|
+
filter 350ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
233
|
+
will-change: transform, opacity, filter;
|
|
234
|
+
}
|
|
235
|
+
.vui-t-panel[data-open="true"] {
|
|
236
|
+
transform: translateY(0);
|
|
237
|
+
opacity: 1;
|
|
238
|
+
filter: blur(0);
|
|
239
|
+
pointer-events: auto;
|
|
240
|
+
transition:
|
|
241
|
+
transform 400ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
242
|
+
opacity 400ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
243
|
+
filter 400ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* PageSlide — side-by-side wizard / tab steps. Pages before the active one
|
|
247
|
+
exit left, pages after exit right (direction set inline per page). */
|
|
248
|
+
.vui-t-pages { position: relative; }
|
|
249
|
+
.vui-t-page {
|
|
250
|
+
position: absolute;
|
|
251
|
+
inset: 0;
|
|
252
|
+
opacity: 0;
|
|
253
|
+
pointer-events: none;
|
|
254
|
+
transform: translateX(var(--vui-t-page-x, 8px));
|
|
255
|
+
filter: blur(3px);
|
|
256
|
+
transition:
|
|
257
|
+
opacity 200ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
258
|
+
transform 200ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
259
|
+
filter 200ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
260
|
+
will-change: opacity, transform, filter;
|
|
261
|
+
}
|
|
262
|
+
.vui-t-page.is-active {
|
|
263
|
+
opacity: 1;
|
|
264
|
+
pointer-events: auto;
|
|
265
|
+
transform: translateX(0);
|
|
266
|
+
filter: blur(0);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* IconSwap — cross-fade/scale between two stacked icons (play/pause, moon/sun) */
|
|
270
|
+
.vui-t-icon-swap { position: relative; display: inline-grid; }
|
|
271
|
+
.vui-t-icon-swap .vui-t-icon {
|
|
272
|
+
grid-area: 1 / 1;
|
|
273
|
+
transition:
|
|
274
|
+
opacity 200ms ease-in-out,
|
|
275
|
+
filter 200ms ease-in-out,
|
|
276
|
+
transform 200ms ease-in-out;
|
|
277
|
+
will-change: opacity, filter, transform;
|
|
278
|
+
}
|
|
279
|
+
.vui-t-icon-swap[data-state="a"] .vui-t-icon[data-icon="a"],
|
|
280
|
+
.vui-t-icon-swap[data-state="b"] .vui-t-icon[data-icon="b"] {
|
|
281
|
+
opacity: 1; filter: blur(0); transform: scale(1);
|
|
282
|
+
}
|
|
283
|
+
.vui-t-icon-swap[data-state="a"] .vui-t-icon[data-icon="b"],
|
|
284
|
+
.vui-t-icon-swap[data-state="b"] .vui-t-icon[data-icon="a"] {
|
|
285
|
+
opacity: 0; filter: blur(2px); transform: scale(0.25);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* TextSwap — smoothly replace a label/status string in place.
|
|
289
|
+
--vui-t-text-dur is read by the wrapper to time the text content swap, so
|
|
290
|
+
tune the duration here (one place) and the JS phase stays in sync. */
|
|
291
|
+
.vui-t-text {
|
|
292
|
+
--vui-t-text-dur: 200ms;
|
|
293
|
+
display: inline-block;
|
|
294
|
+
transform: translateY(0);
|
|
295
|
+
filter: blur(0);
|
|
296
|
+
opacity: 1;
|
|
297
|
+
transition:
|
|
298
|
+
transform var(--vui-t-text-dur) ease-out,
|
|
299
|
+
filter var(--vui-t-text-dur) ease-out,
|
|
300
|
+
opacity var(--vui-t-text-dur) ease-out;
|
|
301
|
+
will-change: transform, filter, opacity;
|
|
302
|
+
}
|
|
303
|
+
.vui-t-text.is-exit { transform: translateY(-8px); filter: blur(2px); opacity: 0; }
|
|
304
|
+
.vui-t-text.is-enter-start {
|
|
305
|
+
transform: translateY(8px); filter: blur(2px); opacity: 0; transition: none;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* NotificationBadge — pop a count/dot in and out on a relative trigger */
|
|
309
|
+
@keyframes vui-t-badge-slide-in {
|
|
310
|
+
from { transform: translate(-8.2px, 12.4px); }
|
|
311
|
+
to { transform: translate(0, 0); }
|
|
312
|
+
}
|
|
313
|
+
.vui-t-badge {
|
|
314
|
+
position: absolute;
|
|
315
|
+
top: -6px;
|
|
316
|
+
right: -8px;
|
|
317
|
+
pointer-events: none;
|
|
318
|
+
will-change: transform;
|
|
319
|
+
}
|
|
320
|
+
.vui-t-badge[data-open="true"] {
|
|
321
|
+
animation: vui-t-badge-slide-in 260ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
322
|
+
}
|
|
323
|
+
.vui-t-badge-dot {
|
|
324
|
+
display: block;
|
|
325
|
+
transform-origin: center;
|
|
326
|
+
transform: scale(1);
|
|
327
|
+
opacity: 1;
|
|
328
|
+
filter: blur(0);
|
|
329
|
+
transition:
|
|
330
|
+
transform 500ms var(--vui-ease-bounce, cubic-bezier(0.34, 1.36, 0.64, 1)),
|
|
331
|
+
opacity 400ms var(--vui-ease-bounce, cubic-bezier(0.34, 1.36, 0.64, 1)),
|
|
332
|
+
filter 500ms var(--vui-ease-bounce, cubic-bezier(0.34, 1.36, 0.64, 1));
|
|
333
|
+
will-change: transform, opacity, filter;
|
|
334
|
+
}
|
|
335
|
+
.vui-t-badge[data-open="false"] .vui-t-badge-dot {
|
|
336
|
+
transform: scale(0);
|
|
337
|
+
opacity: 0;
|
|
338
|
+
filter: blur(2px);
|
|
339
|
+
transition:
|
|
340
|
+
transform 180ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
341
|
+
opacity 180ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
342
|
+
filter 180ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* Expand — height-auto reveal via grid-template-rows 0fr→1fr (no JS measuring,
|
|
346
|
+
no max-height guesswork). The inner wrapper clips overflow while collapsed;
|
|
347
|
+
min-height:0 lets the grid track actually shrink to zero. Content fades in
|
|
348
|
+
slightly behind the height so a tall body doesn't pop. */
|
|
349
|
+
.vui-t-expand {
|
|
350
|
+
display: grid;
|
|
351
|
+
grid-template-rows: 0fr;
|
|
352
|
+
transition: grid-template-rows 320ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
353
|
+
}
|
|
354
|
+
.vui-t-expand[data-open="true"] { grid-template-rows: 1fr; }
|
|
355
|
+
.vui-t-expand > .vui-t-expand-inner {
|
|
356
|
+
overflow: hidden;
|
|
357
|
+
min-height: 0;
|
|
358
|
+
opacity: 0;
|
|
359
|
+
transition: opacity 180ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
360
|
+
}
|
|
361
|
+
.vui-t-expand[data-open="true"] > .vui-t-expand-inner {
|
|
362
|
+
opacity: 1;
|
|
363
|
+
transition: opacity 240ms var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1)) 80ms;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* View Transitions — soft default cross-fade for the animateView() helper
|
|
367
|
+
(document.startViewTransition). Slightly slower than the UA default so
|
|
368
|
+
morphs read as intentional rather than a flicker. */
|
|
369
|
+
::view-transition-old(root),
|
|
370
|
+
::view-transition-new(root) {
|
|
371
|
+
animation-duration: 260ms;
|
|
372
|
+
animation-timing-function: var(--vui-ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
@media (prefers-reduced-motion: reduce) {
|
|
376
|
+
.vui-t-modal, .vui-t-backdrop, .vui-t-menu, .vui-t-panel, .vui-t-page,
|
|
377
|
+
.vui-t-icon-swap .vui-t-icon, .vui-t-text, .vui-t-expand,
|
|
378
|
+
.vui-t-expand > .vui-t-expand-inner {
|
|
379
|
+
transition: none !important;
|
|
380
|
+
}
|
|
381
|
+
.vui-t-badge, .vui-t-badge-dot { animation: none !important; transition: none !important; }
|
|
382
|
+
/* Kill View-Transition animations too — animateView() also short-circuits in
|
|
383
|
+
JS, but a transition started elsewhere still gets neutralized here. */
|
|
384
|
+
::view-transition-group(*),
|
|
385
|
+
::view-transition-old(*),
|
|
386
|
+
::view-transition-new(*) { animation: none !important; }
|
|
387
|
+
}
|
|
388
|
+
`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["vite/client"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
|
|
18
|
+
/* Linting */
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
|
|
24
|
+
/* Path alias — keep in lockstep with vite.config.ts */
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": ["./src/*"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"include": ["src"]
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"erasableSyntaxOnly": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["vite.config.ts"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { defineConfig } from 'vite-plus'
|
|
3
|
+
import react from '@vitejs/plugin-react'
|
|
4
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
+
import vibes from '@omg-dev/vite-plugin'
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [react(), tailwindcss(), vibes()],
|
|
9
|
+
resolve: {
|
|
10
|
+
// pi (and humans copying from shadcn docs) reaches for '@/components/...'
|
|
11
|
+
// unconditionally. Bake the alias here so the import resolves in every
|
|
12
|
+
// new project without each one having to configure it.
|
|
13
|
+
alias: { '@': path.resolve(__dirname, './src') },
|
|
14
|
+
},
|
|
15
|
+
server: {
|
|
16
|
+
host: '0.0.0.0',
|
|
17
|
+
allowedHosts: true,
|
|
18
|
+
cors: true,
|
|
19
|
+
hmr: {
|
|
20
|
+
overlay: false,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-omg",
|
|
3
|
+
"version": "0.4.30",
|
|
4
|
+
"description": "Create a new omg.dev app.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-omg": "./dist/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "bun run scripts/build.ts",
|
|
14
|
+
"test": "bun test",
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"bun": ">=1.1.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bun": "^1.0.0",
|
|
22
|
+
"typescript": "npm:@typescript/typescript6@6.0.2"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/BennyKok/vibes.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://docs.omg.dev",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": "./dist/index.mjs"
|
|
36
|
+
}
|
|
37
|
+
}
|