ui-ux-consultant-cli 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/assets/ui-ux-consultant/SKILL.md +844 -0
- package/assets/ui-ux-consultant/references/accessibility.md +175 -0
- package/assets/ui-ux-consultant/references/alt-libraries.md +90 -0
- package/assets/ui-ux-consultant/references/animations.md +448 -0
- package/assets/ui-ux-consultant/references/catalog/colors.md +91 -0
- package/assets/ui-ux-consultant/references/catalog/fonts.md +363 -0
- package/assets/ui-ux-consultant/references/catalog/products.md +340 -0
- package/assets/ui-ux-consultant/references/catalog/styles.md +165 -0
- package/assets/ui-ux-consultant/references/components.md +1116 -0
- package/assets/ui-ux-consultant/references/patterns.md +600 -0
- package/assets/ui-ux-consultant/references/performance.md +198 -0
- package/assets/ui-ux-consultant/references/stacks/astro.md +382 -0
- package/assets/ui-ux-consultant/references/stacks/flutter.md +308 -0
- package/assets/ui-ux-consultant/references/stacks/html-tailwind.md +415 -0
- package/assets/ui-ux-consultant/references/stacks/jetpack-compose.md +333 -0
- package/assets/ui-ux-consultant/references/stacks/laravel.md +521 -0
- package/assets/ui-ux-consultant/references/stacks/nextjs.md +275 -0
- package/assets/ui-ux-consultant/references/stacks/nuxt-ui.md +384 -0
- package/assets/ui-ux-consultant/references/stacks/nuxtjs.md +264 -0
- package/assets/ui-ux-consultant/references/stacks/react-native.md +346 -0
- package/assets/ui-ux-consultant/references/stacks/react.md +268 -0
- package/assets/ui-ux-consultant/references/stacks/shadcn.md +485 -0
- package/assets/ui-ux-consultant/references/stacks/svelte.md +429 -0
- package/assets/ui-ux-consultant/references/stacks/swiftui.md +336 -0
- package/assets/ui-ux-consultant/references/stacks/threejs.md +366 -0
- package/assets/ui-ux-consultant/references/stacks/vue.md +272 -0
- package/assets/ui-ux-consultant/references/theming.md +701 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/package.json +51 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# Svelte 5 + SvelteKit — UI/UX Reference
|
|
2
|
+
|
|
3
|
+
## When to Read
|
|
4
|
+
Use this file when building UI with Svelte 5 (runes API) or SvelteKit. Covers reactivity, props, bindings, stores, transitions, routing, and form actions.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Recommended Libraries
|
|
9
|
+
|
|
10
|
+
| Library | Best for | Install |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| Shadcn-svelte | Copy-paste components | `npx shadcn-svelte@latest init` |
|
|
13
|
+
| Skeleton UI | Tailwind component system | `npm install @skeletonlabs/skeleton` |
|
|
14
|
+
| Melt UI | Headless accessible primitives | `npm install @melt-ui/svelte` |
|
|
15
|
+
| Paneforge | Resizable pane layouts | `npm install paneforge` |
|
|
16
|
+
| Superforms | SvelteKit form handling | `npm install sveltekit-superforms` |
|
|
17
|
+
| Floating UI | Tooltips, popovers, dropdowns | `npm install @floating-ui/dom` |
|
|
18
|
+
| Motion One | Animations | `npm install motion` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Style Recommendations
|
|
23
|
+
|
|
24
|
+
- **Consumer / lifestyle apps:** Skeleton UI + Aurora UI aesthetic
|
|
25
|
+
- **Minimal SaaS:** Shadcn-svelte + Flat Design
|
|
26
|
+
- **Custom design system:** Tailwind utilities directly, no component lib
|
|
27
|
+
- **Documentation:** Minimalism, dark mode first
|
|
28
|
+
- **Marketing / landing:** Motion-Driven + subtle gradients
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Svelte 5 Runes — Core Patterns
|
|
33
|
+
|
|
34
|
+
### Reactive State
|
|
35
|
+
|
|
36
|
+
```svelte
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
// $state — reactive primitive (replaces let with reactivity)
|
|
39
|
+
let count = $state(0);
|
|
40
|
+
let items = $state<string[]>([]);
|
|
41
|
+
let user = $state({ name: 'Alice', age: 30 });
|
|
42
|
+
|
|
43
|
+
// $derived — computed value (replaces $: reactive statements)
|
|
44
|
+
let doubled = $derived(count * 2);
|
|
45
|
+
let total = $derived(items.length);
|
|
46
|
+
let greeting = $derived(`Hello, ${user.name}`);
|
|
47
|
+
|
|
48
|
+
// $effect — side effect after render (replaces onMount + reactive blocks)
|
|
49
|
+
$effect(() => {
|
|
50
|
+
document.title = `Count: ${count}`;
|
|
51
|
+
return () => { document.title = 'App'; }; // cleanup on destroy
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Props
|
|
57
|
+
|
|
58
|
+
```svelte
|
|
59
|
+
<script lang="ts">
|
|
60
|
+
// Destructure $props() for typed props
|
|
61
|
+
let {
|
|
62
|
+
name,
|
|
63
|
+
count = 0, // default value
|
|
64
|
+
onchange,
|
|
65
|
+
class: className = '', // rename reserved words
|
|
66
|
+
...rest // pass-through props
|
|
67
|
+
}: {
|
|
68
|
+
name: string;
|
|
69
|
+
count?: number;
|
|
70
|
+
onchange: (v: string) => void;
|
|
71
|
+
class?: string;
|
|
72
|
+
} = $props();
|
|
73
|
+
</script>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Bindings
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<script lang="ts">
|
|
80
|
+
let value = $state('');
|
|
81
|
+
let checked = $state(false);
|
|
82
|
+
let selected = $state('option-a');
|
|
83
|
+
let el = $state<HTMLDivElement>(); // bind:this
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<input bind:value />
|
|
87
|
+
<input type="checkbox" bind:checked />
|
|
88
|
+
<select bind:value={selected}>
|
|
89
|
+
<option value="option-a">A</option>
|
|
90
|
+
</select>
|
|
91
|
+
<div bind:this={el}>Reference</div>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Two-Way Bindable Props (Svelte 5)
|
|
95
|
+
|
|
96
|
+
```svelte
|
|
97
|
+
<!-- Child.svelte -->
|
|
98
|
+
<script lang="ts">
|
|
99
|
+
let { value = $bindable('') } = $props();
|
|
100
|
+
</script>
|
|
101
|
+
<input bind:value />
|
|
102
|
+
|
|
103
|
+
<!-- Parent.svelte -->
|
|
104
|
+
<Child bind:value={parentValue} />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Top UX Patterns with Code
|
|
110
|
+
|
|
111
|
+
### 1. Conditional Rendering with Transitions
|
|
112
|
+
|
|
113
|
+
```svelte
|
|
114
|
+
<script lang="ts">
|
|
115
|
+
import { fade, fly, slide, scale } from 'svelte/transition';
|
|
116
|
+
import { quintOut } from 'svelte/easing';
|
|
117
|
+
let visible = $state(true);
|
|
118
|
+
let items = $state(['a', 'b', 'c']);
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
{#if visible}
|
|
122
|
+
<div transition:fade={{ duration: 200 }}>Fades in/out</div>
|
|
123
|
+
<div in:fly={{ y: -20, duration: 300 }} out:fade={{ duration: 150 }}>
|
|
124
|
+
Flies in, fades out
|
|
125
|
+
</div>
|
|
126
|
+
{/if}
|
|
127
|
+
|
|
128
|
+
<!-- List animations -->
|
|
129
|
+
{#each items as item (item)}
|
|
130
|
+
<div animate:flip={{ duration: 200 }} transition:slide>
|
|
131
|
+
{item}
|
|
132
|
+
</div>
|
|
133
|
+
{/each}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2. Async Data with Loading States
|
|
137
|
+
|
|
138
|
+
```svelte
|
|
139
|
+
<script lang="ts">
|
|
140
|
+
async function fetchUser(id: string) {
|
|
141
|
+
const res = await fetch(`/api/users/${id}`);
|
|
142
|
+
if (!res.ok) throw new Error('Not found');
|
|
143
|
+
return res.json();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let userPromise = $state(fetchUser('1'));
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
{#await userPromise}
|
|
150
|
+
<div class="skeleton h-8 w-48 rounded" />
|
|
151
|
+
{:then user}
|
|
152
|
+
<h2>{user.name}</h2>
|
|
153
|
+
{:catch error}
|
|
154
|
+
<p class="text-red-500">{error.message}</p>
|
|
155
|
+
{/await}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Reusable Modal Pattern
|
|
159
|
+
|
|
160
|
+
```svelte
|
|
161
|
+
<!-- Modal.svelte -->
|
|
162
|
+
<script lang="ts">
|
|
163
|
+
let { open = $bindable(false), title, children } = $props();
|
|
164
|
+
|
|
165
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
166
|
+
if (e.key === 'Escape') open = false;
|
|
167
|
+
}
|
|
168
|
+
</script>
|
|
169
|
+
|
|
170
|
+
<svelte:window on:keydown={handleKeydown} />
|
|
171
|
+
|
|
172
|
+
{#if open}
|
|
173
|
+
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
|
174
|
+
<button class="absolute inset-0 bg-black/50" onclick={() => open = false} />
|
|
175
|
+
<div class="relative z-10 bg-white rounded-xl p-6 shadow-xl max-w-md w-full"
|
|
176
|
+
role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
177
|
+
<h2 id="modal-title" class="text-lg font-semibold">{title}</h2>
|
|
178
|
+
{@render children?.()}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 4. Form Validation
|
|
185
|
+
|
|
186
|
+
```svelte
|
|
187
|
+
<script lang="ts">
|
|
188
|
+
import { superForm } from 'sveltekit-superforms';
|
|
189
|
+
import { zod } from 'sveltekit-superforms/adapters';
|
|
190
|
+
import { z } from 'zod';
|
|
191
|
+
|
|
192
|
+
const schema = z.object({
|
|
193
|
+
email: z.string().email(),
|
|
194
|
+
name: z.string().min(2),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const { form, errors, enhance } = superForm(data.form, {
|
|
198
|
+
validators: zod(schema),
|
|
199
|
+
});
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<form method="POST" use:enhance>
|
|
203
|
+
<label>
|
|
204
|
+
Email
|
|
205
|
+
<input bind:value={$form.email} name="email" type="email" />
|
|
206
|
+
{#if $errors.email}<span class="text-red-500">{$errors.email}</span>{/if}
|
|
207
|
+
</label>
|
|
208
|
+
<button type="submit">Submit</button>
|
|
209
|
+
</form>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 5. Context API (Component Tree State)
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// context.ts
|
|
216
|
+
import { setContext, getContext } from 'svelte';
|
|
217
|
+
|
|
218
|
+
const THEME_KEY = Symbol('theme');
|
|
219
|
+
|
|
220
|
+
export function setTheme(theme: 'light' | 'dark') {
|
|
221
|
+
setContext(THEME_KEY, { theme: $state(theme) });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function getTheme() {
|
|
225
|
+
return getContext<{ theme: 'light' | 'dark' }>(THEME_KEY);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 6. Stores (Svelte 4 / cross-component interop)
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// stores/cart.ts
|
|
233
|
+
import { writable, derived, get } from 'svelte/store';
|
|
234
|
+
|
|
235
|
+
interface CartItem { id: string; qty: number; price: number; }
|
|
236
|
+
|
|
237
|
+
const items = writable<CartItem[]>([]);
|
|
238
|
+
const total = derived(items, $items =>
|
|
239
|
+
$items.reduce((sum, i) => sum + i.qty * i.price, 0)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
function add(item: CartItem) {
|
|
243
|
+
items.update(current => {
|
|
244
|
+
const existing = current.find(i => i.id === item.id);
|
|
245
|
+
if (existing) return current.map(i => i.id === item.id ? { ...i, qty: i.qty + 1 } : i);
|
|
246
|
+
return [...current, item];
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const cart = { subscribe: items.subscribe, total, add };
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```svelte
|
|
254
|
+
<!-- Auto-subscribes and unsubscribes with $ prefix -->
|
|
255
|
+
<p>Items: {$cart.length}</p>
|
|
256
|
+
<p>Total: ${$cart.total}</p>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## SvelteKit Routing
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
src/routes/
|
|
265
|
+
+page.svelte # route component
|
|
266
|
+
+layout.svelte # wraps all child routes
|
|
267
|
+
+page.server.ts # server-only load + actions
|
|
268
|
+
+layout.server.ts # server-only layout load
|
|
269
|
+
+server.ts # API endpoint (GET/POST/etc.)
|
|
270
|
+
[slug]/ # dynamic segment
|
|
271
|
+
(group)/ # route group (no URL segment)
|
|
272
|
+
[[optional]]/ # optional segment
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Data Loading
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// +page.server.ts — runs server-side only
|
|
279
|
+
import type { PageServerLoad } from './$types';
|
|
280
|
+
|
|
281
|
+
export const load: PageServerLoad = async ({ params, fetch, locals, cookies }) => {
|
|
282
|
+
const user = await db.user.findUnique({ where: { id: params.id } });
|
|
283
|
+
if (!user) error(404, 'User not found');
|
|
284
|
+
return { user };
|
|
285
|
+
};
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```svelte
|
|
289
|
+
<!-- +page.svelte — typed data from load() -->
|
|
290
|
+
<script lang="ts">
|
|
291
|
+
import type { PageData } from './$types';
|
|
292
|
+
let { data }: { data: PageData } = $props();
|
|
293
|
+
</script>
|
|
294
|
+
<h1>{data.user.name}</h1>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Form Actions
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// +page.server.ts
|
|
301
|
+
import type { Actions } from './$types';
|
|
302
|
+
import { fail, redirect } from '@sveltejs/kit';
|
|
303
|
+
|
|
304
|
+
export const actions: Actions = {
|
|
305
|
+
create: async ({ request, locals }) => {
|
|
306
|
+
const data = await request.formData();
|
|
307
|
+
const title = data.get('title') as string;
|
|
308
|
+
|
|
309
|
+
if (!title) return fail(400, { error: 'Title required' });
|
|
310
|
+
|
|
311
|
+
await db.post.create({ data: { title, userId: locals.user.id } });
|
|
312
|
+
redirect(303, '/posts');
|
|
313
|
+
},
|
|
314
|
+
delete: async ({ request }) => {
|
|
315
|
+
const data = await request.formData();
|
|
316
|
+
await db.post.delete({ where: { id: data.get('id') as string } });
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```svelte
|
|
322
|
+
<!-- +page.svelte -->
|
|
323
|
+
<script lang="ts">
|
|
324
|
+
import { enhance } from '$app/forms';
|
|
325
|
+
let { form } = $props(); // ActionData from failed actions
|
|
326
|
+
</script>
|
|
327
|
+
|
|
328
|
+
<form method="POST" action="?/create" use:enhance>
|
|
329
|
+
<input name="title" required />
|
|
330
|
+
{#if form?.error}<p class="text-red-500">{form.error}</p>{/if}
|
|
331
|
+
<button type="submit">Create</button>
|
|
332
|
+
</form>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### API Routes
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// src/routes/api/users/+server.ts
|
|
339
|
+
import type { RequestHandler } from './$types';
|
|
340
|
+
import { json } from '@sveltejs/kit';
|
|
341
|
+
|
|
342
|
+
export const GET: RequestHandler = async ({ url }) => {
|
|
343
|
+
const users = await db.user.findMany();
|
|
344
|
+
return json(users);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
348
|
+
const body = await request.json();
|
|
349
|
+
const user = await db.user.create({ data: body });
|
|
350
|
+
return json(user, { status: 201 });
|
|
351
|
+
};
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Best Practices by Category
|
|
357
|
+
|
|
358
|
+
### Reactivity
|
|
359
|
+
- Use `$state` for all mutable values in Svelte 5
|
|
360
|
+
- Use `$derived` for computed values — never `$effect` for derivations
|
|
361
|
+
- `$effect` is for side effects only (logging, DOM manipulation, subscriptions)
|
|
362
|
+
- Always return cleanup from `$effect` when subscribing to events
|
|
363
|
+
- Prefer `$state` arrays/objects — they are deeply reactive via proxies
|
|
364
|
+
|
|
365
|
+
### Component Design
|
|
366
|
+
- Keep components under 150 lines; extract sub-components early
|
|
367
|
+
- Use `$props()` destructuring with TypeScript types always
|
|
368
|
+
- Use `$bindable()` sparingly — only for true two-way binding needs
|
|
369
|
+
- Prefer composition with `{@render children?.()}` over slots where possible
|
|
370
|
+
- Co-locate component logic in `<script>` not in separate stores
|
|
371
|
+
|
|
372
|
+
### Accessibility
|
|
373
|
+
- Always use semantic HTML: `<button>`, `<nav>`, `<main>`, `<article>`
|
|
374
|
+
- Add `role` and `aria-*` to custom interactive elements
|
|
375
|
+
- Ensure keyboard navigation: `on:keydown` handlers for custom widgets
|
|
376
|
+
- Use `transition:` instead of display toggling for screen reader compat
|
|
377
|
+
- Test with `svelte-check` — it catches a11y issues at compile time
|
|
378
|
+
|
|
379
|
+
### SvelteKit Specifics
|
|
380
|
+
- Use `+page.server.ts` for data that should never reach the client
|
|
381
|
+
- Prefer form actions over fetch for mutations (progressive enhancement)
|
|
382
|
+
- Use `locals` in `hooks.server.ts` for auth/session data
|
|
383
|
+
- `$app/navigation`: `goto()`, `invalidate()`, `preloadData()` for SPA feel
|
|
384
|
+
- Enable `prerender = true` on static pages for performance
|
|
385
|
+
|
|
386
|
+
### Styling
|
|
387
|
+
- Scoped styles in `<style>` block are local by default
|
|
388
|
+
- Use `:global()` sparingly for third-party component overrides
|
|
389
|
+
- Tailwind + shadcn-svelte is the recommended production pairing
|
|
390
|
+
- CSS custom properties work well with Svelte's scoped styles
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Common Anti-Patterns
|
|
395
|
+
|
|
396
|
+
1. **Mutating arrays without reassignment (Svelte 4)** — `items.push(x)` doesn't trigger reactivity in Svelte 4; use `items = [...items, x]`. In Svelte 5, `$state` arrays are proxy-wrapped and reactive to mutations.
|
|
397
|
+
|
|
398
|
+
2. **Using `$effect` for derived values** — `$effect` runs asynchronously after render. Use `$derived` for synchronous computed state.
|
|
399
|
+
|
|
400
|
+
3. **`export let` without defaults (Svelte 4)** — props may arrive as `undefined`. Always provide defaults or use TypeScript optional types.
|
|
401
|
+
|
|
402
|
+
4. **Missing `{#key}` when reusing components with different data** — without a key, Svelte reuses the DOM node and the component won't re-initialize. Use `{#key item.id}<Component />{/key}`.
|
|
403
|
+
|
|
404
|
+
5. **DOM manipulation in `onMount` when a binding suffices** — use `bind:this`, `bind:value`, `bind:clientWidth` instead of querySelector.
|
|
405
|
+
|
|
406
|
+
6. **Fetching in `+page.svelte` instead of `+page.server.ts`** — client-side fetch loses SSR, caching, and auth context benefits.
|
|
407
|
+
|
|
408
|
+
7. **`client:*` directives on every component (Astro crossover)** — in SvelteKit, all components are server-rendered by default; no directives needed.
|
|
409
|
+
|
|
410
|
+
8. **Large inline event handlers** — extract to named functions for readability and testability.
|
|
411
|
+
|
|
412
|
+
9. **Not using `use:enhance`** on forms — without it, SvelteKit forms do full page reloads and lose progressive enhancement.
|
|
413
|
+
|
|
414
|
+
10. **Reactive store values without `$` prefix in templates** — `{count}` renders the store object, not its value; use `{$count}`.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Performance Checklist
|
|
419
|
+
|
|
420
|
+
- [ ] Use `{#key item.id}` when component identity matters across data changes
|
|
421
|
+
- [ ] `svelte:options immutable={true}` for components receiving large immutable objects
|
|
422
|
+
- [ ] Lazy-load heavy routes: `import('./HeavyComponent.svelte')` with dynamic import
|
|
423
|
+
- [ ] Use built-in `svelte/transition` instead of JS animation libraries where possible
|
|
424
|
+
- [ ] `$derived` not `$effect` for computed values (avoids async render cycle)
|
|
425
|
+
- [ ] `loading="lazy"` on below-fold images
|
|
426
|
+
- [ ] SvelteKit prerendering: `export const prerender = true` for static pages
|
|
427
|
+
- [ ] `invalidate()` instead of full `goto()` for partial data refresh
|
|
428
|
+
- [ ] Avoid reactive statements that trigger on every keystroke without debounce
|
|
429
|
+
- [ ] Profile with Svelte DevTools — check for unnecessary re-renders
|