feather-toast-vue 1.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/.vscode/extensions.json +3 -0
- package/README.md +286 -0
- package/desktop.ini +2 -0
- package/env.d.ts +1 -0
- package/index.html +15 -0
- package/package.json +33 -0
- package/public/favicon.ico +0 -0
- package/src/App.vue +711 -0
- package/src/assets/toast.css +305 -0
- package/src/components/Toast.vue +230 -0
- package/src/components/ToastContainer.vue +69 -0
- package/src/composables/useToast.ts +133 -0
- package/src/index.ts +11 -0
- package/src/main.ts +8 -0
- package/src/plugins/toast.ts +17 -0
- package/src/types/toast.ts +41 -0
- package/tsconfig.app.json +18 -0
- package/tsconfig.json +11 -0
- package/tsconfig.node.json +27 -0
- package/vite.config.lib.ts +33 -0
- package/vite.config.ts +19 -0
package/src/App.vue
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { useToast } from '@/composables/useToast'
|
|
4
|
+
import ToastContainer from '@/components/ToastContainer.vue'
|
|
5
|
+
import type { ToastPosition } from '@/types/toast'
|
|
6
|
+
|
|
7
|
+
const { toast } = useToast()
|
|
8
|
+
|
|
9
|
+
// Copy install command
|
|
10
|
+
const installCopied = ref(false)
|
|
11
|
+
function copyInstall() {
|
|
12
|
+
navigator.clipboard.writeText('npm install feather-toast')
|
|
13
|
+
installCopied.value = true
|
|
14
|
+
setTimeout(() => { installCopied.value = false }, 2000)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const selectedPosition = ref<ToastPosition>('bottom-right')
|
|
18
|
+
|
|
19
|
+
const positions: { label: string; value: ToastPosition }[] = [
|
|
20
|
+
{ label: 'Top Right', value: 'top-right' },
|
|
21
|
+
{ label: 'Top Left', value: 'top-left' },
|
|
22
|
+
{ label: 'Top Center', value: 'top-center' },
|
|
23
|
+
{ label: 'Bottom Right', value: 'bottom-right' },
|
|
24
|
+
{ label: 'Bottom Left', value: 'bottom-left' },
|
|
25
|
+
{ label: 'Bottom Center', value: 'bottom-center' },
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
function fireDefault() {
|
|
29
|
+
toast({
|
|
30
|
+
title: 'Event Created',
|
|
31
|
+
description: 'Your event has been successfully scheduled.',
|
|
32
|
+
position: selectedPosition.value,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function fireSuccess() {
|
|
37
|
+
toast.success({
|
|
38
|
+
title: 'Changes saved',
|
|
39
|
+
description: 'Your profile has been updated successfully.',
|
|
40
|
+
position: selectedPosition.value,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function fireError() {
|
|
45
|
+
toast.error({
|
|
46
|
+
title: 'Something went wrong',
|
|
47
|
+
description: 'There was an error processing your request. Please try again.',
|
|
48
|
+
position: selectedPosition.value,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fireWarning() {
|
|
53
|
+
toast.warning({
|
|
54
|
+
title: 'Disk space low',
|
|
55
|
+
description: 'You have less than 10% storage remaining.',
|
|
56
|
+
position: selectedPosition.value,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fireInfo() {
|
|
61
|
+
toast.info({
|
|
62
|
+
title: 'New update available',
|
|
63
|
+
description: 'Version 2.4.0 is ready to install.',
|
|
64
|
+
position: selectedPosition.value,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function fireLoading() {
|
|
69
|
+
toast.loading({
|
|
70
|
+
title: 'Uploading files...',
|
|
71
|
+
description: 'Please wait while your files are being uploaded.',
|
|
72
|
+
position: selectedPosition.value,
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function firePromise() {
|
|
77
|
+
const fakeApiCall = new Promise<{ name: string }>((resolve, reject) => {
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
if (Math.random() > 0.3) {
|
|
80
|
+
resolve({ name: 'Project Alpha' })
|
|
81
|
+
} else {
|
|
82
|
+
reject(new Error('Network timeout'))
|
|
83
|
+
}
|
|
84
|
+
}, 2500)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
toast
|
|
88
|
+
.promise(fakeApiCall, {
|
|
89
|
+
loading: {
|
|
90
|
+
title: 'Creating project...',
|
|
91
|
+
description: 'Setting up your workspace.',
|
|
92
|
+
position: selectedPosition.value,
|
|
93
|
+
},
|
|
94
|
+
success: (data) => ({
|
|
95
|
+
title: `"${data.name}" created`,
|
|
96
|
+
description: 'Your project is ready to go.',
|
|
97
|
+
position: selectedPosition.value,
|
|
98
|
+
}),
|
|
99
|
+
error: (err) => ({
|
|
100
|
+
title: 'Failed to create project',
|
|
101
|
+
description: err instanceof Error ? err.message : 'Unknown error occurred.',
|
|
102
|
+
position: selectedPosition.value,
|
|
103
|
+
}),
|
|
104
|
+
})
|
|
105
|
+
.catch(() => {
|
|
106
|
+
/* handled by toast */
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function fireWithAction() {
|
|
111
|
+
toast({
|
|
112
|
+
title: 'Message archived',
|
|
113
|
+
description: 'The conversation has been moved to archive.',
|
|
114
|
+
position: selectedPosition.value,
|
|
115
|
+
action: {
|
|
116
|
+
label: 'Undo',
|
|
117
|
+
onClick: () => {
|
|
118
|
+
toast.success({
|
|
119
|
+
title: 'Restored',
|
|
120
|
+
description: 'Message moved back to inbox.',
|
|
121
|
+
position: selectedPosition.value,
|
|
122
|
+
})
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<template>
|
|
130
|
+
<div class="demo">
|
|
131
|
+
|
|
132
|
+
<!-- Top nav -->
|
|
133
|
+
<nav class="demo-nav">
|
|
134
|
+
<div class="demo-nav__left">
|
|
135
|
+
<span class="demo-nav__logo">🪶 feather-toast</span>
|
|
136
|
+
<span class="demo-nav__version">v1.0.0</span>
|
|
137
|
+
</div>
|
|
138
|
+
<a
|
|
139
|
+
href="https://github.com/zin-ix/feather-toast"
|
|
140
|
+
target="_blank"
|
|
141
|
+
rel="noopener noreferrer"
|
|
142
|
+
class="demo-nav__github"
|
|
143
|
+
aria-label="View on GitHub"
|
|
144
|
+
title="View on GitHub"
|
|
145
|
+
>
|
|
146
|
+
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
147
|
+
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
|
|
148
|
+
</svg>
|
|
149
|
+
GitHub
|
|
150
|
+
</a>
|
|
151
|
+
</nav>
|
|
152
|
+
|
|
153
|
+
<!-- Header -->
|
|
154
|
+
<header class="demo-header">
|
|
155
|
+
<div class="demo-header__badge">Vue 3 Plugin</div>
|
|
156
|
+
<h1 class="demo-header__title">feather-toast</h1>
|
|
157
|
+
<p class="demo-header__subtitle">
|
|
158
|
+
A lightweight, smooth toast notification plugin with minimalist design and spring-based animations.
|
|
159
|
+
</p>
|
|
160
|
+
<div class="demo-header__stats">
|
|
161
|
+
<span class="demo-stat">~6KB gzipped</span>
|
|
162
|
+
<span class="demo-stat__sep">·</span>
|
|
163
|
+
<span class="demo-stat">Zero deps</span>
|
|
164
|
+
<span class="demo-stat__sep">·</span>
|
|
165
|
+
<span class="demo-stat">TypeScript</span>
|
|
166
|
+
</div>
|
|
167
|
+
</header>
|
|
168
|
+
|
|
169
|
+
<!-- Install -->
|
|
170
|
+
<section class="demo-section">
|
|
171
|
+
<label class="demo-label">Install</label>
|
|
172
|
+
<div class="demo-code-block">
|
|
173
|
+
<span class="demo-code-prompt">$</span>
|
|
174
|
+
<code>npm install feather-toast</code>
|
|
175
|
+
<button class="demo-code-copy" @click="copyInstall" :class="{ copied: installCopied }" title="Copy">
|
|
176
|
+
<svg v-if="!installCopied" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
177
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
178
|
+
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
|
|
179
|
+
</svg>
|
|
180
|
+
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
181
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
182
|
+
</svg>
|
|
183
|
+
</button>
|
|
184
|
+
</div>
|
|
185
|
+
</section>
|
|
186
|
+
|
|
187
|
+
<!-- Usage -->
|
|
188
|
+
<section class="demo-section">
|
|
189
|
+
<label class="demo-label">Quick Start</label>
|
|
190
|
+
<div class="demo-usage-card">
|
|
191
|
+
|
|
192
|
+
<!-- Step 1 -->
|
|
193
|
+
<div class="demo-usage-step">
|
|
194
|
+
<span class="demo-usage-step__num">1</span>
|
|
195
|
+
<div class="demo-usage-step__body">
|
|
196
|
+
<p class="demo-usage-step__title">Register the plugin in <code class="demo-inline-code">main.ts</code></p>
|
|
197
|
+
<pre class="demo-code-pre"><code><span class="c-purple">import</span> <span class="c-white">{ createApp }</span> <span class="c-purple">from</span> <span class="c-green">'vue'</span>
|
|
198
|
+
<span class="c-purple">import</span> <span class="c-white">App</span> <span class="c-purple">from</span> <span class="c-green">'./App.vue'</span>
|
|
199
|
+
<span class="c-purple">import</span> <span class="c-white">{ ToastPlugin }</span> <span class="c-purple">from</span> <span class="c-green">'feather-toast/src/plugins/toast'</span>
|
|
200
|
+
<span class="c-purple">import</span> <span class="c-green">'feather-toast/src/assets/toast.css'</span>
|
|
201
|
+
|
|
202
|
+
<span class="c-purple">const</span> <span class="c-white">app =</span> <span class="c-blue">createApp</span><span class="c-white">(App)</span>
|
|
203
|
+
<span class="c-white">app</span><span class="c-muted">.</span><span class="c-blue">use</span><span class="c-white">(ToastPlugin)</span>
|
|
204
|
+
<span class="c-white">app</span><span class="c-muted">.</span><span class="c-blue">mount</span><span class="c-white">(</span><span class="c-green">'#app'</span><span class="c-white">)</span></code></pre>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<!-- Step 2 -->
|
|
209
|
+
<div class="demo-usage-step">
|
|
210
|
+
<span class="demo-usage-step__num">2</span>
|
|
211
|
+
<div class="demo-usage-step__body">
|
|
212
|
+
<p class="demo-usage-step__title">Place the container once in your root <code class="demo-inline-code">App.vue</code></p>
|
|
213
|
+
<pre class="demo-code-pre"><code><span class="c-muted"><!-- App.vue template --></span>
|
|
214
|
+
<span class="c-blue"><template></span>
|
|
215
|
+
<span class="c-blue"><RouterView</span> <span class="c-white">/></span>
|
|
216
|
+
<span class="c-blue"><ToastContainer</span> <span class="c-white">/></span> <span class="c-muted"><!-- add this --></span>
|
|
217
|
+
<span class="c-blue"></template></span></code></pre>
|
|
218
|
+
<p class="demo-usage-note">✦ No import needed — the plugin registers it globally.</p>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<!-- Step 3 -->
|
|
223
|
+
<div class="demo-usage-step">
|
|
224
|
+
<span class="demo-usage-step__num">3</span>
|
|
225
|
+
<div class="demo-usage-step__body">
|
|
226
|
+
<p class="demo-usage-step__title">Call <code class="demo-inline-code">useToast()</code> in any component</p>
|
|
227
|
+
<pre class="demo-code-pre"><code><span class="c-purple">import</span> <span class="c-white">{ useToast }</span> <span class="c-purple">from</span> <span class="c-green">'feather-toast/src/composables/useToast'</span>
|
|
228
|
+
|
|
229
|
+
<span class="c-purple">const</span> <span class="c-white">{ toast } =</span> <span class="c-blue">useToast</span><span class="c-white">()</span>
|
|
230
|
+
|
|
231
|
+
<span class="c-white">toast</span><span class="c-muted">.</span><span class="c-blue">success</span><span class="c-white">({ title:</span> <span class="c-green">'Changes saved'</span><span class="c-white">, description:</span> <span class="c-green">'All good!'</span> <span class="c-white">})</span>
|
|
232
|
+
<span class="c-white">toast</span><span class="c-muted">.</span><span class="c-blue">error</span><span class="c-white">({ title:</span> <span class="c-green">'Something went wrong'</span> <span class="c-white">})</span>
|
|
233
|
+
<span class="c-white">toast</span><span class="c-muted">.</span><span class="c-blue">promise</span><span class="c-white">(fetch(</span><span class="c-green">'/api/save'</span><span class="c-white">), {</span>
|
|
234
|
+
<span class="c-white">loading:</span> <span class="c-white">{ title:</span> <span class="c-green">'Saving...'</span> <span class="c-white">},</span>
|
|
235
|
+
<span class="c-white">success:</span> <span class="c-white">{ title:</span> <span class="c-green">'Saved!'</span> <span class="c-white">},</span>
|
|
236
|
+
<span class="c-white">error:</span> <span class="c-white">{ title:</span> <span class="c-green">'Failed'</span> <span class="c-white">},</span>
|
|
237
|
+
<span class="c-white">})</span></code></pre>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
</div>
|
|
242
|
+
</section>
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
<!-- Position Picker -->
|
|
246
|
+
<section class="demo-section">
|
|
247
|
+
<label class="demo-label">Position</label>
|
|
248
|
+
<div class="demo-position-grid">
|
|
249
|
+
<button
|
|
250
|
+
v-for="pos in positions"
|
|
251
|
+
:key="pos.value"
|
|
252
|
+
:class="['demo-position-btn', { 'demo-position-btn--active': selectedPosition === pos.value }]"
|
|
253
|
+
@click="selectedPosition = pos.value"
|
|
254
|
+
>
|
|
255
|
+
{{ pos.label }}
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
258
|
+
</section>
|
|
259
|
+
|
|
260
|
+
<!-- Variant Buttons -->
|
|
261
|
+
<section class="demo-section">
|
|
262
|
+
<label class="demo-label">Variants</label>
|
|
263
|
+
<div class="demo-btn-grid">
|
|
264
|
+
<button class="demo-btn" @click="fireDefault">
|
|
265
|
+
<span class="demo-btn__dot demo-btn__dot--default"></span>
|
|
266
|
+
Default
|
|
267
|
+
</button>
|
|
268
|
+
<button class="demo-btn" @click="fireSuccess">
|
|
269
|
+
<span class="demo-btn__dot demo-btn__dot--success"></span>
|
|
270
|
+
Success
|
|
271
|
+
</button>
|
|
272
|
+
<button class="demo-btn" @click="fireError">
|
|
273
|
+
<span class="demo-btn__dot demo-btn__dot--error"></span>
|
|
274
|
+
Error
|
|
275
|
+
</button>
|
|
276
|
+
<button class="demo-btn" @click="fireWarning">
|
|
277
|
+
<span class="demo-btn__dot demo-btn__dot--warning"></span>
|
|
278
|
+
Warning
|
|
279
|
+
</button>
|
|
280
|
+
<button class="demo-btn" @click="fireInfo">
|
|
281
|
+
<span class="demo-btn__dot demo-btn__dot--info"></span>
|
|
282
|
+
Info
|
|
283
|
+
</button>
|
|
284
|
+
<button class="demo-btn" @click="fireLoading">
|
|
285
|
+
<span class="demo-btn__dot demo-btn__dot--loading"></span>
|
|
286
|
+
Loading
|
|
287
|
+
</button>
|
|
288
|
+
</div>
|
|
289
|
+
</section>
|
|
290
|
+
|
|
291
|
+
<!-- Special Features -->
|
|
292
|
+
<section class="demo-section">
|
|
293
|
+
<label class="demo-label">Features</label>
|
|
294
|
+
<div class="demo-btn-grid demo-btn-grid--two">
|
|
295
|
+
<button class="demo-btn" @click="firePromise">
|
|
296
|
+
<svg class="demo-btn__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
297
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
298
|
+
</svg>
|
|
299
|
+
Promise
|
|
300
|
+
</button>
|
|
301
|
+
<button class="demo-btn" @click="fireWithAction">
|
|
302
|
+
<svg class="demo-btn__icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
303
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
304
|
+
</svg>
|
|
305
|
+
With Action
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
</section>
|
|
309
|
+
|
|
310
|
+
<!-- Hints -->
|
|
311
|
+
<footer class="demo-footer">
|
|
312
|
+
<p>Swipe horizontally on any toast to dismiss it.</p>
|
|
313
|
+
<p>Hover a toast to pause the auto-dismiss timer.</p>
|
|
314
|
+
</footer>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<ToastContainer />
|
|
318
|
+
</template>
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
<style>
|
|
323
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
324
|
+
|
|
325
|
+
*, *::before, *::after {
|
|
326
|
+
box-sizing: border-box;
|
|
327
|
+
margin: 0;
|
|
328
|
+
padding: 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
body {
|
|
332
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
333
|
+
background: #09090b;
|
|
334
|
+
color: #fafafa;
|
|
335
|
+
min-height: 100vh;
|
|
336
|
+
-webkit-font-smoothing: antialiased;
|
|
337
|
+
-moz-osx-font-smoothing: grayscale;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.demo {
|
|
341
|
+
max-width: 540px;
|
|
342
|
+
margin: 0 auto;
|
|
343
|
+
padding: 0 24px 64px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* ── Nav ── */
|
|
347
|
+
.demo-nav {
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
justify-content: space-between;
|
|
351
|
+
padding: 16px 0;
|
|
352
|
+
border-bottom: 1px solid #18181b;
|
|
353
|
+
margin-bottom: 48px;
|
|
354
|
+
position: sticky;
|
|
355
|
+
top: 0;
|
|
356
|
+
background: #09090b;
|
|
357
|
+
z-index: 10;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.demo-nav__left {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
gap: 8px;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.demo-nav__logo {
|
|
367
|
+
font-size: 14px;
|
|
368
|
+
font-weight: 600;
|
|
369
|
+
color: #fafafa;
|
|
370
|
+
letter-spacing: -0.01em;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.demo-nav__version {
|
|
374
|
+
font-size: 11px;
|
|
375
|
+
font-weight: 500;
|
|
376
|
+
color: #52525b;
|
|
377
|
+
background: #18181b;
|
|
378
|
+
border: 1px solid #27272a;
|
|
379
|
+
border-radius: 999px;
|
|
380
|
+
padding: 2px 8px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.demo-nav__github {
|
|
384
|
+
display: flex;
|
|
385
|
+
align-items: center;
|
|
386
|
+
gap: 6px;
|
|
387
|
+
font-size: 13px;
|
|
388
|
+
font-weight: 500;
|
|
389
|
+
color: #a1a1aa;
|
|
390
|
+
text-decoration: none;
|
|
391
|
+
padding: 6px 12px;
|
|
392
|
+
border: 1px solid #27272a;
|
|
393
|
+
border-radius: 8px;
|
|
394
|
+
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.demo-nav__github:hover {
|
|
398
|
+
color: #fafafa;
|
|
399
|
+
border-color: #3f3f46;
|
|
400
|
+
background: #18181b;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.demo-nav__github svg {
|
|
404
|
+
width: 16px;
|
|
405
|
+
height: 16px;
|
|
406
|
+
flex-shrink: 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* ── Header ── */
|
|
410
|
+
.demo-header {
|
|
411
|
+
text-align: center;
|
|
412
|
+
margin-bottom: 40px;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.demo-header__badge {
|
|
416
|
+
display: inline-block;
|
|
417
|
+
padding: 4px 12px;
|
|
418
|
+
font-size: 11px;
|
|
419
|
+
font-weight: 600;
|
|
420
|
+
letter-spacing: 0.05em;
|
|
421
|
+
text-transform: uppercase;
|
|
422
|
+
color: #a1a1aa;
|
|
423
|
+
border: 1px solid #27272a;
|
|
424
|
+
border-radius: 999px;
|
|
425
|
+
margin-bottom: 16px;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.demo-header__title {
|
|
429
|
+
font-size: 38px;
|
|
430
|
+
font-weight: 700;
|
|
431
|
+
letter-spacing: -0.03em;
|
|
432
|
+
color: #fafafa;
|
|
433
|
+
margin-bottom: 12px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.demo-header__subtitle {
|
|
437
|
+
font-size: 15px;
|
|
438
|
+
line-height: 1.6;
|
|
439
|
+
color: #71717a;
|
|
440
|
+
max-width: 380px;
|
|
441
|
+
margin: 0 auto 16px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.demo-header__stats {
|
|
445
|
+
display: flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
justify-content: center;
|
|
448
|
+
gap: 6px;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.demo-stat {
|
|
452
|
+
font-size: 12px;
|
|
453
|
+
font-weight: 500;
|
|
454
|
+
color: #52525b;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.demo-stat__sep {
|
|
458
|
+
font-size: 12px;
|
|
459
|
+
color: #3f3f46;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* ── Sections ── */
|
|
463
|
+
.demo-section {
|
|
464
|
+
margin-bottom: 28px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.demo-label {
|
|
468
|
+
display: block;
|
|
469
|
+
font-size: 11px;
|
|
470
|
+
font-weight: 600;
|
|
471
|
+
color: #52525b;
|
|
472
|
+
margin-bottom: 10px;
|
|
473
|
+
text-transform: uppercase;
|
|
474
|
+
letter-spacing: 0.07em;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* ── Install block ── */
|
|
478
|
+
.demo-code-block {
|
|
479
|
+
display: flex;
|
|
480
|
+
align-items: center;
|
|
481
|
+
gap: 10px;
|
|
482
|
+
background: #0e0e11;
|
|
483
|
+
border: 1px solid #27272a;
|
|
484
|
+
border-radius: 8px;
|
|
485
|
+
padding: 12px 14px;
|
|
486
|
+
font-family: 'Menlo', 'Monaco', 'Cascadia Code', monospace;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.demo-code-prompt {
|
|
490
|
+
color: #3f3f46;
|
|
491
|
+
font-size: 13px;
|
|
492
|
+
user-select: none;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.demo-code-block code {
|
|
496
|
+
flex: 1;
|
|
497
|
+
font-size: 13px;
|
|
498
|
+
color: #e4e4e7;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.demo-code-copy {
|
|
502
|
+
flex-shrink: 0;
|
|
503
|
+
display: flex;
|
|
504
|
+
align-items: center;
|
|
505
|
+
justify-content: center;
|
|
506
|
+
width: 28px;
|
|
507
|
+
height: 28px;
|
|
508
|
+
border: none;
|
|
509
|
+
border-radius: 6px;
|
|
510
|
+
background: transparent;
|
|
511
|
+
color: #52525b;
|
|
512
|
+
cursor: pointer;
|
|
513
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.demo-code-copy:hover {
|
|
517
|
+
background: #18181b;
|
|
518
|
+
color: #a1a1aa;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.demo-code-copy.copied {
|
|
522
|
+
color: #16a34a;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.demo-code-copy svg {
|
|
526
|
+
width: 14px;
|
|
527
|
+
height: 14px;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* ── Usage card ── */
|
|
531
|
+
.demo-usage-card {
|
|
532
|
+
background: #0e0e11;
|
|
533
|
+
border: 1px solid #27272a;
|
|
534
|
+
border-radius: 10px;
|
|
535
|
+
overflow: hidden;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.demo-usage-step {
|
|
539
|
+
display: flex;
|
|
540
|
+
gap: 14px;
|
|
541
|
+
padding: 16px;
|
|
542
|
+
border-bottom: 1px solid #18181b;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.demo-usage-step:last-child {
|
|
546
|
+
border-bottom: none;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.demo-usage-step__num {
|
|
550
|
+
flex-shrink: 0;
|
|
551
|
+
width: 22px;
|
|
552
|
+
height: 22px;
|
|
553
|
+
display: flex;
|
|
554
|
+
align-items: center;
|
|
555
|
+
justify-content: center;
|
|
556
|
+
font-size: 11px;
|
|
557
|
+
font-weight: 700;
|
|
558
|
+
color: #52525b;
|
|
559
|
+
border: 1px solid #27272a;
|
|
560
|
+
border-radius: 50%;
|
|
561
|
+
margin-top: 1px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.demo-usage-step__body {
|
|
565
|
+
flex: 1;
|
|
566
|
+
min-width: 0;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.demo-usage-step__title {
|
|
570
|
+
font-size: 12px;
|
|
571
|
+
font-weight: 500;
|
|
572
|
+
color: #71717a;
|
|
573
|
+
margin-bottom: 8px;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.demo-inline-code {
|
|
577
|
+
font-family: 'Menlo', 'Monaco', 'Cascadia Code', monospace;
|
|
578
|
+
font-size: 11px;
|
|
579
|
+
font-style: normal;
|
|
580
|
+
color: #a1a1aa;
|
|
581
|
+
background: #18181b;
|
|
582
|
+
border: 1px solid #27272a;
|
|
583
|
+
border-radius: 4px;
|
|
584
|
+
padding: 1px 5px;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.demo-usage-note {
|
|
588
|
+
font-size: 11px;
|
|
589
|
+
color: #52525b;
|
|
590
|
+
margin-top: 8px;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.demo-code-pre {
|
|
594
|
+
font-family: 'Menlo', 'Monaco', 'Cascadia Code', monospace;
|
|
595
|
+
font-size: 12px;
|
|
596
|
+
line-height: 1.7;
|
|
597
|
+
white-space: pre;
|
|
598
|
+
overflow-x: auto;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* Syntax colors */
|
|
602
|
+
.c-purple { color: #c084fc; }
|
|
603
|
+
.c-blue { color: #60a5fa; }
|
|
604
|
+
.c-green { color: #86efac; }
|
|
605
|
+
.c-white { color: #f4f4f5; }
|
|
606
|
+
.c-muted { color: #71717a; }
|
|
607
|
+
|
|
608
|
+
/* ── Position Grid ── */
|
|
609
|
+
.demo-position-grid {
|
|
610
|
+
display: grid;
|
|
611
|
+
grid-template-columns: repeat(3, 1fr);
|
|
612
|
+
gap: 6px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.demo-position-btn {
|
|
616
|
+
padding: 9px 8px;
|
|
617
|
+
font-size: 12px;
|
|
618
|
+
font-weight: 500;
|
|
619
|
+
font-family: inherit;
|
|
620
|
+
color: #71717a;
|
|
621
|
+
background: #0e0e11;
|
|
622
|
+
border: 1px solid #27272a;
|
|
623
|
+
border-radius: 8px;
|
|
624
|
+
cursor: pointer;
|
|
625
|
+
transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.demo-position-btn:hover {
|
|
629
|
+
color: #fafafa;
|
|
630
|
+
background: #18181b;
|
|
631
|
+
border-color: #3f3f46;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.demo-position-btn--active {
|
|
635
|
+
color: #fafafa;
|
|
636
|
+
background: #18181b;
|
|
637
|
+
border-color: #52525b;
|
|
638
|
+
box-shadow: 0 0 0 1px #52525b;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* ── Button Grid ── */
|
|
642
|
+
.demo-btn-grid {
|
|
643
|
+
display: grid;
|
|
644
|
+
grid-template-columns: repeat(3, 1fr);
|
|
645
|
+
gap: 6px;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.demo-btn-grid--two {
|
|
649
|
+
grid-template-columns: repeat(2, 1fr);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.demo-btn {
|
|
653
|
+
display: flex;
|
|
654
|
+
align-items: center;
|
|
655
|
+
justify-content: center;
|
|
656
|
+
gap: 8px;
|
|
657
|
+
padding: 11px 16px;
|
|
658
|
+
font-size: 13px;
|
|
659
|
+
font-weight: 500;
|
|
660
|
+
font-family: inherit;
|
|
661
|
+
color: #fafafa;
|
|
662
|
+
background: #0e0e11;
|
|
663
|
+
border: 1px solid #27272a;
|
|
664
|
+
border-radius: 8px;
|
|
665
|
+
cursor: pointer;
|
|
666
|
+
transition: background 0.15s ease, border-color 0.15s ease;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.demo-btn:hover {
|
|
670
|
+
background: #18181b;
|
|
671
|
+
border-color: #3f3f46;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.demo-btn:active {
|
|
675
|
+
transform: scale(0.97);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.demo-btn__dot {
|
|
679
|
+
width: 7px;
|
|
680
|
+
height: 7px;
|
|
681
|
+
border-radius: 50%;
|
|
682
|
+
flex-shrink: 0;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.demo-btn__dot--default { background: #52525b; }
|
|
686
|
+
.demo-btn__dot--success { background: #16a34a; }
|
|
687
|
+
.demo-btn__dot--error { background: #dc2626; }
|
|
688
|
+
.demo-btn__dot--warning { background: #d97706; }
|
|
689
|
+
.demo-btn__dot--info { background: #2563eb; }
|
|
690
|
+
.demo-btn__dot--loading { background: #71717a; }
|
|
691
|
+
|
|
692
|
+
.demo-btn__icon {
|
|
693
|
+
width: 15px;
|
|
694
|
+
height: 15px;
|
|
695
|
+
flex-shrink: 0;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/* ── Footer ── */
|
|
699
|
+
.demo-footer {
|
|
700
|
+
text-align: center;
|
|
701
|
+
padding-top: 24px;
|
|
702
|
+
margin-top: 4px;
|
|
703
|
+
border-top: 1px solid #18181b;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.demo-footer p {
|
|
707
|
+
font-size: 12px;
|
|
708
|
+
color: #3f3f46;
|
|
709
|
+
line-height: 1.9;
|
|
710
|
+
}
|
|
711
|
+
</style>
|