1 changed files with 90 additions and 0 deletions
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
--- |
||||
description: |
||||
globs: *.svelte |
||||
alwaysApply: false |
||||
--- |
||||
# Svelte Style |
||||
|
||||
Observe the the following style guidelines when programming Svelte components or SvelteKit pages: |
||||
|
||||
- Always use idiomatic Svelte 5 syntax and features. Svelte 5 idioms include: |
||||
- Runes, such as `$state`, `$derived`, `$effect`, and `$props`. |
||||
- Callback props. |
||||
- Snippets. |
||||
- Avoid using deprecated Svelte 4 syntax and features. Depecrated features include: |
||||
- Props declared via `export let`. |
||||
- Event handlers attached via the `on:` directive. |
||||
- Event dispatchers. |
||||
- Component slots. |
||||
- Remember that Svelte 5 state is deeply reactive. |
||||
- Mutating a state object automatically triggers reactivity in most cases. |
||||
- Avoid trying to trigger reactivity by reassigning state variables unless other options have failed. |
||||
- Write components in TypeScript, and prefer strong typing for variables, props, and function signatures. |
||||
- Limit component logic to rendering concerns. Extract business logic into separate TypeScript modules, and import functions and classes into Svelte components as needed. |
||||
|
||||
## Component Code Organization Example |
||||
|
||||
When writing or editing a Svelte component, organize the code according to the following template: |
||||
|
||||
``` |
||||
<script lang='ts'> |
||||
// Begin the script section with imports. |
||||
// Import only what is necessary. |
||||
import type { PublicationTree } from '$lib/data_structures/publication_tree'; |
||||
import { getContext } from 'svelte'; |
||||
import type { Asciidoctor } from 'asciidoctor'; |
||||
|
||||
// Define props immediately after imports. |
||||
// Strongly type the props object. |
||||
let { |
||||
address, |
||||
publicationType, |
||||
ref, |
||||
}: { |
||||
address: string, |
||||
publicationType: string, |
||||
ref: (ref: HTMLElement) => void, |
||||
} = $props(); |
||||
|
||||
// Import shared state via `getContext` next. |
||||
const publicationTree: PublicationTree = getContext('publicationTree'); |
||||
const asciidoctor: Asciidoctor = getContext('asciidoctor'); |
||||
|
||||
// Then define component state. |
||||
// Put `$state` definitions first, followed by `$derived`. |
||||
// If derived values depend on others, declare them in the order of derivation. |
||||
let leafEvent: Promise<NDKEvent | null> = $derived.by(async () => |
||||
await publicationTree.getEvent(address)); |
||||
|
||||
// Define any non-reactive variables after the reactive ones. |
||||
let sectionRef: HTMLElement; |
||||
|
||||
// Define component logic below any state declarations. |
||||
// Component logic may include functions or `$effect` runes. |
||||
$effect(() => { |
||||
// Some reactive logic... |
||||
}); |
||||
|
||||
// Lastly, define any lifecycle hooks, such as `onMount`, at the end of the `<script>` block. |
||||
onMount(() => { |
||||
// Some mount logic... |
||||
}); |
||||
</script> |
||||
|
||||
<!-- Insert any snippets before the component's regular markup. --> |
||||
{#snippet contentParagraph(content: string, publicationType: string, isSectionStart: boolean)} |
||||
<section class='whitespace-normal publication-leather'> |
||||
{@html content} |
||||
</section> |
||||
{/snippet} |
||||
|
||||
<!-- The component's markup is typically the last code within the component. --> |
||||
<section id={address} bind:this={sectionRef} class='publication-leather content-visibility-auto'> |
||||
{#await leafEvent} |
||||
{@render contentParagraph(leafEvent.content.toString(), publicationType ?? 'article', false)} |
||||
{/await} |
||||
</section> |
||||
|
||||
<!-- Style blocks, if needed, may be placed at the end of a component. --> |
||||
<!-- Since Tailwind is used, style blocks are usually avoided in favor of Tailwind utility classes. --> |
||||
``` |
||||
Loading…
Reference in new issue