算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
frontend-svelte-patterns【设计与创意】Use this skill when the user says 'Svelte pattern', 'Svelte form', 'Svelte animation', 'Svelte transition', 'Svelte action', 'Svelte store pattern', 'Svelte data fetching'. This skill enforces: bind:value for form inputs, use:enhance for progressive enhancement, transition/animate directives for animations, use:action for reusable DOM behavior, and S…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Apply production patterns to Svelte 5 applications: forms with bind:value and progressive enhancement, animations via transition/animate directives, actions for reusable DOM behavior, and data fetching strategies.
Exact user phrases: "Svelte pattern", "Svelte form", "Svelte animation", "Svelte transition", "Svelte action", "Svelte store pattern", "Svelte data fetching".
Before activating, verify:
No file output. Produces code patterns for forms, animations, actions, and data fetching.
Code: show .svelte component patterns with script + template.
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
Code: 15 lines per example.
| Approach | Trade-off | When to Use |
|---|---|---|
| bind:value + manual submit handler | Full control, no magic | Simple forms, custom validation |
| use:enhance (SvelteKit) | Progressive enhancement, no JS fallback | SvelteKit form actions |
| Superforms + Zod | Auto validation, field errors, loading states | Complex forms with validation |
| SvelteKit load functions | SSR data fetching, streaming | Full-stack apps |
| onMount + fetch | CSR data fetching | Standalone Svelte (non-SvelteKit) |
| Transition directives | CSS-driven animation | Enter/leave animations |
| use:action | Reusable DOM behavior | Global event listeners, tooltips, portals |
Is the project using SvelteKit?
├── No -> bind:value + manual submit + fetch
└── Yes -> Is the form complex (many fields, validation)?
├── Yes -> Superforms + Zod + use:enhance
└── No -> use:enhance with basic validation
Is SvelteKit used?
├── Yes -> Use load() in +page.ts or +page.server.ts
│ ├── Public data -> universal load (+page.ts)
│ └── Private/DB data -> server load (+page.server.ts)
└── No -> onMount() + $state() + fetch()
What kind of animation?
├── Enter/leave single element -> transition: directive
├── List reorder -> animate:flip + keyed each
├── Shared element between routes -> SvelteKit crossfade
└── Continuous animation -> svelte/motion (tweened, spring)
<script>
import { superForm } from 'sveltekit-superforms/client'
import { schema } from './schema'
const { form, errors, enhance, submitting } = superForm(data.form, { schema })
</script>
<form method="POST" use:enhance>
<input name="email" type="email" bind:value={form.email} aria-invalid={!!errors.email} />
{#if errors.email}<span>{errors.email}</span>{/if}
<input name="password" type="password" bind:value={form.password} />
{#if errors.password}<span>{errors.password}</span>{/if}
<button type="submit" disabled={submitting}>
{submitting ? 'Submitting...' : 'Login'}
</button>
</form>
<script>
function tooltip(node: HTMLElement, text: string) {
const tip = document.createElement('div')
tip.className = 'tooltip'
tip.textContent = text
tip.style.cssText = 'position:absolute;background:#333;color:white;padding:4px 8px;border-radius:4px;'
function show() {
const rect = node.getBoundingClientRect()
tip.style.top = `${rect.top - tip.offsetHeight - 4}px`
tip.style.left = `${rect.left + rect.width / 2 - tip.offsetWidth / 2}px`
document.body.appendChild(tip)
}
function hide() { tip.remove() }
node.addEventListener('mouseenter', show)
node.addEventListener('mouseleave', hide)
return {
destroy() {
node.removeEventListener('mouseenter', show)
node.removeEventListener('mouseleave', hide)
hide()
},
update(newText: string) { tip.textContent = newText },
}
}
let message = $state('Hello')
</script>
<button use:tooltip={message}>Hover me</button>
<script>
import { fly, slide } from 'svelte/transition'
import { cubicOut } from 'svelte/easing'
let items = $state([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' },
])
function removeItem(id: number) {
items = items.filter(i => i.id !== id)
}
function addItem() {
items = [...items, { id: Date.now(), text: `Item ${items.length + 1}` }]
}
</script>
<button onclick={addItem}>Add</button>
{#each items as item (item.id)}
<div
transition:fly={{ y: -20, duration: 200, easing: cubicOut }}
animate:flip={{ duration: 300 }}
>
<span>{item.text}</span>
<button onclick={() => removeItem(item.id)}>x</button>
</div>
{/each}
<script>
let email = $state('')
let password = $state('')
let errors = $state<Record<string, string>>({})
</script>
<script>
let cart = $state<CartItem[]>([])
let total = $derived(cart.reduce((sum, i) => sum + i.price * i.quantity, 0))
let itemCount = $derived(cart.reduce((sum, i) => sum + i.quantity, 0))
</script>
<script>
let theme = $state('light')
$effect(() => {
localStorage.setItem('theme', theme)
document.documentElement.setAttribute('data-theme', theme)
})
</script>
<script>
// provider component
import { setContext } from 'svelte'
let user = $state<User | null>(null)
setContext('user', {
get user() { return user },
login: (u: User) => user = u,
logout: () => user = null,
})
</script>
<script>
// consumer component
import { getContext } from 'svelte'
let ctx = getContext<{ user: User | null; login: (u: User) => void; logout: () => void }>('user')
</script>
Svelte transitions compile to CSS animations. Use css option in custom transitions for GPU-accelerated properties (transform, opacity). Avoid animating layout-triggering properties (width, height, margin).
transition:fade — lightweight (opacity only)transition:slide — moderate (transform + overflow)transition:fly — moderate (transform + opacity)animate:flip — heavier (measures positions, uses transform)SvelteKit load functions run on the server for SSR, meaning no waterfall fetch on the client. Use +page.server.ts for DB queries and +page.ts for public API calls. Parallelize with Promise.all in load functions.
Always use keyed each blocks {#each items as item (item.id)} for efficient DOM updates and correct animation behavior.
npm run build # Adapter-specific build
npm run preview # Preview production build
npm run build # Output to dist/
Svelte scopes CSS automatically per component. Global styles go in app.html or imported via Vite.
import { render, screen, fireEvent } from '@testing-library/svelte'
import { describe, it, expect } from 'vitest'
import Counter from './Counter.svelte'
it('increments on click', async () => {
render(Counter, { props: { initial: 0 } })
await fireEvent.click(screen.getByRole('button'))
expect(screen.getByText('1')).toBeDefined()
})
import { describe, it, expect } from 'vitest'
import { actions } from './+page.server'
it('validates form data', async () => {
const formData = new FormData()
formData.set('email', 'invalid')
const result = await actions.default({ request: new Request('http://localhost', { method: 'POST', body: formData }) })
expect(result.status).toBe(400)
})
// Svelte 4: stores
import { writable, derived } from 'svelte/store'
const count = writable(0)
const doubled = derived(count, $c => $c * 2)
count.update(n => n + 1)
// Svelte 5: runes
let count = $state(0)
let doubled = $derived(count * 2)
count++
// React
const [count, setCount] = useState(0)
const doubled = useMemo(() => count * 2, [count])
useEffect(() => { document.title = String(count) }, [count])
// Svelte 5
let count = $state(0)
let doubled = $derived(count * 2)
$effect(() => { document.title = String(count) })
<!-- Anti-pattern: no key -->
{#each items as item}
<div transition:slide>{item.text}</div>
{/each}
<!-- Correct: keyed -->
{#each items as item (item.id)}
<div transition:slide animate:flip>{item.text}</div>
{/each}
In SvelteKit, use load() functions instead of onMount for initial data. onMount is for standalone Svelte only.
Action update functions should not trigger re-renders. Keep them focused on DOM manipulation.
Too many concurrent transitions cause jank. Use fade/slide with short durations for lists; reserve fly/scale for hero elements.
Svelte's bind:value is simpler. SvelteKit's use:enhance provides progressive enhancement out of the box.
Svelte transitions are CSS-driven with zero JS overhead. Framer Motion is JS-driven with larger bundle.
Both encapsulate DOM behavior. Svelte actions are simpler (function returning update/destroy).
| Package | Purpose |
|---|---|
| svelte | Framework core |
| @sveltejs/kit | Meta-framework |
| superforms | Zod form validation |
| svelte-motion | Spring animations |
| Felte | Form library |
<form onsubmit={handleSubmit}>
<input type="email" bind:value={email} aria-invalid={!!errors.email} />
{#if errors.email}<span>{errors.email}</span>{/if}
<input type="password" bind:value={password} />
<button type="submit">Login</button>
</form>
{#if visible}
<div transition:fade={{ duration: 300 }}>Fade</div>
<div transition:slide>Slide</div>
<div transition:fly={{ x: 200 }}>Fly in</div>
{/if}
{#each items as item (item.id)}
<div transition:slide animate:flip={{ duration: 300 }}>
{item.text}
</div>
{/each}
<div use:clickOutside={() => open = false}>
Menu content
</div>
onMount(async () => {
const res = await fetch('/api/users')
users = await res.json()
})
<form method="POST" use:enhance>
<input name="email" />
<button>Submit</button>
</form>
<script>
import { tweened } from 'svelte/motion'
const progress = tweened(0, { duration: 400 })
</script>
<progress value={$progress}></progress>
No artifact produced. Next skill: frontend-sveltekit if using SvelteKit. Carry forward: form binding patterns, transition/animate conventions, action patterns.
function createModule<T>(config: ModuleConfig): T { const dependencies = initializeDependencies(config); const module = new Module(dependencies); module.hooks.onInit(); return module as T; }
class ConfigBuilder { private config: AppConfig = new AppConfig(); withDatabase(url: string): ConfigBuilder { ... } withCache(ttl: number): ConfigBuilder { ... } withLogging(level: string): ConfigBuilder { ... } build(): AppConfig { return this.config; } }
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% | Critical | Rollback or fix |
| p95 latency | > 500ms | Warning | Profile and optimize |
| Uptime | < 99.9% | Critical | Investigate infrastructure |
| Memory usage | > 80% | Warning | Check for leaks |
| CPU usage | > 80% | Warning | Scale up or optimize |
Is the state used in a single component?
├── No → Should it persist across page loads?
│ ├── Yes → localStorage store or URL search params
│ └── No → Svelte writable store (shared module-level store)
└── Yes → Is it derived from props or other state?
├── Yes → $derived expression
└── No → $state with local component scope
Does it need async handling?
├── Yes → $state with promise resolution or $effect
└── No → Synchronous state only
Does the child need to project content?
├── No → Standard props-based component
└── Yes → Is the projected content static?
├── Yes → <slot> with fallback content
└── No → Named slots + slot props for dynamic content
Need to pass children through multiple levels?
├── Yes → Svelte snippets ({#snippet}) or stores
└── No → Direct slot usage with prop drilling
{@html userContent} bypasses Svelte's auto-escaping. Always sanitize HTML through DOMPurify. Never use {@html} with unsanitized user input. For rich text rendering, use a dedicated sanitized component.use:action functions have access to the element. Validate any data passed to action parameters. Actions in SSR must handle missing window/document. Clean up event listeners in destroy callback.FormData values - validate server-side. Use zod or superforms for schema validation. Sanitize output before rendering in {@html}.