feat(site-2): Split docs ()

* Update links

* Move blog to site/content

* Update site/content/docs/02-component-format.md

* Fix docs links

* Add global prettierrc

* Auto format

* Fix git merge artifact

* Fix errors

* Update html to svelte(remaining ones)

* Add 2 blog posts

* Modify prettierrc

* Minor design fix

* Switch package lock to spaces, prettier ignore

* Regenerate package lock

* prettier format

* Push

* Remove console.logs

* Minor fixes

* Fix search

* Fix heading <code> style

* Fix search some more

* Code cleanup

* Update deps

* Move content around

* Allow drafts

* Redirect logic

* Don't render anything on docs if /docs

* Shorten the regex patterns

* Fix some more

* Hack the build into working

* Modernize docs links

* Add content to introduction

* Modify the content

* fix content links

* Reduce duplication in redirect regex

* Differences from Kit page

* Fix link

* Make OnThisPage visible on all docs

* Misc changes

* Move differences page to introduction

* Run prettier

* Prerender examples api routes

* Modify introdution page

* replace positions of readonly and get

* Minor blog style enhancement

---------

Co-authored-by: Rich Harris <richard.a.harris@gmail.com>
pull/8453/head
Puru Vijay 2 years ago committed by GitHub
parent 95abaec32a
commit b569018fef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,6 @@
---
title: "What's new in Svelte: April 2023"
description: "Loads of new Svelte compiler features, plus Svelte Summit and SvelteHack"
description: 'Loads of new Svelte compiler features, plus Svelte Summit and SvelteHack'
author: Dani Sandoval
authorURL: https://dreamindani.com
---
@ -14,6 +14,7 @@ Don't forget! Svelte Summit Spring, Svelte's 6th virtual conference, will be hap
Now let's jump into this month's changes...
## What's new in Svelte
- A bunch of new features are now available as of **3.56.0**!
- Add `|stopImmediatePropagation` event modifier for `on:eventname` ([#5085](https://github.com/sveltejs/svelte/issues/5085), [Docs](https://svelte.dev/docs#template-syntax-element-directives-on-eventname))
- Add `axis` parameter to `slide` transition ([#6182](https://github.com/sveltejs/svelte/issues/6182), [Docs](https://svelte.dev/docs#run-time-svelte-transition-slide))
@ -31,15 +32,18 @@ Now let's jump into this month's changes...
For all the changes to the Svelte compiler, including unreleased changes, check out the [CHANGELOG](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md).
## What's new in SvelteKit
- You can now get all cookies for a request with `cookies.getAll` (**1.10.0**, [#9287](https://github.com/sveltejs/kit/pull/9287), [Docs](https://kit.svelte.dev/docs/types#public-types-cookies))
- Easily manage the submission status of (multiple) forms with the new exposed `submitter` parameter in `use:enhance` (**1.12.0**, [#9425](https://github.com/sveltejs/kit/pull/9425), [Docs](https://kit.svelte.dev/docs/types#public-types-submitfunction))
- The default error page now has dark mode styles (**1.13.0**, [#9460](https://github.com/sveltejs/kit/pull/9460))
- You can now omit types on all methods and variables with special meaning to SvelteKit and still benefit from full type safety! Read more about it in the [announcement blog post](https://svelte.dev/blog/zero-config-type-safety)
---
## Community Showcase
**Apps & Sites built with Svelte**
- [Peerbeer](https://peer.beer/) lets you share files peer-to-peer (p2p) without any third parties or data limits
- [unplaneted](https://unplaneted.com/) is an interface for exploring very large space images
- [PokeBook](https://github.com/pokegh0st/pokebook) is a digital notebook for writing poetry that provides a beautiful distraction-free environment and autosave
@ -55,6 +59,7 @@ For all the changes to the Svelte compiler, including unreleased changes, check
**Learning Resources**
_Featuring Svelte Contributors and Ambassadors_
- [Streaming, snapshots, and other new features since SvelteKit 1.0](https://svelte.dev/blog/streaming-snapshots-sveltekit) by Geoff Rich on the svelte.dev Blog
- [Dev Vlog: Rich Harris shows us what's new in Svelte and Kit, March 2023](https://www.youtube.com/watch?v=vgXgex5E-8g) from Svelte Society
- If you missed this one live, check out [the next one](https://www.youtube.com/watch?v=MJHO6FSioPI) - scheduled for April 5th
@ -71,13 +76,14 @@ _Featuring Svelte Contributors and Ambassadors_
- [2023 March 23 - SvelteKit 1.13.0, Vitest and Playwright overview](https://www.youtube.com/watch?v=vpbhsbg2otg)
_To Watch or Hear_
- [Full Stack SvelteKit App Deployment Using Vercel And Supabase For $0](https://www.youtube.com/watch?v=uAF4Yd-gddo) by Joy of Code
- [Why Is Svelte.js so Popular?](https://www.youtube.com/watch?v=73Y8Yyg54zc) by Prismic
- [Interactive Tables in SvelteKit with TanStack Table](https://www.youtube.com/watch?v=-Zuo3UWjjI8) by hartenfellerdev
- [SvelteKit + GraphQL with Houdini](https://www.youtube.com/watch?v=ADnaRwQZfqw&list=PLm0ILX0LGQk_220vvpsbyXH2VesRlCm-E) by Aftab Alam
_To Read_
- [Thoughts on Svelte](https://tyhopp.com/notes/thoughts-on-svelte) by Ty Hopp
- [Storybook](https://storybook.js.org/blog/storybook-for-sveltekit/) on why (and how) it supports SvelteKit
- [Svelte Authentication Tutorial with Authorizer](https://thethinks.vercel.app/blog/svelte-authorizer) by The Thinks
@ -89,18 +95,19 @@ _To Read_
- [Secure Authentication in Svelte using Hooks](https://dev.to/brewhousedigital/secure-authentication-in-svelte-using-hooks-k5j) by Brewhouse Digital
**Libraries, Tools & Components**
- [@vavite/node-loader](https://github.com/cyco130/vavite/tree/main/packages/node-loader) is a Node ESM loader that uses Vite to transpile modules to enable sourcemap and breakpoints support in SvelteKit (or any Vite) project
- [Inlang](https://github.com/inlang/inlang) is building i18n for SvelteKit and is [looking for feedback](https://www.reddit.com/r/sveltejs/comments/11ydtui/sveltekit_and_i18n_lets_finally_solve_this_never/)
- [Skeleton](https://www.skeleton.dev/) - the UI toolkit for Svelte and Tailwind - is now 1.0 🎉
- [SvelteKit-integrated-WebSocket](https://github.com/suhaildawood/SvelteKit-integrated-WebSocket) provides first-class support for WebSockets within SvelteKit by attaching a WebSocket server to the global state
- [Svelte Legos](https://github.com/ankurrsinghal/svelte-legos) is a collection of essential Svelte Composition Utilities
- [Svelte Legos](https://github.com/ankurrsinghal/svelte-legos) is a collection of essential Svelte Composition Utilities
- [svelte-stored-writable](https://github.com/efstajas/svelte-stored-writable) is a drop-in extension of Svelte's writable that additionally stores and restores its contents using localStorage.
- [svelte-virtual](https://github.com/ghostebony/svelte-virtual) provides Svelte components for efficiently rendering large lists.
- ChatGPT Clones and Starters
- [chatwithme.chat](https://github.com/kierangilliam/chatwithme.chat) is an open source ChatGPT UI
- [SlickGPT](https://github.com/ShipBit/slickgpt) is a light-weight "use-your-own-API-key" web client for the OpenAI API written in Svelte
- [AI Chat Bestie](https://github.com/KTruong008/aichatbestie) is an unofficial ChatGPT app
- [chatgpt-svelte](https://github.com/ichbtrv/chatgpt-svelte) is a simple UI for the ChatGPT Open AI API
- [chatgpt-svelte](https://github.com/ichbtrv/chatgpt-svelte) is a simple UI for the ChatGPT Open AI API
Thanks for reading! And don't forget to try your hand at the [Svelte Hackathon](https://hack.sveltesociety.dev/) 🧑‍💻

@ -1,11 +0,0 @@
---
title: Before we begin
---
This page contains detailed API reference documentation. It's intended to be a resource for people who already have some familiarity with Svelte.
If that's not you (yet), you may prefer to visit the [interactive tutorial](/tutorial) or the [examples](/examples) before consulting this reference.
Don't be shy about asking for help in the [Discord chatroom](https://svelte.dev/chat).
Using an older version of Svelte? Have a look at the [v2 docs](https://v2.svelte.dev).

@ -1,24 +0,0 @@
---
title: Getting started
---
---
To try Svelte in an interactive online environment you can try [the REPL](https://svelte.dev/repl) or [StackBlitz](https://node.new/svelte).
To create a project locally we recommend using [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team:
```
npm create svelte@latest myapp
cd myapp
npm install
npm run dev
```
SvelteKit will handle calling [the Svelte compiler](https://www.npmjs.com/package/svelte) to convert your `.svelte` files into `.js` files that create the DOM and `.css` files that style it. It also provides all the other pieces you need to build a web application such as a development server, routing, and deployment. [SvelteKit](https://kit.svelte.dev/) utilizes [Vite](https://vitejs.dev/) to build your code and handle server-side rendering (SSR). There are [plugins for all the major web bundlers](https://sveltesociety.dev/tools#bundling) to handle Svelte compilation, which will output `.js` and `.css` that you can insert into your HTML, but most others won't handle SSR.
If you don't need a full-fledged app framework and instead want to build a simple frontend-only site/app, you can also use Svelte (without Kit) with Vite by running `npm init vite` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS and CSS files inside the `dist` directory.
The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and there are integrations with various other [editors](https://sveltesociety.dev/tools#editor-support) and tools as well.
If you're having trouble, get help on [Discord](https://svelte.dev/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte).

@ -0,0 +1,49 @@
---
title: Introduction
---
Welcome to Svelte docs. These are for folks who are already familiar with Svelte and want to learn more about the details of how it works.
If that's not you (yet), you may prefer to visit the [interactive tutorial](/tutorial) or the [examples](/examples) before consulting this reference.
Don't be shy about asking for help in the [Discord chatroom](https://svelte.dev/chat).
Using an older version of Svelte? Have a look at the [v2 docs](https://v2.svelte.dev).
## Start a new project
To try Svelte in an interactive online environment you can try [the REPL](https://svelte.dev/repl) or [StackBlitz](https://node.new/svelte).
To create a project locally we recommend using [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team:
```
npm create svelte@latest myapp
cd myapp
npm install
npm run dev
```
SvelteKit will handle calling [the Svelte compiler](https://www.npmjs.com/package/svelte) to convert your `.svelte` files into `.js` files that create the DOM and `.css` files that style it. It also provides all the other pieces you need to build a web application such as a development server, routing, and deployment. [SvelteKit](https://kit.svelte.dev/) utilizes [Vite](https://vitejs.dev/) to build your code and handle server-side rendering (SSR). There are [plugins for all the major web bundlers](https://sveltesociety.dev/tools#bundling) to handle Svelte compilation, which will output `.js` and `.css` that you can insert into your HTML, but most others won't handle SSR.
### Alternatives to SvelteKit
If you don't need a full-fledged app framework and instead want to build a simple frontend-only site/app, you can also use Svelte (without Kit) with Vite by running `npm init vite` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS and CSS files inside the `dist` directory.
Notable Mentions:
- [Routify](https://www.routify.dev/)
- [Elder.js](https://elderjs.com/)
## Differences from SvelteKit
Svelte renders UI components. You can compose these components and render an entire page with just Svelte, but you need more than just Svelte to write an entire app.
SvelteKit provides basic functionality like a [router](https://kit.svelte.dev/glossary#routing) — which updates the UI when a link is clicked — and [server-side rendering (SSR)](https://kit.svelte.dev/glossary#ssr). But beyond that, building an app with all the modern best practices is fiendishly complicated. Those practices include [build optimizations](https://vitejs.dev/guide/features.html#build-optimizations), so that you load only the minimal required code; [offline support](https://kit.svelte.devservice-workers); [preloading](https://kit.svelte.dev/link-options#data-sveltekit-preload-data) pages before the user initiates navigation; [configurable rendering](https://kit.svelte.dev/page-options) that allows you to render different parts of your app on the server with [SSR](https://kit.svelte.dev/glossary#ssr), in the browser [client-side rendering](https://kit.svelte.dev/glossary#csr), or at build-time with [prerendering](https://kit.svelte.dev/glossary#prerendering); and many other things. SvelteKit does all the boring stuff for you so that you can get on with the creative part.
It reflects changes to your code in the browser instantly to provide a lightning-fast and feature-rich development experience by leveraging [Vite](https://vitejs.dev/) with a [Svelte plugin](https://github.com/sveltejs/vite-plugin-svelte) to do [Hot Module Replacement (HMR)](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#hot).
## Editor tooling
The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and there are integrations with various other [editors](https://sveltesociety.dev/tools#editor-support) and tools as well.
If you're having trouble, get help on [Discord](https://svelte.dev/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte).

@ -0,0 +1,3 @@
{
"title": "Getting Started"
}

@ -1,14 +1,12 @@
---
title: Component format
---
title: .svelte files
---
Components are the building blocks of Svelte applications. They are written into `.svelte` files, using a superset of HTML.
All three sections — script, styles and markup — are optional.
```sv
```svelte
<script>
// logic goes here
</script>
@ -20,17 +18,15 @@ All three sections — script, styles and markup — are optional.
</style>
```
### &lt;script&gt;
## &lt;script&gt;
A `<script>` block contains JavaScript that runs when a component instance is created. Variables declared (or imported) at the top level are 'visible' from the component's markup. There are four additional rules:
#### 1. `export` creates a component prop
---
### 1. `export` creates a component prop
Svelte uses the `export` keyword to mark a variable declaration as a _property_ or _prop_, which means it becomes accessible to consumers of the component (see the section on [attributes and props](/docs/template-syntax#attributes-and-props) for more information).
Svelte uses the `export` keyword to mark a variable declaration as a _property_ or _prop_, which means it becomes accessible to consumers of the component (see the section on [attributes and props](#attributes-and-props) for more information).
```sv
```svelte
<script>
export let foo;
@ -40,24 +36,20 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_
</script>
```
---
You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value.
In development mode (see the [compiler options](/docs/compile-time#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
```sv
```svelte
<script>
export let bar = 'optional default initial value';
export let baz = undefined;
</script>
```
---
If you export a `const`, `class` or `function`, it is readonly from outside the component. Functions are valid prop values, however, as shown below.
```sv
```svelte
<script>
// these are readonly
export const thisIs = 'readonly';
@ -67,17 +59,15 @@ If you export a `const`, `class` or `function`, it is readonly from outside the
}
// this is a prop
export let format = n => n.toFixed(2);
export let format = (n) => n.toFixed(2);
</script>
```
Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](/docs/template-syntax#component-directives-bind-this).
---
Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](/docs/component-directives#bind-this).
You can use reserved words as prop names.
```sv
```svelte
<script>
let className;
@ -87,19 +77,17 @@ You can use reserved words as prop names.
</script>
```
#### 2. Assignments are 'reactive'
---
### 2. Assignments are 'reactive'
To change component state and trigger a re-render, just assign to a locally declared variable.
Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect.
```sv
```svelte
<script>
let count = 0;
function handleClick () {
function handleClick() {
// calling this function will trigger an
// update if the markup references `count`
count = count + 1;
@ -107,29 +95,25 @@ Update expressions (`count += 1`) and property assignments (`obj.x = y`) have th
</script>
```
---
Because Svelte's reactivity is based on assignments, using array methods like `.push()` and `.splice()` won't automatically trigger updates. A subsequent assignment is required to trigger the update. This and more details can also be found in the [tutorial](/tutorial/updating-arrays-and-objects).
```sv
```svelte
<script>
let arr = [0, 1];
function handleClick () {
function handleClick() {
// this method call does not trigger an update
arr.push(2);
// this assignment will trigger an update
// if the markup references `arr`
arr = arr
arr = arr;
}
</script>
```
---
Svelte's `<script>` blocks are run only when the component is created, so assignments within a `<script>` block are not automatically run again when a prop updates. If you'd like to track changes to a prop, see the next example in the following section.
```sv
```svelte
<script>
export let person;
// this will only set `name` on component creation
@ -138,13 +122,13 @@ Svelte's `<script>` blocks are run only when the component is created, so assign
</script>
```
#### 3. `$:` marks a statement as reactive
### 3. `$:` marks a statement as reactive
---
Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the `$:` [JS label syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). Reactive statements run after other script code and before the component markup is rendered, whenever the values that they depend on have changed.
```sv
```svelte
<script>
export let title;
export let person;
@ -166,11 +150,9 @@ Any top-level statement (i.e. not inside a block or a function) can be made reac
</script>
```
---
Only values which directly appear within the `$:` block will become dependencies of the reactive statement. For example, in the code below `total` will only update when `x` changes, but not `y`.
```sv
```svelte
<script>
let x = 0;
let y = 0;
@ -183,27 +165,21 @@ Only values which directly appear within the `$:` block will become dependencies
</script>
Total: {total}
<button on:click={() => x++}>
Increment X
</button>
<button on:click={() => x++}> Increment X </button>
<button on:click={() => y++}>
Increment Y
</button>
<button on:click={() => y++}> Increment Y </button>
```
---
It is important to note that the reactive blocks are ordered via simple static analysis at compile time, and all the compiler looks at are the variables that are assigned to and used within the block itself, not in any functions called by them. This means that `yDependent` will not be updated when `x` is updated in the following example:
```sv
```svelte
<script>
let x = 0;
let y = 0;
const setY = (value) => {
y = value;
}
};
$: yDependent = y;
$: setY(x);
@ -212,11 +188,9 @@ It is important to note that the reactive blocks are ordered via simple static a
Moving the line `$: yDependent = y` below `$: setY(x)` will cause `yDependent` to be updated when `x` is updated.
---
If a statement consists entirely of an assignment to an undeclared variable, Svelte will inject a `let` declaration on your behalf.
```sv
```svelte
<script>
export let num;
@ -227,11 +201,9 @@ If a statement consists entirely of an assignment to an undeclared variable, Sve
</script>
```
#### 4. Prefix stores with `$` to access their values
### 4. Prefix stores with `$` to access their values
---
A _store_ is an object that allows reactive access to a value via a simple _store contract_. The [`svelte/store` module](/docs/run-time#svelte-store) contains minimal store implementations which fulfil this contract.
A _store_ is an object that allows reactive access to a value via a simple _store contract_. The [`svelte/store` module](/docs/svelte-store) contains minimal store implementations which fulfil this contract.
Any time you have a reference to a store, you can access its value inside a component by prefixing it with the `$` character. This causes Svelte to declare the prefixed variable, subscribe to the store at component initialization and unsubscribe when appropriate.
@ -241,7 +213,7 @@ Note that the store must be declared at the top level of the component — not i
Local variables (that do not represent store values) must _not_ have a `$` prefix.
```sv
```svelte
<script>
import { writable } from 'svelte/store';
@ -256,13 +228,13 @@ Local variables (that do not represent store values) must _not_ have a `$` prefi
</script>
```
##### Store contract
#### Store contract
```js
store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
```
You can create your own stores without relying on [`svelte/store`](/docs/run-time#svelte-store), by implementing the _store contract_:
You can create your own stores without relying on [`svelte/store`](/docs/svelte-store), by implementing the _store contract_:
1. A store must contain a `.subscribe` method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling `.subscribe`. All of a store's active subscription functions must later be synchronously called whenever the store's value changes.
2. The `.subscribe` method must return an unsubscribe function. Calling an unsubscribe function must stop its subscription, and its corresponding subscription function must not be called again by the store.
@ -270,9 +242,7 @@ You can create your own stores without relying on [`svelte/store`](/docs/run-tim
For interoperability with RxJS Observables, the `.subscribe` method is also allowed to return an object with an `.unsubscribe` method, rather than return the unsubscription function directly. Note however that unless `.subscribe` synchronously calls the subscription (which is not required by the Observable spec), Svelte will see the value of the store as `undefined` until it does.
### &lt;script context="module"&gt;
---
## &lt;script context="module"&gt;
A `<script>` tag with a `context="module"` attribute runs once when the module first evaluates, rather than for each component instance. Values declared in this block are accessible from a regular `<script>` (and the component markup) but not vice versa.
@ -280,9 +250,9 @@ You can `export` bindings from this block, and they will become exports of the c
You cannot `export default`, since the default export is the component itself.
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](/docs/run-time#svelte-store).
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](/docs/svelte-store).
```sv
```svelte
<script context="module">
let totalComponents = 0;
@ -299,15 +269,13 @@ You cannot `export default`, since the default export is the component itself.
</script>
```
### &lt;style&gt;
---
## &lt;style&gt;
CSS inside a `<style>` block will be scoped to that component.
This works by adding a class to affected elements, which is based on a hash of the component styles (e.g. `svelte-123xyz`).
```sv
```svelte
<style>
p {
/* this will only affect <p> elements in this component */
@ -316,11 +284,9 @@ This works by adding a class to affected elements, which is based on a hash of t
</style>
```
---
To apply styles to a selector globally, use the `:global(...)` modifier.
```sv
```svelte
<style>
:global(body) {
/* this will apply to <body> */
@ -345,8 +311,6 @@ To apply styles to a selector globally, use the `:global(...)` modifier.
</style>
```
---
If you want to make @keyframes that are accessible globally, you need to prepend your keyframe names with `-global-`.
The `-global-` part will be removed when compiled, and the keyframe then be referenced using just `my-animation-name` elsewhere in your code.
@ -359,8 +323,6 @@ The `-global-` part will be removed when compiled, and the keyframe then be refe
</style>
```
---
There should only be 1 top-level `<style>` tag per component.
However, it is possible to have `<style>` tag nested inside other elements or logic blocks.
@ -378,3 +340,134 @@ In that case, the `<style>` tag will be inserted as-is into the DOM, no scoping
</style>
</div>
```
## Tags
A lowercase tag, like `<div>`, denotes a regular HTML element. A capitalised tag, such as `<Widget>` or `<Namespace.Widget>`, indicates a _component_.
```svelte
<script>
import Widget from './Widget.svelte';
</script>
<div>
<Widget />
</div>
```
## Attributes and props
By default, attributes work exactly like their HTML counterparts.
```svelte
<div class="foo">
<button disabled>can't touch this</button>
</div>
```
As in HTML, values may be unquoted.
```svelte
<input type="checkbox" />
```
Attribute values can contain JavaScript expressions.
```svelte
<a href="page/{p}">page {p}</a>
```
Or they can _be_ JavaScript expressions.
```svelte
<button disabled={!clickable}>...</button>
```
Boolean attributes are included on the element if their value is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and excluded if it's [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy).
All other attributes are included unless their value is [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` or `undefined`).
```svelte
<input required={false} placeholder="This input field is not required" />
<div title={null}>This div has no title attribute</div>
```
An expression might include characters that would cause syntax highlighting to fail in regular HTML, so quoting the value is permitted. The quotes do not affect how the value is parsed:
```svelte
<button disabled={number !== 42}>...</button>
```
When the attribute name and value match (`name={name}`), they can be replaced with `{name}`.
```svelte
<!-- These are equivalent -->
<button {disabled}>...</button>
<button {disabled}>...</button>
```
By convention, values passed to components are referred to as _properties_ or _props_ rather than _attributes_, which are a feature of the DOM.
As with elements, `name={name}` can be replaced with the `{name}` shorthand.
```svelte
<Widget foo={bar} answer={42} text="hello" />
```
_Spread attributes_ allow many attributes or properties to be passed to an element or component at once.
An element or component can have multiple spread attributes, interspersed with regular ones.
```svelte
<Widget {...things} />
```
_`$$props`_ references all props that are passed to a component, including ones that are not declared with `export`. It is not generally recommended, as it is difficult for Svelte to optimise. But it can be useful in rare cases for example, when you don't know at compile time what props might be passed to a component.
```svelte
<Widget {...$$props} />
```
_`$$restProps`_ contains only the props which are _not_ declared with `export`. It can be used to pass down other unknown attributes to an element in a component. It shares the same optimisation problems as _`$$props`_, and is likewise not recommended.
```svelte
<input {...$$restProps} />
```
> The `value` attribute of an `input` element or its children `option` elements must not be set with spread attributes when using `bind:group` or `bind:checked`. Svelte needs to be able to see the element's `value` directly in the markup in these cases so that it can link it to the bound variable.
> Sometimes, the attribute order matters as Svelte sets attributes sequentially in JavaScript. For example, `<input type="range" min="0" max="1" value={0.5} step="0.1"/>`, Svelte will attempt to set the value to `1` (rounding up from 0.5 as the step by default is 1), and then set the step to `0.1`. To fix this, change it to `<input type="range" min="0" max="1" step="0.1" value={0.5}/>`.
> Another example is `<img src="..." loading="lazy" />`. Svelte will set the img `src` before making the img element `loading="lazy"`, which is probably too late. Change this to `<img loading="lazy" src="...">` to make the image lazily loaded.
## Text expressions
```svelte
{expression}
```
Text can also contain JavaScript expressions:
> If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses.
```svelte
<h1>Hello {name}!</h1>
<p>{a} + {b} = {a + b}.</p>
<div>{/^[A-Za-z ]+$/.test(value) ? x : y}</div>
```
## Comments
You can use HTML comments inside components.
```svelte
<!-- this is a comment! --><h1>Hello world</h1>
```
Comments beginning with `svelte-ignore` disable warnings for the next block of markup. Usually, these are accessibility warnings; make sure that you're disabling them for a good reason.
```svelte
<!-- svelte-ignore a11y-autofocus -->
<input bind:value={name} autofocus />
```

@ -0,0 +1,206 @@
---
title: Logic blocks
---
## {#if ...}
```svelte
{#if expression}...{/if}
```
```svelte
{#if expression}...{:else if expression}...{/if}
```
```svelte
{#if expression}...{:else}...{/if}
```
Content that is conditionally rendered can be wrapped in an if block.
```svelte
{#if answer === 42}
<p>what was the question?</p>
{/if}
```
Additional conditions can be added with `{:else if expression}`, optionally ending in an `{:else}` clause.
```svelte
{#if porridge.temperature > 100}
<p>too hot!</p>
{:else if 80 > porridge.temperature}
<p>too cold!</p>
{:else}
<p>just right!</p>
{/if}
```
(Blocks don't have to wrap elements, they can also wrap text within elements!)
## {#each ...}
```svelte
{#each expression as name}...{/each}
```
```svelte
{#each expression as name, index}...{/each}
```
```svelte
{#each expression as name (key)}...{/each}
```
```svelte
{#each expression as name, index (key)}...{/each}
```
```svelte
{#each expression as name}...{:else}...{/each}
```
Iterating over lists of values can be done with an each block.
```svelte
<h1>Shopping list</h1>
<ul>
{#each items as item}
<li>{item.name} x {item.qty}</li>
{/each}
</ul>
```
You can use each blocks to iterate over any array or array-like value — that is, any object with a `length` property.
An each block can also specify an _index_, equivalent to the second argument in an `array.map(...)` callback:
```svelte
{#each items as item, i}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change.
```svelte
{#each items as item (item.id)}
<li>{item.name} x {item.qty}</li>
{/each}
<!-- or with additional index value -->
{#each items as item, i (item.id)}
<li>{i + 1}: {item.name} x {item.qty}</li>
{/each}
```
You can freely use destructuring and rest patterns in each blocks.
```svelte
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```
An each block can also have an `{:else}` clause, which is rendered if the list is empty.
```svelte
{#each todos as todo}
<p>{todo.text}</p>
{:else}
<p>No tasks today!</p>
{/each}
```
## {#await ...}
```svelte
{#await expression}...{:then name}...{:catch name}...{/await}
```
```svelte
{#await expression}...{:then name}...{/await}
```
```svelte
{#await expression then name}...{/await}
```
```svelte
{#await expression catch name}...{/await}
```
Await blocks allow you to branch on the three possible states of a Promise — pending, fulfilled or rejected. In SSR mode, only the pending state will be rendered on the server.
```svelte
{#await promise}
<!-- promise is pending -->
<p>waiting for the promise to resolve...</p>
{:then value}
<!-- promise was fulfilled -->
<p>The value is {value}</p>
{:catch error}
<!-- promise was rejected -->
<p>Something went wrong: {error.message}</p>
{/await}
```
The `catch` block can be omitted if you don't need to render anything when the promise rejects (or no error is possible).
```svelte
{#await promise}
<!-- promise is pending -->
<p>waiting for the promise to resolve...</p>
{:then value}
<!-- promise was fulfilled -->
<p>The value is {value}</p>
{/await}
```
If you don't care about the pending state, you can also omit the initial block.
```svelte
{#await promise then value}
<p>The value is {value}</p>
{/await}
```
Similarly, if you only want to show the error state, you can omit the `then` block.
```svelte
{#await promise catch error}
<p>The error is {error}</p>
{/await}
```
## {#key ...}
```svelte
{#key expression}...{/key}
```
Key blocks destroy and recreate their contents when the value of an expression changes.
This is useful if you want an element to play its transition whenever a value changes.
```svelte
{#key value}
<div transition:fade>{value}</div>
{/key}
```
When used around components, this will cause them to be reinstantiated and reinitialised.
```svelte
{#key value}
<Component />
{/key}
```

@ -0,0 +1,84 @@
---
title: Special tags
---
## {@html ...}
```svelte
{@html expression}
```
In a text expression, characters like `<` and `>` are escaped; however, with HTML expressions, they're not.
The expression should be valid standalone HTML — `{@html "<div>"}content{@html "</div>"}` will _not_ work, because `</div>` is not valid HTML. It also will _not_ compile Svelte code.
> Svelte does not sanitize expressions before injecting HTML. If the data comes from an untrusted source, you must sanitize it, or you are exposing your users to an XSS vulnerability.
```svelte
<div class="blog-post">
<h1>{post.title}</h1>
{@html post.content}
</div>
```
## {@debug ...}
```svelte
{@debug}
```
```svelte
{@debug var1, var2, ..., varN}
```
The `{@debug ...}` tag offers an alternative to `console.log(...)`. It logs the values of specific variables whenever they change, and pauses code execution if you have devtools open.
```svelte
<script>
let user = {
firstname: 'Ada',
lastname: 'Lovelace'
};
</script>
{@debug user}
<h1>Hello {user.firstname}!</h1>
```
`{@debug ...}` accepts a comma-separated list of variable names (not arbitrary expressions).
```svelte
<!-- Compiles -->
{@debug user}
{@debug user1, user2, user3}
<!-- WON'T compile -->
{@debug user.firstname}
{@debug myArray[0]}
{@debug !isReady}
{@debug typeof user === 'object'}
```
The `{@debug}` tag without any arguments will insert a `debugger` statement that gets triggered when _any_ state changes, as opposed to the specified variables.
## {@const ...}
```svelte
{@const assignment}
```
The `{@const ...}` tag defines a local constant.
```svelte
<script>
export let boxes;
</script>
{#each boxes as box}
{@const area = box.width * box.height}
{box.width} * {box.height} = {area}
{/each}
```
`{@const}` is only allowed as direct child of `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<Component />` or `<svelte:fragment />`.

@ -0,0 +1,763 @@
---
title: Element directives
---
As well as attributes, elements can have _directives_, which control the element's behaviour in some way.
## on:_eventname_
```svelte
on:eventname={handler}
```
```svelte
on:eventname|modifiers={handler}
```
Use the `on:` directive to listen to DOM events.
```svelte
<script>
let count = 0;
function handleClick(event) {
count += 1;
}
</script>
<button on:click={handleClick}>
count: {count}
</button>
```
Handlers can be declared inline with no performance penalty. As with attributes, directive values may be quoted for the sake of syntax highlighters.
```svelte
<button on:click={() => (count += 1)}>
count: {count}
</button>
```
Add _modifiers_ to DOM events with the `|` character.
```svelte
<form on:submit|preventDefault={handleSubmit}>
<!-- the `submit` event's default is prevented,
so the page won't reload -->
</form>
```
The following modifiers are available:
- `preventDefault` — calls `event.preventDefault()` before running the handler
- `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
- `stopImmediatePropagation` - calls `event.stopImmediatePropagation()`, preventing other listeners of the same event from being fired.
- `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
- `nonpassive` — explicitly set `passive: false`
- `capture` — fires the handler during the _capture_ phase instead of the _bubbling_ phase
- `once` — remove the handler after the first time it runs
- `self` — only trigger handler if `event.target` is the element itself
- `trusted` — only trigger handler if `event.isTrusted` is `true`. I.e. if the event is triggered by a user action.
Modifiers can be chained together, e.g. `on:click|once|capture={...}`.
If the `on:` directive is used without a value, the component will _forward_ the event, meaning that a consumer of the component can listen for it.
```svelte
<button on:click> The component itself will emit the click event </button>
```
It's possible to have multiple event listeners for the same event:
```svelte
<script>
let counter = 0;
function increment() {
counter = counter + 1;
}
function track(event) {
trackEvent(event);
}
</script>
<button on:click={increment} on:click={track}>Click me!</button>
```
## bind:_property_
```svelte
bind:property={variable}
```
Data ordinarily flows down, from parent to child. The `bind:` directive allows data to flow the other way, from child to parent. Most bindings are specific to particular elements.
The simplest bindings reflect the value of a property, such as `input.value`.
```svelte
<input bind:value={name} />
<textarea bind:value={text} />
<input type="checkbox" bind:checked={yes} />
```
If the name matches the value, you can use shorthand.
```svelte
<!-- These are equivalent -->
<input bind:value />
<input bind:value />
```
Numeric input values are coerced; even though `input.value` is a string as far as the DOM is concerned, Svelte will treat it as a number. If the input is empty or invalid (in the case of `type="number"`), the value is `undefined`.
```svelte
<input type="number" bind:value={num} />
<input type="range" bind:value={num} />
```
On `<input>` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList). It is readonly.
```svelte
<label for="avatar">Upload a picture:</label>
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />
```
If you're using `bind:` directives together with `on:` directives, the order that they're defined in affects the value of the bound variable when the event handler is called.
```svelte
<script>
let value = 'Hello World';
</script>
<input
on:input={() => console.log('Old value:', value)}
bind:value
on:input={() => console.log('New value:', value)}
/>
```
Here we were binding to the value of a text input, which uses the `input` event. Bindings on other elements may use different events such as `change`.
## Binding `<select>` value
A `<select>` value binding corresponds to the `value` property on the selected `<option>`, which can be any value (not just strings, as is normally the case in the DOM).
```svelte
<select bind:value={selected}>
<option value={a}>a</option>
<option value={b}>b</option>
<option value={c}>c</option>
</select>
```
A `<select multiple>` element behaves similarly to a checkbox group. The bound variable is an array with an entry corresponding to the `value` property of each selected `<option>`.
```svelte
<select multiple bind:value={fillings}>
<option value="Rice">Rice</option>
<option value="Beans">Beans</option>
<option value="Cheese">Cheese</option>
<option value="Guac (extra)">Guac (extra)</option>
</select>
```
When the value of an `<option>` matches its text content, the attribute can be omitted.
```svelte
<select multiple bind:value={fillings}>
<option>Rice</option>
<option>Beans</option>
<option>Cheese</option>
<option>Guac (extra)</option>
</select>
```
Elements with the `contenteditable` attribute support the following bindings:
- [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
- [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText)
- [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
There are slight differences between each of these, read more about them [here](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText).
```svelte
<div contenteditable="true" bind:innerHTML={html} />
```
`<details>` elements support binding to the `open` property.
```svelte
<details bind:open={isOpen}>
<summary>Details</summary>
<p>Something small enough to escape casual notice.</p>
</details>
```
## Media element bindings
Media elements (`<audio>` and `<video>`) have their own set of bindings — seven _readonly_ ones...
- `duration` (readonly) — the total duration of the video, in seconds
- `buffered` (readonly) — an array of `{start, end}` objects
- `played` (readonly) — ditto
- `seekable` (readonly) — ditto
- `seeking` (readonly) — boolean
- `ended` (readonly) — boolean
- `readyState` (readonly) — number between (and including) 0 and 4
...and five _two-way_ bindings:
- `currentTime` — the current playback time in the video, in seconds
- `playbackRate` — how fast or slow to play the video, where 1 is 'normal'
- `paused` — this one should be self-explanatory
- `volume` — a value between 0 and 1
- `muted` — a boolean value indicating whether the player is muted
Videos additionally have readonly `videoWidth` and `videoHeight` bindings.
```svelte
<video
src={clip}
bind:duration
bind:buffered
bind:played
bind:seekable
bind:seeking
bind:ended
bind:readyState
bind:currentTime
bind:playbackRate
bind:paused
bind:volume
bind:muted
bind:videoWidth
bind:videoHeight
/>
```
## Image element bindings
Image elements (`<img>`) have two readonly bindings:
- `naturalWidth` (readonly) — the original width of the image, available after the image has loaded
- `naturalHeight` (readonly) — the original height of the image, available after the image has loaded
```svelte
<img
bind:naturalWidth
bind:naturalHeight
></img>
```
## Block-level element bindings
Block-level elements have 4 read-only bindings, measured using a technique similar to [this one](http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/):
- `clientWidth`
- `clientHeight`
- `offsetWidth`
- `offsetHeight`
```svelte
<div bind:offsetWidth={width} bind:offsetHeight={height}>
<Chart {width} {height} />
</div>
```
## bind:group
```svelte
bind:group={variable}
```
Inputs that work together can use `bind:group`.
```svelte
<script>
let tortilla = 'Plain';
let fillings = [];
</script>
<!-- grouped radio inputs are mutually exclusive -->
<input type="radio" bind:group={tortilla} value="Plain" />
<input type="radio" bind:group={tortilla} value="Whole wheat" />
<input type="radio" bind:group={tortilla} value="Spinach" />
<!-- grouped checkbox inputs populate an array -->
<input type="checkbox" bind:group={fillings} value="Rice" />
<input type="checkbox" bind:group={fillings} value="Beans" />
<input type="checkbox" bind:group={fillings} value="Cheese" />
<input type="checkbox" bind:group={fillings} value="Guac (extra)" />
```
## bind:this
```svelte
bind:this={dom_node}
```
To get a reference to a DOM node, use `bind:this`.
```svelte
<script>
import { onMount } from 'svelte';
let canvasElement;
onMount(() => {
const ctx = canvasElement.getContext('2d');
drawStuff(ctx);
});
</script>
<canvas bind:this={canvasElement} />
```
## class:_name_
```svelte
class:name={value}
```
```svelte
class:name
```
A `class:` directive provides a shorter way of toggling a class on an element.
```svelte
<!-- These are equivalent -->
<div class={active ? 'active' : ''}>...</div>
<div class:active>...</div>
<!-- Shorthand, for when name and value match -->
<div class:active>...</div>
<!-- Multiple class toggles can be included -->
<div class:active class:inactive={!active} class:isAdmin>...</div>
```
## style:_property_
```svelte
style:property={value}
```
```svelte
style:property="value"
```
```svelte
style:property
```
The `style:` directive provides a shorthand for setting multiple styles on an element.
```svelte
<!-- These are equivalent -->
<div style:color="red">...</div>
<div style="color: red;">...</div>
<!-- Variables can be used -->
<div style:color={myColor}>...</div>
<!-- Shorthand, for when property and variable name match -->
<div style:color>...</div>
<!-- Multiple styles can be included -->
<div style:color style:width="12rem" style:background-color={darkMode ? 'black' : 'white'}>...</div>
<!-- Styles can be marked as important -->
<div style:color="red">...</div>
```
When `style:` directives are combined with `style` attributes, the directives will take precedence:
```svelte
<div style="color: blue;" style:color="red">This will be red</div>
```
## use:_action_
```svelte
use:action
```
```svelte
use:action={parameters}
```
```js
action = (node: HTMLElement, parameters: any) => {
update?: (parameters: any) => void,
destroy?: () => void
}
```
Actions are functions that are called when an element is created. They can return an object with a `destroy` method that is called after the element is unmounted:
```svelte
<script>
function foo(node) {
// the node has been mounted in the DOM
return {
destroy() {
// the node has been removed from the DOM
}
};
}
</script>
<div use:foo />
```
An action can have a parameter. If the returned value has an `update` method, it will be called whenever that parameter changes, immediately after Svelte has applied updates to the markup.
> Don't worry about the fact that we're redeclaring the `foo` function for every component instance — Svelte will hoist any functions that don't depend on local state out of the component definition.
```svelte
<script>
export let bar;
function foo(node, bar) {
// the node has been mounted in the DOM
return {
update(bar) {
// the value of `bar` has changed
},
destroy() {
// the node has been removed from the DOM
}
};
}
</script>
<div use:foo={bar} />
```
## transition:_fn_
```svelte
transition:fn
```
```svelte
transition:fn={params}
```
```svelte
transition:fn|local
```
```svelte
transition:fn|local={params}
```
```js
transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
delay?: number,
duration?: number,
easing?: (t: number) => number,
css?: (t: number, u: number) => string,
tick?: (t: number, u: number) => void
}
```
A transition is triggered by an element entering or leaving the DOM as a result of a state change.
When a block is transitioning out, all elements inside the block, including those that do not have their own transitions, are kept in the DOM until every transition in the block has been completed.
The `transition:` directive indicates a _bidirectional_ transition, which means it can be smoothly reversed while the transition is in progress.
```svelte
{#if visible}
<div transition:fade>fades in and out</div>
{/if}
```
> By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](/docs/client-side-component-api).
## Transition parameters
Like actions, transitions can have parameters.
(The double `{{curlies}}` aren't a special syntax; this is an object literal inside an expression tag.)
```svelte
{#if visible}
<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}
```
## Custom transition functions
Transitions can use custom functions. If the returned object has a `css` function, Svelte will create a CSS animation that plays on the element.
The `t` argument passed to `css` is a value between `0` and `1` after the `easing` function has been applied. _In_ transitions run from `0` to `1`, _out_ transitions run from `1` to `0` in other words, `1` is the element's natural state, as though no transition had been applied. The `u` argument is equal to `1 - t`.
The function is called repeatedly _before_ the transition begins, with different `t` and `u` arguments.
```svelte
<script>
import { elasticOut } from 'svelte/easing';
export let visible;
function whoosh(node, params) {
const existingTransform = getComputedStyle(node).transform.replace('none', '');
return {
delay: params.delay || 0,
duration: params.duration || 400,
easing: params.easing || elasticOut,
css: (t, u) => `transform: ${existingTransform} scale(${t})`
};
}
</script>
{#if visible}
<div in:whoosh>whooshes in</div>
{/if}
```
A custom transition function can also return a `tick` function, which is called _during_ the transition with the same `t` and `u` arguments.
> If it's possible to use `css` instead of `tick`, do so — CSS animations can run off the main thread, preventing jank on slower devices.
```svelte
<script>
export let visible = false;
function typewriter(node, { speed = 1 }) {
const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
if (!valid) {
throw new Error(`This transition only works on elements with a single text node child`);
}
const text = node.textContent;
const duration = text.length / (speed * 0.01);
return {
duration,
tick: (t) => {
const i = ~~(text.length * t);
node.textContent = text.slice(0, i);
}
};
}
</script>
{#if visible}
<p in:typewriter={{ speed: 1 }}>The quick brown fox jumps over the lazy dog</p>
{/if}
```
If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making [crossfade effects](/tutorial/deferred-transitions) possible.
Transition functions also receive a third argument, `options`, which contains information about the transition.
Available values in the `options` object are:
- `direction` - one of `in`, `out`, or `both` depending on the type of transition
## Transition events
An element with transitions will dispatch the following events in addition to any standard DOM events:
- `introstart`
- `introend`
- `outrostart`
- `outroend`
```svelte
{#if visible}
<p
transition:fly={{ y: 200, duration: 2000 }}
on:introstart={() => (status = 'intro started')}
on:outrostart={() => (status = 'outro started')}
on:introend={() => (status = 'intro ended')}
on:outroend={() => (status = 'outro ended')}
>
Flies in and out
</p>
{/if}
```
Local transitions only play when the block they belong to is created or destroyed, _not_ when parent blocks are created or destroyed.
```svelte
{#if x}
{#if y}
<p transition:fade>fades in and out when x or y change</p>
<p transition:fade|local>fades in and out only when y changes</p>
{/if}
{/if}
```
## in:_fn_/out:_fn_
```svelte
in:fn
```
```svelte
in:fn={params}
```
```svelte
in:fn|local
```
```svelte
in:fn|local={params}
```
```svelte
out:fn
```
```svelte
out:fn={params}
```
```svelte
out:fn|local
```
```svelte
out:fn|local={params}
```
Similar to `transition:`, but only applies to elements entering (`in:`) or leaving (`out:`) the DOM.
Unlike with `transition:`, transitions applied with `in:` and `out:` are not bidirectional — an in transition will continue to 'play' alongside the out transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
```svelte
{#if visible}
<div in:fly out:fade>flies in, fades out</div>
{/if}
```
## animate:_fn_
```svelte
animate:name
```
```svelte
animate:name={params}
```
```js
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
delay?: number,
duration?: number,
easing?: (t: number) => number,
css?: (t: number, u: number) => string,
tick?: (t: number, u: number) => void
}
```
```ts
DOMRect {
bottom: number,
height: number,
left: number,
right: number,
top: number,
width: number,
x: number,
y: number
}
```
An animation is triggered when the contents of a [keyed each block](/docs/logic-blocks#each) are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an _immediate_ child of a keyed each block.
Animations can be used with Svelte's [built-in animation functions](/docs/svelte-animate) or [custom animation functions](/docs/element-directives#custom-transition-functions).
```svelte
<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
<li animate:flip>{item}</li>
{/each}
```
## Animation Parameters
As with actions and transitions, animations can have parameters.
(The double `{{curlies}}` aren't a special syntax; this is an object literal inside an expression tag.)
```svelte
{#each list as item, index (item)}
<li animate:flip={{ delay: 500 }}>{item}</li>
{/each}
```
## Custom animation functions
Animations can use custom functions that provide the `node`, an `animation` object and any `parameters` as arguments. The `animation` parameter is an object containing `from` and `to` properties each containing a [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect#Properties) describing the geometry of the element in its `start` and `end` positions. The `from` property is the DOMRect of the element in its starting position, and the `to` property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.
If the returned object has a `css` method, Svelte will create a CSS animation that plays on the element.
The `t` argument passed to `css` is a value that goes from `0` and `1` after the `easing` function has been applied. The `u` argument is equal to `1 - t`.
The function is called repeatedly _before_ the animation begins, with different `t` and `u` arguments.
```svelte
<script>
import { cubicOut } from 'svelte/easing';
function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}
```
A custom animation function can also return a `tick` function, which is called _during_ the animation with the same `t` and `u` arguments.
> If it's possible to use `css` instead of `tick`, do so — CSS animations can run off the main thread, preventing jank on slower devices.
```svelte
<script>
import { cubicOut } from 'svelte/easing';
function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
tick: (t, u) => Object.assign(node.style, { color: t > 0.5 ? 'Pink' : 'Blue' })
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}
```

@ -0,0 +1,109 @@
---
title: Component directives
---
## on:_eventname_
```svelte
on:eventname={handler}
```
Components can emit events using [createEventDispatcher](/docs/svelte#createeventdispatcher), or by forwarding DOM events. Listening for component events looks the same as listening for DOM events:
```svelte
<SomeComponent on:whatever={handler} />
```
As with DOM events, if the `on:` directive is used without a value, the component will _forward_ the event, meaning that a consumer of the component can listen for it.
```svelte
<SomeComponent on:whatever />
```
## --style-props
```svelte
--style-props="anycssvalue"
```
You can also pass styles as props to components for the purposes of theming, using CSS custom properties.
Svelte's implementation is essentially syntactic sugar for adding a wrapper element. This example:
```svelte
<Slider bind:value min={0} --rail-color="black" --track-color="rgb(0, 0, 255)" />
```
Desugars to this:
```svelte
<div style="display: contents; --rail-color: black; --track-color: rgb(0, 0, 255)">
<Slider bind:value min={0} max={100} />
</div>
```
**Note**: Since this is an extra `<div>`, beware that your CSS structure might accidentally target this. Be mindful of this added wrapper element when using this feature.
For SVG namespace, the example above desugars into using `<g>` instead:
```svelte
<g style="--rail-color: black; --track-color: rgb(0, 0, 255)">
<Slider bind:value min={0} max={100} />
</g>
```
**Note**: Since this is an extra `<g>`, beware that your CSS structure might accidentally target this. Be mindful of this added wrapper element when using this feature.
Svelte's CSS Variables support allows for easily themeable components:
```svelte
<!-- Slider.svelte -->
<style>
.potato-slider-rail {
background-color: var(--rail-color, var(--theme-color, 'purple'));
}
</style>
```
So you can set a high-level theme color:
```css
/* global.css */
html {
--theme-color: black;
}
```
Or override it at the consumer level:
```svelte
<Slider --rail-color="goldenrod" />
```
## bind:_property_
```svelte
bind:property={variable}
```
You can bind to component props using the same syntax as for elements.
```svelte
<Keypad bind:value={pin} />
```
## bind:this
```svelte
bind:this={component_instance}
```
Components also support `bind:this`, allowing you to interact with component instances programmatically.
> Note that we can't do `{cart.empty}` since `cart` is `undefined` when the button is first rendered and throws an error.
```svelte
<ShoppingCart bind:this={cart} />
<button on:click={() => cart.empty()}> Empty shopping cart </button>
```

@ -0,0 +1,335 @@
---
title: Special elements
---
## `<slot>`
```svelte
<slot><!-- optional fallback --></slot>
```
```svelte
<slot name="x"><!-- optional fallback --></slot>
```
```svelte
<slot prop={value} />
```
Components can have child content, in the same way that elements can.
The content is exposed in the child component using the `<slot>` element, which can contain fallback content that is rendered if no children are provided.
```svelte
<!-- Widget.svelte -->
<div>
<slot>
this fallback content will be rendered when no content is provided, like in the first example
</slot>
</div>
<!-- App.svelte -->
<Widget />
<!-- this component will render the default content -->
<Widget>
<p>this is some child content that will overwrite the default slot content</p>
</Widget>
```
Note: If you want to render regular `<slot>` element, You can use `<svelte:element this="slot" />`.
Note: If you want to render regular `<slot>` element, You can use `<svelte:element this="slot" />`.
### `<slot name="`_name_`">`
Named slots allow consumers to target specific areas. They can also have fallback content.
```svelte
<!-- Widget.svelte -->
<div>
<slot name="header">No header was provided</slot>
<p>Some content between header and footer</p>
<slot name="footer" />
</div>
<!-- App.svelte -->
<Widget>
<h1 slot="header">Hello</h1>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</Widget>
```
Components can be placed in a named slot using the syntax `<Component slot="name" />`.
In order to place content in a slot without using a wrapper element, you can use the special element `<svelte:fragment>`.
```svelte
<!-- Widget.svelte -->
<div>
<slot name="header">No header was provided</slot>
<p>Some content between header and footer</p>
<slot name="footer" />
</div>
<!-- App.svelte -->
<Widget>
<HeaderComponent slot="header" />
<svelte:fragment slot="footer">
<p>All rights reserved.</p>
<p>Copyright (c) 2019 Svelte Industries</p>
</svelte:fragment>
</Widget>
```
### $$slots
`$$slots` is an object whose keys are the names of the slots passed into the component by the parent. If the parent does not pass in a slot with a particular name, that name will not be present in `$$slots`. This allows components to render a slot (and other elements, like wrappers for styling) only if the parent provides it.
Note that explicitly passing in an empty named slot will add that slot's name to `$$slots`. For example, if a parent passes `<div slot="title" />` to a child component, `$$slots.title` will be truthy within the child.
```svelte
<!-- Card.svelte -->
<div>
<slot name="title" />
{#if $$slots.description}
<!-- This <hr> and slot will render only if a slot named "description" is provided. -->
<hr />
<slot name="description" />
{/if}
</div>
<!-- App.svelte -->
<Card>
<h1 slot="title">Blog Post Title</h1>
<!-- No slot named "description" was provided so the optional slot will not be rendered. -->
</Card>
```
### `<slot key={`_value_`}>`
Slots can be rendered zero or more times and can pass values _back_ to the parent using props. The parent exposes the values to the slot template using the `let:` directive.
The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}`, and `<slot {item}>` is equivalent to `<slot item={item}>`.
```svelte
<!-- FancyList.svelte -->
<ul>
{#each items as item}
<li class="fancy">
<slot prop={item} />
</li>
{/each}
</ul>
<!-- App.svelte -->
<FancyList {items} let:prop={thing}>
<div>{thing.text}</div>
</FancyList>
```
Named slots can also expose values. The `let:` directive goes on the element with the `slot` attribute.
```svelte
<!-- FancyList.svelte -->
<ul>
{#each items as item}
<li class="fancy">
<slot name="item" {item} />
</li>
{/each}
</ul>
<slot name="footer" />
<!-- App.svelte -->
<FancyList {items}>
<div slot="item" let:item>{item.text}</div>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</FancyList>
```
## `<svelte:self>`
The `<svelte:self>` element allows a component to include itself, recursively.
It cannot appear at the top level of your markup; it must be inside an if or each block or passed to a component's slot to prevent an infinite loop.
```svelte
<script>
export let count;
</script>
{#if count > 0}
<p>counting down... {count}</p>
<svelte:self count={count - 1} />
{:else}
<p>lift-off!</p>
{/if}
```
## `<svelte:component>`
```svelte
<svelte:component this={expression} />
```
The `<svelte:component>` element renders a component dynamically, using the component constructor specified as the `this` property. When the property changes, the component is destroyed and recreated.
If `this` is falsy, no component is rendered.
```svelte
<svelte:component this={currentSelection.component} foo={bar} />
```
## `<svelte:element>`
```svelte
<svelte:element this={expression} />
```
The `<svelte:element>` element lets you render an element of a dynamically specified type. This is useful for example when displaying rich text content from a CMS. Any properties and event listeners present will be applied to the element.
The only supported binding is `bind:this`, since the element type-specific bindings that Svelte does at build time (e.g. `bind:value` for input elements) do not work with a dynamic tag type.
If `this` has a nullish value, the element and its children will not be rendered.
If `this` is the name of a [void element](https://developer.mozilla.org/en-US/docs/Glossary/Void_element) (e.g., `br`) and `<svelte:element>` has child elements, a runtime error will be thrown in development mode.
```svelte
<script>
let tag = 'div';
export let handler;
</script>
<svelte:element this={tag} on:click={handler}>Foo</svelte:element>
```
## `<svelte:window>`
```svelte
<svelte:window on:event={handler} />
```
```svelte
<svelte:window bind:prop={value} />
```
The `<svelte:window>` element allows you to add event listeners to the `window` object without worrying about removing them when the component is destroyed, or checking for the existence of `window` when server-side rendering.
Unlike `<svelte:self>`, this element may only appear at the top level of your component and must never be inside a block or element.
```svelte
<script>
function handleKeydown(event) {
alert(`pressed the ${event.key} key`);
}
</script>
<svelte:window on:keydown={handleKeydown} />
```
You can also bind to the following properties:
- `innerWidth`
- `innerHeight`
- `outerWidth`
- `outerHeight`
- `scrollX`
- `scrollY`
- `online` — an alias for `window.navigator.onLine`
All except `scrollX` and `scrollY` are readonly.
```svelte
<svelte:window bind:scrollY={y} />
```
> Note that the page will not be scrolled to the initial value to avoid accessibility issues. Only subsequent changes to the bound variable of `scrollX` and `scrollY` will cause scrolling. However, if the scrolling behaviour is desired, call `scrollTo()` in `onMount()`.
## `<svelte:document>`
```svelte
<svelte:document on:event={handler}/>
```
Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document`, such as `visibilitychange`, which don't fire on `window`. It also lets you use [actions](/docs/element-directives#use-action) on `document`.
As with `<svelte:window>`, this element may only appear the top level of your component and must never be inside a block or element.
```svelte
<svelte:document
on:visibilitychange={handleVisibilityChange}
use:someAction
/>
```
## `<svelte:body>`
```svelte
<svelte:body on:event={handler} />
```
Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave`, which don't fire on `window`. It also lets you use [actions](/docs/element-directives#use-action) on the `<body>` element.
As with `<svelte:window>` and `<svelte:document>`, this element may only appear the top level of your component and must never be inside a block or element.
```svelte
<svelte:body on:mouseenter={handleMouseenter} on:mouseleave={handleMouseleave} use:someAction />
```
## `<svelte:head>`
```svelte
<svelte:head>...</svelte:head>
```
This element makes it possible to insert elements into `document.head`. During server-side rendering, `head` content is exposed separately to the main `html` content.
As with `<svelte:window>`, `<svelte:document>` and `<svelte:body>`, this element may only appear at the top level of your component and must never be inside a block or element.
```svelte
<svelte:head>
<link rel="stylesheet" href="/tutorial/dark-theme.css" />
</svelte:head>
```
## `<svelte:options>`
```svelte
<svelte:options option={value} />
```
The `<svelte:options>` element provides a place to specify per-component compiler options, which are detailed in the [compiler section](/docs/svelte-compiler#svelte-compile). The possible options are:
- `immutable={true}` — you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed
- `immutable={false}` — the default. Svelte will be more conservative about whether or not mutable objects have changed
- `accessors={true}` — adds getters and setters for the component's props
- `accessors={false}` — the default
- `namespace="..."` — the namespace where this component will be used, most commonly "svg"; use the "foreign" namespace to opt out of case-insensitive attribute names and HTML-specific warnings
- `tag="..."` — the name to use when compiling this component as a custom element
```svelte
<svelte:options tag="my-custom-element" />
```
## `<svelte:fragment>`
The `<svelte:fragment>` element allows you to place content in a [named slot](/docs/special-elements#slot-name-name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
```svelte
<!-- Widget.svelte -->
<div>
<slot name="header">No header was provided</slot>
<p>Some content between header and footer</p>
<slot name="footer" />
</div>
<!-- App.svelte -->
<Widget>
<h1 slot="header">Hello</h1>
<svelte:fragment slot="footer">
<p>All rights reserved.</p>
<p>Copyright (c) 2019 Svelte Industries</p>
</svelte:fragment>
</Widget>
```

@ -0,0 +1,3 @@
{
"title": "Template Syntax"
}

@ -0,0 +1,248 @@
---
title: svelte
---
The `svelte` package exposes [lifecycle functions](/tutorial/onmount) and the [context API](/tutorial/context-api).
## `onMount`
```js
onMount(callback: () => void)
```
```js
onMount(callback: () => () => void)
```
The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component's initialisation (but doesn't need to live _inside_ the component; it can be called from an external module).
`onMount` does not run inside a [server-side component](/docs/server-side-component-api).
```svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('the component has mounted');
});
</script>
```
If a function is returned from `onMount`, it will be called when the component is unmounted.
```svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
const interval = setInterval(() => {
console.log('beep');
}, 1000);
return () => clearInterval(interval);
});
</script>
```
> This behaviour will only work when the function passed to `onMount` _synchronously_ returns a value. `async` functions always return a `Promise`, and as such cannot _synchronously_ return a function.
## `beforeUpdate`
```js
beforeUpdate(callback: () => void)
```
Schedules a callback to run immediately before the component is updated after any state change.
> The first time the callback runs will be before the initial `onMount`
```svelte
<script>
import { beforeUpdate } from 'svelte';
beforeUpdate(() => {
console.log('the component is about to update');
});
</script>
```
## `afterUpdate`
```js
afterUpdate(callback: () => void)
```
Schedules a callback to run immediately after the component has been updated.
> The first time the callback runs will be after the initial `onMount`
```svelte
<script>
import { afterUpdate } from 'svelte';
afterUpdate(() => {
console.log('the component just updated');
});
</script>
```
## `onDestroy`
```js
onDestroy(callback: () => void)
```
Schedules a callback to run immediately before the component is unmounted.
Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the only one that runs inside a server-side component.
```svelte
<script>
import { onDestroy } from 'svelte';
onDestroy(() => {
console.log('the component is being destroyed');
});
</script>
```
## `tick`
```js
promise: Promise = tick();
```
Returns a promise that resolves once any pending state changes have been applied, or in the next microtask if there are none.
```svelte
<script>
import { beforeUpdate, tick } from 'svelte';
beforeUpdate(async () => {
console.log('the component is about to update');
await tick();
console.log('the component just updated');
});
</script>
```
## `setContext`
```js
setContext(key: any, context: any)
```
Associates an arbitrary `context` object with the current component and the specified `key` and returns that object. The context is then available to children of the component (including slotted content) with `getContext`.
Like lifecycle functions, this must be called during component initialisation.
```svelte
<script>
import { setContext } from 'svelte';
setContext('answer', 42);
</script>
```
> Context is not inherently reactive. If you need reactive values in context then you can pass a store into context, which _will_ be reactive.
## `getContext`
```js
context: any = getContext(key: any)
```
Retrieves the context that belongs to the closest parent component with the specified `key`. Must be called during component initialisation.
```svelte
<script>
import { getContext } from 'svelte';
const answer = getContext('answer');
</script>
```
## `hasContext`
```js
hasContext: boolean = hasContext(key: any)
```
Checks whether a given `key` has been set in the context of a parent component. Must be called during component initialisation.
```svelte
<script>
import { hasContext } from 'svelte';
if (hasContext('answer')) {
// do something
}
</script>
```
## `getAllContexts`
```js
contexts: Map<any, any> = getAllContexts()
```
Retrieves the whole context map that belongs to the closest parent component. Must be called during component initialisation. Useful, for example, if you programmatically create a component and want to pass the existing context to it.
```svelte
<script>
import { getAllContexts } from 'svelte';
const contexts = getAllContexts();
</script>
```
## `createEventDispatcher`
```js
dispatch: ((name: string, detail?: any, options?: DispatchOptions) => boolean) = createEventDispatcher();
```
Creates an event dispatcher that can be used to dispatch [component events](/docs/component-directives#on-eventname). Event dispatchers are functions that can take two arguments: `name` and `detail`.
Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data.
```svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('notify', 'detail value')}>Fire Event</button>
```
Events dispatched from child components can be listened to in their parent. Any data provided when the event was dispatched is available on the `detail` property of the event object.
```svelte
<script>
function callbackFunction(event) {
console.log(`Notify fired! Detail: ${event.detail}`);
}
</script>
<Child on:notify={callbackFunction} />
```
Events can be cancelable by passing a third parameter to the dispatch function. The function returns `false` if the event is cancelled with `event.preventDefault()`, otherwise it returns `true`.
```svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function notify() {
const shouldContinue = dispatch('notify', 'detail value', { cancelable: true });
if (shouldContinue) {
// no one called preventDefault
} else {
// a listener called preventDefault
}
}
</script>
```

@ -0,0 +1,194 @@
---
title: 'svelte/store'
---
The `svelte/store` module exports functions for creating [readable](/docs/svelte-store#readable), [writable](/docs/svelte-store#writable) and [derived](/docs/svelte-store#derived) stores.
Keep in mind that you don't _have_ to use these functions to enjoy the [reactive `$store` syntax](/docs/dot-svelte-files#4-prefix-stores-with-$-to-access-their-values) in your components. Any object that correctly implements `.subscribe`, unsubscribe, and (optionally) `.set` is a valid store, and will work both with the special syntax, and with Svelte's built-in [`derived` stores](/docs/svelte-store#derived).
This makes it possible to wrap almost any other reactive state handling library for use in Svelte. Read more about the [store contract](/docs/dot-svelte-files#4-prefix-stores-with-$-to-access-their-values) to see what a correct implementation looks like.
## `writable`
```js
store = writable(value?: any)
```
```js
store = writable(value?: any, start?: (set: (value: any) => void) => () => void)
```
Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional `set` and `update` methods.
`set` is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it.
`update` is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store.
```js
import { writable } from 'svelte/store';
const count = writable(0);
count.subscribe((value) => {
console.log(value);
}); // logs '0'
count.set(1); // logs '1'
count.update((n) => n + 1); // logs '2'
```
If a function is passed as the second argument, it will be called when the number of subscribers goes from zero to one (but not from one to two, etc). That function will be passed a `set` function which changes the value of the store. It must return a `stop` function that is called when the subscriber count goes from one to zero.
```js
import { writable } from 'svelte/store';
const count = writable(0, () => {
console.log('got a subscriber');
return () => console.log('no more subscribers');
});
count.set(1); // does nothing
const unsubscribe = count.subscribe((value) => {
console.log(value);
}); // logs 'got a subscriber', then '1'
unsubscribe(); // logs 'no more subscribers'
```
Note that the value of a `writable` is lost when it is destroyed, for example when the page is refreshed. However, you can write your own logic to sync the value to for example the `localStorage`.
## `readable`
```js
store = readable(value?: any, start?: (set: (value: any) => void) => () => void)
```
Creates a store whose value cannot be set from 'outside', the first argument is the store's initial value, and the second argument to `readable` is the same as the second argument to `writable`.
```js
import { readable } from 'svelte/store';
const time = readable(null, (set) => {
set(new Date());
const interval = setInterval(() => {
set(new Date());
}, 1000);
return () => clearInterval(interval);
});
```
## `derived`
```js
store = derived(a, callback: (a: any) => any)
```
```js
store = derived(a, callback: (a: any, set: (value: any) => void) => void | () => void, initial_value: any)
```
```js
store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)
```
```js
store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void) => void | () => void, initial_value: any)
```
Derives a store from one or more other stores. The callback runs initially when the first subscriber subscribes and then whenever the store dependencies change.
In the simplest version, `derived` takes a single store, and the callback returns a derived value.
```js
import { derived } from 'svelte/store';
const doubled = derived(a, ($a) => $a * 2);
```
The callback can set a value asynchronously by accepting a second argument, `set`, and calling it when appropriate.
In this case, you can also pass a third argument to `derived` — the initial value of the derived store before `set` is first called.
```js
import { derived } from 'svelte/store';
const delayed = derived(
a,
($a, set) => {
setTimeout(() => set($a), 1000);
},
'one moment...'
);
```
If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes.
```js
import { derived } from 'svelte/store';
const tick = derived(
frequency,
($frequency, set) => {
const interval = setInterval(() => {
set(Date.now());
}, 1000 / $frequency);
return () => {
clearInterval(interval);
};
},
'one moment...'
);
```
In both cases, an array of arguments can be passed as the first argument instead of a single store.
```js
import { derived } from 'svelte/store';
const summed = derived([a, b], ([$a, $b]) => $a + $b);
const delayed = derived([a, b], ([$a, $b], set) => {
setTimeout(() => set($a + $b), 1000);
});
```
## `readonly`
```js
readableStore = readonly(writableStore);
```
This simple helper function makes a store readonly. You can still subscribe to the changes from the original one using this new readable store.
```js
import { readonly } from 'svelte/store';
const writableStore = writable(1);
const readableStore = readonly(writableStore);
readableStore.subscribe(console.log);
writableStore.set(2); // console: 2
readableStore.set(2); // ERROR
```
## `get`
```js
value: any = get(store);
```
Generally, you should read the value of a store by subscribing to it and using the value as it changes over time. Occasionally, you may need to retrieve the value of a store to which you're not subscribed. `get` allows you to do so.
> This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
```js
import { get } from 'svelte/store';
const value = get(store);
```

@ -0,0 +1,141 @@
---
title: 'svelte/motion'
---
The `svelte/motion` module exports two functions, `tweened` and `spring`, for creating writable stores whose values change over time after `set` and `update`, rather than immediately.
## `tweened`
```js
store = tweened(value: any, options)
```
Tweened stores update their values over a fixed duration. The following options are available:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number` | `function`, default 400) — milliseconds the tween lasts
- `easing` (`function`, default `t => t`) — an [easing function](/docs/svelte-easing)
- `interpolate` (`function`) — see below
`store.set` and `store.update` can accept a second `options` argument that will override the options passed in upon instantiation.
Both functions return a Promise that resolves when the tween completes. If the tween is interrupted, the promise will never resolve.
Out of the box, Svelte will interpolate between two numbers, two arrays or two objects (as long as the arrays and objects are the same 'shape', and their 'leaf' properties are also numbers).
```svelte
<script>
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const size = tweened(1, {
duration: 300,
easing: cubicOut
});
function handleClick() {
// this is equivalent to size.update(n => n + 1)
$size += 1;
}
</script>
<button on:click={handleClick} style="transform: scale({$size}); transform-origin: 0 0">
embiggen
</button>
```
If the initial value is `undefined` or `null`, the first value change will take effect immediately. This is useful when you have tweened values that are based on props, and don't want any motion when the component first renders.
```js
const size = tweened(undefined, {
duration: 300,
easing: cubicOut
});
$: $size = big ? 100 : 10;
```
The `interpolate` option allows you to tween between _any_ arbitrary values. It must be an `(a, b) => t => value` function, where `a` is the starting value, `b` is the target value, `t` is a number between 0 and 1, and `value` is the result. For example, we can use the [d3-interpolate](https://github.com/d3/d3-interpolate) package to smoothly interpolate between two colours.
```svelte
<script>
import { interpolateLab } from 'd3-interpolate';
import { tweened } from 'svelte/motion';
const colors = ['rgb(255, 62, 0)', 'rgb(64, 179, 255)', 'rgb(103, 103, 120)'];
const color = tweened(colors[0], {
duration: 800,
interpolate: interpolateLab
});
</script>
{#each colors as c}
<button style="background-color: {c}; color: white; border: none;" on:click={(e) => color.set(c)}>
{c}
</button>
{/each}
<h1 style="color: {$color}">{$color}</h1>
```
## `spring`
```js
store = spring(value: any, options)
```
A `spring` store gradually changes to its target value based on its `stiffness` and `damping` parameters. Whereas `tweened` stores change their values over a fixed duration, `spring` stores change over a duration that is determined by their existing velocity, allowing for more natural-seeming motion in many situations. The following options are available:
- `stiffness` (`number`, default `0.15`) — a value between 0 and 1 where higher means a 'tighter' spring
- `damping` (`number`, default `0.8`) — a value between 0 and 1 where lower means a 'springier' spring
- `precision` (`number`, default `0.01`) — determines the threshold at which the spring is considered to have 'settled', where lower means more precise
All of the options above can be changed while the spring is in motion, and will take immediate effect.
```js
const size = spring(100);
size.stiffness = 0.3;
size.damping = 0.4;
size.precision = 0.005;
```
As with [`tweened`](/docs/svelte-motion#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles.
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.
```js
const coords = spring({ x: 50, y: 50 });
// updates the value immediately
coords.set({ x: 100, y: 200 }, { hard: true });
// preserves existing momentum for 1s
coords.update(
(target_coords, coords) => {
return { x: target_coords.x, y: coords.y };
},
{ soft: 1 }
);
```
[See a full example on the spring tutorial.](/tutorial/spring)
```svelte
<script>
import { spring } from 'svelte/motion';
const coords = spring(
{ x: 50, y: 50 },
{
stiffness: 0.1,
damping: 0.25
}
);
</script>
```
If the initial value is `undefined` or `null`, the first value change will take effect immediately, just as with `tweened` values (see above).
```js
const size = spring();
$: $size = big ? 100 : 10;
```

@ -0,0 +1,264 @@
---
title: 'svelte/transition'
---
The `svelte/transition` module exports seven functions: `fade`, `blur`, `fly`, `slide`, `scale`, `draw` and `crossfade`. They are for use with Svelte [`transitions`](/docs/element-directives#transition-fn).
## `fade`
```svelte
transition:fade={params}
```
```svelte
in:fade={params}
```
```svelte
out:fade={params}
```
Animates the opacity of an element from 0 to the current opacity for `in` transitions and from the current opacity to 0 for `out` transitions.
`fade` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number`, default 400) — milliseconds the transition lasts
- `easing` (`function`, default `linear`) — an [easing function](/docs/svelte-easing)
You can see the `fade` transition in action in the [transition tutorial](/tutorial/transition).
```svelte
<script>
import { fade } from 'svelte/transition';
</script>
{#if condition}
<div transition:fade={{ delay: 250, duration: 300 }}>fades in and out</div>
{/if}
```
## `blur`
```svelte
transition:blur={params}
```
```svelte
in:blur={params}
```
```svelte
out:blur={params}
```
Animates a `blur` filter alongside an element's opacity.
`blur` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number`, default 400) — milliseconds the transition lasts
- `easing` (`function`, default `cubicInOut`) — an [easing function](/docs/svelte-easing)
- `opacity` (`number`, default 0) - the opacity value to animate out to and in from
- `amount` (`number | string`, default 5) - the size of the blur. Supports css units (for example: `"4rem"`). The default unit is `px`
```svelte
<script>
import { blur } from 'svelte/transition';
</script>
{#if condition}
<div transition:blur={{ amount: 10 }}>fades in and out</div>
{/if}
```
## `fly`
```svelte
transition:fly={params}
```
```svelte
in:fly={params}
```
```svelte
out:fly={params}
```
Animates the x and y positions and the opacity of an element. `in` transitions animate from the provided values, passed as parameters to the element's default values. `out` transitions animate from the element's default values to the provided values.
`fly` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number`, default 400) — milliseconds the transition lasts
- `easing` (`function`, default `cubicOut`) — an [easing function](/docs/svelte-easing)
- `x` (`number | string`, default 0) - the x offset to animate out to and in from
- `y` (`number | string`, default 0) - the y offset to animate out to and in from
- `opacity` (`number`, default 0) - the opacity value to animate out to and in from
x and y use `px` by default but support css units, for example `x: '100vw'` or `y: '50%'`.
You can see the `fly` transition in action in the [transition tutorial](/tutorial/adding-parameters-to-transitions).
```svelte
<script>
import { fly } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div
transition:fly={{ delay: 250, duration: 300, x: 100, y: 500, opacity: 0.5, easing: quintOut }}
>
flies in and out
</div>
{/if}
```
## `slide`
```svelte
transition:slide={params}
```
```svelte
in:slide={params}
```
```svelte
out:slide={params}
```
Slides an element in and out.
`slide` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number`, default 400) — milliseconds the transition lasts
- `easing` (`function`, default `cubicOut`) — an [easing function](/docs/svelte-easing)
* `axis` (`x` | `y`, default `y`) — the axis of motion along which the transition occurs
```svelte
<script>
import { slide } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div transition:slide={{ delay: 250, duration: 300, easing: quintOut, axis: 'x' }}>
slides in and out horizontally
</div>
{/if}
```
## `scale`
```svelte
transition:scale={params}
```
```svelte
in:scale={params}
```
```svelte
out:scale={params}
```
Animates the opacity and scale of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values.
`scale` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number`, default 400) — milliseconds the transition lasts
- `easing` (`function`, default `cubicOut`) — an [easing function](/docs/svelte-easing)
- `start` (`number`, default 0) - the scale value to animate out to and in from
- `opacity` (`number`, default 0) - the opacity value to animate out to and in from
```svelte
<script>
import { scale } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div transition:scale={{ duration: 500, delay: 500, opacity: 0.5, start: 0.5, easing: quintOut }}>
scales in and out
</div>
{/if}
```
## `draw`
```svelte
transition:draw={params}
```
```svelte
in:draw={params}
```
```svelte
out:draw={params}
```
Animates the stroke of an SVG element, like a snake in a tube. `in` transitions begin with the path invisible and draw the path to the screen over time. `out` transitions start in a visible state and gradually erase the path. `draw` only works with elements that have a `getTotalLength` method, like `<path>` and `<polyline>`.
`draw` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `speed` (`number`, default undefined) - the speed of the animation, see below.
- `duration` (`number` | `function`, default 800) — milliseconds the transition lasts
- `easing` (`function`, default `cubicInOut`) — an [easing function](/docs/svelte-easing)
The `speed` parameter is a means of setting the duration of the transition relative to the path's length. It is a modifier that is applied to the length of the path: `duration = length / speed`. A path that is 1000 pixels with a speed of 1 will have a duration of `1000ms`, setting the speed to `0.5` will double that duration and setting it to `2` will halve it.
```svelte
<script>
import { draw } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
<svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
{#if condition}
<path
transition:draw={{ duration: 5000, delay: 500, easing: quintOut }}
d="M2 1 h1 v1 h1 v1 h-1 v1 h-1 v-1 h-1 v-1 h1 z"
fill="none"
stroke="cornflowerblue"
stroke-width="0.1px"
stroke-linejoin="round"
/>
{/if}
</svg>
```
## `crossfade`
The `crossfade` function creates a pair of [transitions](/docs/element-directives#transition-fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used.
`crossfade` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number` | `function`, default 800) — milliseconds the transition lasts
- `easing` (`function`, default `cubicOut`) — an [easing function](/docs/svelte-easing)
- `fallback` (`function`) — A fallback [transition](/docs/element-directives#transition-fn) to use for send when there is no matching element being received, and for receive when there is no element being sent.
```svelte
<script>
import { crossfade } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
const [send, receive] = crossfade({
duration: 1500,
easing: quintOut
});
</script>
{#if condition}
<h1 in:send={{ key }} out:receive={{ key }}>BIG ELEM</h1>
{:else}
<small in:send={{ key }} out:receive={{ key }}>small elem</small>
{/if}
```

@ -0,0 +1,41 @@
---
title: 'svelte/animate'
---
The `svelte/animate` module exports one function for use with Svelte [animations](/docs/element-directives#animate-fn).
## `flip`
```svelte
animate:flip={params}
```
The `flip` function calculates the start and end position of an element and animates between them, translating the `x` and `y` values. `flip` stands for [First, Last, Invert, Play](https://aerotwist.com/blog/flip-your-animations/).
`flip` accepts the following parameters:
- `delay` (`number`, default 0) — milliseconds before starting
- `duration` (`number` | `function`, default `d => Math.sqrt(d) * 120`) — see below
- `easing` (`function`, default `cubicOut`) — an [easing function](/docs/svelte-easing)
`duration` can be provided as either:
- a `number`, in milliseconds.
- a function, `distance: number => duration: number`, receiving the distance the element will travel in pixels and returning the duration in milliseconds. This allows you to assign a duration that is relative to the distance travelled by each element.
You can see a full example on the [animations tutorial](/tutorial/animate)
```svelte
<script>
import { flip } from 'svelte/animate';
import { quintOut } from 'svelte/easing';
let list = [1, 2, 3];
</script>
{#each list as n (n)}
<div animate:flip={{ delay: 250, duration: 250, easing: quintOut }}>
{n}
</div>
{/each}
```

@ -0,0 +1,31 @@
---
title: 'svelte/easing'
---
Easing functions specify the rate of change over time and are useful when working with Svelte's built-in transitions and animations as well as the tweened and spring utilities. `svelte/easing` contains 31 named exports, a `linear` ease and 3 variants of 10 different easing functions: `in`, `out` and `inOut`.
You can explore the various eases using the [ease visualiser](/examples/easing) in the [examples section](/examples).
| ease | in | out | inOut |
| ----------- | ----------- | ------------ | -------------- |
| **back** | `backIn` | `backOut` | `backInOut` |
| **bounce** | `bounceIn` | `bounceOut` | `bounceInOut` |
| **circ** | `circIn` | `circOut` | `circInOut` |
| **cubic** | `cubicIn` | `cubicOut` | `cubicInOut` |
| **elastic** | `elasticIn` | `elasticOut` | `elasticInOut` |
| **expo** | `expoIn` | `expoOut` | `expoInOut` |
| **quad** | `quadIn` | `quadOut` | `quadInOut` |
| **quart** | `quartIn` | `quartOut` | `quartInOut` |
| **quint** | `quintIn` | `quintOut` | `quintInOut` |
| **sine** | `sineIn` | `sineOut` | `sineInOut` |
<!-- TODO -->
<!--
<div class="max">
<iframe
title="Aphrodite example"
src="/repl/easing"
scrolling="no"
></iframe>
</div> -->

@ -0,0 +1,6 @@
---
title: Actions
draft: true
---
TODO

@ -0,0 +1,3 @@
{
"title": "Runtime"
}

File diff suppressed because it is too large Load Diff

@ -1,12 +1,12 @@
---
title: Compile time
title: 'svelte/compiler'
---
Typically, you won't interact with the Svelte compiler directly, but will instead integrate it into your build system using a bundler plugin. The bundler plugin that the Svelte team most recommends and invests in is [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). The [SvelteKit](https://kit.svelte.dev/) framework provides a setup leveraging `vite-plugin-svelte` to build applications as well as a [tool for packaging Svelte component libraries](https://kit.svelte.dev/docs/packaging). Svelte Society maintains a list of [other bundler plugins](https://sveltesociety.dev/tools/#bundling) for additional tools like Rollup and Webpack.
Nonetheless, it's useful to understand how to use the compiler, since bundler plugins generally expose compiler options to you.
### `svelte.compile`
## `svelte.compile`
```js
result: {
@ -19,8 +19,6 @@ result: {
} = svelte.compile(source: string, options?: {...})
```
---
This is where the magic happens. `svelte.compile` takes your component source code, and turns it into a JavaScript module that exports a class.
```js
@ -83,8 +81,6 @@ The following options can be passed to the compiler. None are required:
| `sveltePath` | `"svelte"` | The location of the `svelte` package. Any imports from `svelte` or `svelte/[module]` will be modified accordingly. |
| `namespace` | `"html"` | The namespace of the element; e.g., `"mathml"`, `"svg"`, `"foreign"`. |
---
The returned `result` object contains the code for your component, along with useful bits of metadata.
```js
@ -149,7 +145,7 @@ compiled: {
-->
### `svelte.parse`
## `svelte.parse`
```js
ast: object = svelte.parse(
@ -161,8 +157,6 @@ ast: object = svelte.parse(
)
```
---
The `parse` function parses a component, returning only its abstract syntax tree. Unlike compiling with the `generate: false` option, this will not perform any validation or other analysis of the component beyond parsing it. Note that the returned AST is not considered public API, so breaking changes could occur at any point in time.
```js
@ -171,7 +165,7 @@ const svelte = require('svelte/compiler');
const ast = svelte.parse(source, { filename: 'App.svelte' });
```
### `svelte.preprocess`
## `svelte.preprocess`
A number of [community-maintained preprocessing plugins](https://sveltesociety.dev/tools#preprocessors) are available to allow you to use Svelte with tools like TypeScript, PostCSS, SCSS, and Less.
@ -203,8 +197,6 @@ result: {
)
```
---
The `preprocess` function provides convenient hooks for arbitrarily transforming component source code. For example, it can be used to convert a `<style lang="sass">` block into vanilla CSS.
The first argument is the component source code. The second is an array of _preprocessors_ (or a single preprocessor, if you only have one), where a preprocessor is an object with `markup`, `script` and `style` functions, each of which is optional.
@ -241,8 +233,6 @@ const { code } = await svelte.preprocess(
);
```
---
The `script` and `style` functions receive the contents of `<script>` and `<style>` elements respectively (`content`) as well as the entire component source text (`markup`). In addition to `filename`, they get an object of the element's attributes.
If a `dependencies` array is returned, it will be included in the result object. This is used by packages like [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) to watch additional files for changes, in the case where your `<style>` tag has an `@import` (for example).
@ -285,8 +275,6 @@ const { code, dependencies } = await svelte.preprocess(
);
```
---
Multiple preprocessors can be used together. The output of the first becomes the input to the second. `markup` functions run first, then `script` and `style`.
```js
@ -324,7 +312,7 @@ const { code } = await svelte.preprocess(
);
```
### `svelte.walk`
## `svelte.walk`
```js
walk(ast: Node, {
@ -333,8 +321,6 @@ walk(ast: Node, {
})
```
---
The `walk` function provides a way to walk the abstract syntax trees generated by the parser, using the compiler's own built-in instance of [estree-walker](https://github.com/Rich-Harris/estree-walker).
The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node.
@ -354,9 +340,7 @@ svelte.walk(ast, {
});
```
### `svelte.VERSION`
---
## `svelte.VERSION`
The current version, as set in package.json.

@ -0,0 +1,111 @@
---
title: 'Client-side component API'
---
## Creating a component
```js
const component = new Component(options);
```
A client-side component — that is, a component compiled with `generate: 'dom'` (or the `generate` option left unspecified) is a JavaScript class.
```js
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
// assuming App.svelte contains something like
// `export let answer`:
answer: 42
}
});
```
The following initialisation options can be provided:
| option | default | description |
| --------- | ----------- | ---------------------------------------------------------------------------------------------------- |
| `target` | **none** | An `HTMLElement` or `ShadowRoot` to render to. This option is required |
| `anchor` | `null` | A child of `target` to render the component immediately before |
| `props` | `{}` | An object of properties to supply to the component |
| `context` | `new Map()` | A `Map` of root-level context key-value pairs to supply to the component |
| `hydrate` | `false` | See below |
| `intro` | `false` | If `true`, will play transitions on initial render, rather than waiting for subsequent state changes |
Existing children of `target` are left where they are.
The `hydrate` option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements. It will only work if the component was compiled with the [`hydratable: true` option](/docs/svelte-compiler#svelte-compile). Hydration of `<head>` elements only works properly if the server-side rendering code was also compiled with `hydratable: true`, which adds a marker to each element in the `<head>` so that the component knows which elements it's responsible for removing during hydration.
Whereas children of `target` are normally left alone, `hydrate: true` will cause any children to be removed. For that reason, the `anchor` option cannot be used alongside `hydrate: true`.
The existing DOM doesn't need to match the component — Svelte will 'repair' the DOM as it goes.
```js
import App from './App.svelte';
const app = new App({
target: document.querySelector('#server-rendered-html'),
hydrate: true
});
```
## `$set`
```js
component.$set(props);
```
Programmatically sets props on an instance. `component.$set({ x: 1 })` is equivalent to `x = 1` inside the component's `<script>` block.
Calling this method schedules an update for the next microtask — the DOM is _not_ updated synchronously.
```js
component.$set({ answer: 42 });
```
## `$on`
```js
component.$on(event, callback);
```
Causes the `callback` function to be called whenever the component dispatches an `event`.
A function is returned that will remove the event listener when called.
```js
const off = app.$on('selected', (event) => {
console.log(event.detail.selection);
});
off();
```
## `$destroy`
```js
component.$destroy();
```
Removes a component from the DOM and triggers any `onDestroy` handlers.
## Component props
```js
component.prop;
```
```js
component.prop = value;
```
If a component is compiled with `accessors: true`, each instance will have getters and setters corresponding to each of the component's props. Setting a value will cause a _synchronous_ update, rather than the default async update caused by `component.$set(...)`.
By default, `accessors` is `false`, unless you're compiling as a custom element.
```js
console.log(app.count);
app.count += 1;
```

@ -0,0 +1,47 @@
---
title: 'Server-side component API'
---
```js
const result = Component.render(...)
```
Unlike client-side components, server-side components don't have a lifespan after you render them — their whole job is to create some HTML and CSS. For that reason, the API is somewhat different.
A server-side component exposes a `render` method that can be called with optional props. It returns an object with `head`, `html`, and `css` properties, where `head` contains the contents of any `<svelte:head>` elements encountered.
You can import a Svelte component directly into Node using [`svelte/register`](/docs/svelte-register).
```js
require('svelte/register');
const App = require('./App.svelte').default;
const { head, html, css } = App.render({
answer: 42
});
```
The `.render()` method accepts the following parameters:
| parameter | default | description |
| --------- | ------- | -------------------------------------------------- |
| `props` | `{}` | An object of properties to supply to the component |
| `options` | `{}` | An object of options |
The `options` object takes in the following options:
| option | default | description |
| --------- | ----------- | ------------------------------------------------------------------------ |
| `context` | `new Map()` | A `Map` of root-level context key-value pairs to supply to the component |
```js
const { head, html, css } = App.render(
// props
{ answer: 42 },
// options
{
context: new Map([['context-key', 'context-value']])
}
);
```

@ -0,0 +1,57 @@
---
title: 'Custom elements API'
---
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](/docs/special-elements#svelte-options).
```svelte
<svelte:options tag="my-element" />
<script>
export let name = 'world';
</script>
<h1>Hello {name}!</h1>
<slot />
```
Alternatively, use `tag={null}` to indicate that the consumer of the custom element should name it.
```js
import MyElement from './MyElement.svelte';
customElements.define('my-element', MyElement);
```
Once a custom element has been defined, it can be used as a regular DOM element:
```js
document.body.innerHTML = `
<my-element>
<p>This is some slotted content</p>
</my-element>
`;
```
By default, custom elements are compiled with `accessors: true`, which means that any [props](/docs/dot-svelte-files#attributes-and-props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).
To prevent this, add `accessors={false}` to `<svelte:options>`.
```js
const el = document.querySelector('my-element');
// get the current value of the 'name' prop
console.log(el.name);
// set a new value, updating the shadow DOM
el.name = 'everybody';
```
Custom elements can be a useful way to package components for consumption in a non-Svelte app, as they will work with vanilla HTML and JavaScript as well as [most frameworks](https://custom-elements-everywhere.com/). There are, however, some important differences to be aware of:
- Styles are _encapsulated_, rather than merely _scoped_. This means that any non-component styles (such as you might have in a `global.css` file) will not apply to the custom element, including styles with the `:global(...)` modifier
- Instead of being extracted out as a separate .css file, styles are inlined into the component as a JavaScript string
- Custom elements are not generally suitable for server-side rendering, as the shadow DOM is invisible until JavaScript loads
- In Svelte, slotted content renders _lazily_. In the DOM, it renders _eagerly_. In other words, it will always be created even if the component's `<slot>` element is inside an `{#if ...}` block. Similarly, including a `<slot>` in an `{#each ...}` block will not cause the slotted content to be rendered multiple times
- The `let:` directive has no effect
- Polyfills are required to support older browsers

@ -0,0 +1,3 @@
{
"title": "Compiler and API"
}

File diff suppressed because it is too large Load Diff

@ -1,102 +1,83 @@
---
title: Accessibility warnings
title: 'Accessibility warnings'
---
Accessibility (shortened to a11y) isn't always easy to get right, but Svelte will help by warning you at compile time if you write inaccessible markup. However, keep in mind that many accessibility issues can only be identified at runtime using other automated tools and by manually testing your application.
Here is a list of accessibility checks Svelte will do for you.
---
### `a11y-accesskey`
## `a11y-accesskey`
Enforce no `accesskey` on element. Access keys are HTML attributes that allow web developers to assign keyboard shortcuts to elements. Inconsistencies between keyboard shortcuts and keyboard commands used by screen reader and keyboard-only users create accessibility complications. To avoid complications, access keys should not be used.
```sv
<!-- A11y: Avoid using accesskey -->
<div accessKey='z'></div>
```svelte
<!-- A11y: Avoid using accesskey --><div accessKey="z" />
```
---
### `a11y-aria-activedescendant-has-tabindex`
## `a11y-aria-activedescendant-has-tabindex`
An element with `aria-activedescendant` must be tabbable, so it must either have an inherent `tabindex` or declare `tabindex` as an attribute.
```sv
```svelte
<!-- A11y: Elements with attribute aria-activedescendant should have tabindex value -->
<div aria-activedescendant="some-id" />
```
---
### `a11y-aria-attributes`
## `a11y-aria-attributes`
Certain reserved DOM elements do not support ARIA roles, states and properties. This is often because they are not visible, for example `meta`, `html`, `script`, `style`. This rule enforces that these DOM elements do not contain the `aria-*` props.
```sv
```svelte
<!-- A11y: <meta> should not have aria-* attributes -->
<meta aria-hidden="false">
<meta aria-hidden="false" />
```
---
### `a11y-autofocus`
## `a11y-autofocus`
Enforce that `autofocus` is not used on elements. Autofocusing elements can cause usability issues for sighted and non-sighted users alike.
```sv
```svelte
<!-- A11y: Avoid using autofocus -->
<input autofocus>
<input autofocus />
```
---
### `a11y-click-events-have-key-events`
## `a11y-click-events-have-key-events`
Enforce `on:click` is accompanied by at least one of the following: `on:keyup`, `on:keydown`, `on:keypress`. Coding for the keyboard is important for users with physical disabilities who cannot use a mouse, AT compatibility, and screenreader users.
This does not apply for interactive or hidden elements.
```sv
```svelte
<!-- A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event. -->
<div on:click={() => {}} />
```
Note that the `keypress` event is now deprecated, so it is officially recommended to use either the `keyup` or `keydown` event instead, accordingly.
---
### `a11y-distracting-elements`
## `a11y-distracting-elements`
Enforces that no distracting elements are used. Elements that can be visually distracting can cause accessibility issues with visually impaired users. Such elements are most likely deprecated, and should be avoided.
The following elements are visually distracting: `<marquee>` and `<blink>`.
```sv
```svelte
<!-- A11y: Avoid <marquee> elements -->
<marquee />
```
---
### `a11y-hidden`
## `a11y-hidden`
Certain DOM elements are useful for screen reader navigation and should not be hidden.
```sv
<!-- A11y: <h2> element should not be hidden -->
<h2 aria-hidden="true">invisible header</h2>
```svelte
<!-- A11y: <h2> element should not be hidden --><h2 aria-hidden="true">invisible header</h2>
```
---
### `a11y-img-redundant-alt`
## `a11y-img-redundant-alt`
Enforce img alt attribute does not contain the word image, picture, or photo. Screen readers already announce `img` elements as an image. There is no need to use words such as _image_, _photo_, and/or _picture_.
```sv
```svelte
<img src="foo" alt="Foo eating a sandwich." />
<!-- aria-hidden, won't be announced by screen reader -->
@ -112,43 +93,35 @@ Enforce img alt attribute does not contain the word image, picture, or photo. Sc
<img src="foo" alt="Picture of baz fixing a bug." />
```
---
### `a11y-incorrect-aria-attribute-type`
## `a11y-incorrect-aria-attribute-type`
Enforce that only the correct type of value is used for aria attributes. For example, `aria-hidden`
should only receive a boolean.
```sv
```svelte
<!-- A11y: The value of 'aria-hidden' must be exactly one of true or false -->
<div aria-hidden="yes"/>
<div aria-hidden="yes" />
```
---
### `a11y-invalid-attribute`
## `a11y-invalid-attribute`
Enforce that attributes important for accessibility have a valid value. For example, `href` should not be empty, `'#'`, or `javascript:`.
```sv
```svelte
<!-- A11y: '' is not a valid href attribute -->
<a href=''>invalid</a>
<a href="">invalid</a>
```
---
### `a11y-interactive-supports-focus`
## `a11y-interactive-supports-focus`
Enforce that elements with an interactive role and interactive handlers (mouse or key press) must be focusable or tabbable.
```sv
```svelte
<!-- A11y: Elements with the 'button' interactive role must have a tabindex value. -->
<div role="button" on:keypress={() => {}} />
```
---
### `a11y-label-has-associated-control`
## `a11y-label-has-associated-control`
Enforce that a label tag has a text label and an associated control.
@ -157,7 +130,7 @@ There are two supported ways to associate a label with a control:
- Wrapping a control in a label tag.
- Adding `for` to a label and assigning it the ID of an input on the page.
```sv
```svelte
<label for="id">B</label>
<label>C <input type="text" /></label>
@ -166,51 +139,42 @@ There are two supported ways to associate a label with a control:
<label>A</label>
```
---
### `a11y-media-has-caption`
## `a11y-media-has-caption`
Providing captions for media is essential for deaf users to follow along. Captions should be a transcription or translation of the dialogue, sound effects, relevant musical cues, and other relevant audio information. Not only is this important for accessibility, but can also be useful for all users in the case that the media is unavailable (similar to `alt` text on an image when an image is unable to load).
The captions should contain all important and relevant information to understand the corresponding media. This may mean that the captions are not a 1:1 mapping of the dialogue in the media content. However, captions are not necessary for video components with the `muted` attribute.
```sv
<video><track kind="captions"/></video>
```svelte
<video><track kind="captions" /></video>
<audio muted></audio>
<audio muted />
<!-- A11y: Media elements must have a <track kind=\"captions\"> -->
<video></video>
<video />
<!-- A11y: Media elements must have a <track kind=\"captions\"> -->
<video><track /></video>
```
---
### `a11y-misplaced-role`
## `a11y-misplaced-role`
Certain reserved DOM elements do not support ARIA roles, states and properties. This is often because they are not visible, for example `meta`, `html`, `script`, `style`. This rule enforces that these DOM elements do not contain the `role` props.
```sv
```svelte
<!-- A11y: <meta> should not have role attribute -->
<meta role="tooltip">
<meta role="tooltip" />
```
---
### `a11y-misplaced-scope`
## `a11y-misplaced-scope`
The scope attribute should only be used on `<th>` elements.
```sv
<!-- A11y: The scope attribute should only be used with <th> elements -->
<div scope="row" />
```svelte
<!-- A11y: The scope attribute should only be used with <th> elements --><div scope="row" />
```
---
### `a11y-missing-attribute`
## `a11y-missing-attribute`
Enforce that attributes required for accessibility are present on an element. This includes the following checks:
@ -222,38 +186,34 @@ Enforce that attributes required for accessibility are present on an element. Th
- `<object>` should have title, aria-label, or aria-labelledby
- `<input type="image">` should have alt, aria-label, or aria-labelledby
```sv
```svelte
<!-- A11y: <input type=\"image\"> element should have an alt, aria-label or aria-labelledby attribute -->
<input type="image">
<input type="image" />
<!-- A11y: <html> element should have a lang attribute -->
<html></html>
<html />
<!-- A11y: <a> element should have an href attribute -->
<a>text</a>
```
---
### `a11y-missing-content`
## `a11y-missing-content`
Enforce that heading elements (`h1`, `h2`, etc.) and anchors have content and that the content is accessible to screen readers
```sv
```svelte
<!-- A11y: <a> element should have child content -->
<a href='/foo'></a>
<a href="/foo" />
<!-- A11y: <h1> element should have child content -->
<h1></h1>
<h1 />
```
---
### `a11y-mouse-events-have-key-events`
## `a11y-mouse-events-have-key-events`
Enforce that `on:mouseover` and `on:mouseout` are accompanied by `on:focus` and `on:blur`, respectively. This helps to ensure that any functionality triggered by these mouse events is also accessible to keyboard users.
```sv
```svelte
<!-- A11y: on:mouseover must be accompanied by on:focus -->
<div on:mouseover={handleMouseover} />
@ -261,13 +221,11 @@ Enforce that `on:mouseover` and `on:mouseout` are accompanied by `on:focus` and
<div on:mouseout={handleMouseout} />
```
---
### `a11y-no-redundant-roles`
## `a11y-no-redundant-roles`
Some HTML elements have default ARIA roles. Giving these elements an ARIA role that is already set by the browser [has no effect](https://www.w3.org/TR/using-aria/#aria-does-nothing) and is redundant.
```sv
```svelte
<!-- A11y: Redundant role 'button' -->
<button role="button" />
@ -275,68 +233,54 @@ Some HTML elements have default ARIA roles. Giving these elements an ARIA role t
<img role="img" src="foo.jpg" />
```
---
### `a11y-no-interactive-element-to-noninteractive-role`
## `a11y-no-interactive-element-to-noninteractive-role`
[WAI-ARIA](https://www.w3.org/TR/wai-aria-1.1/#usage_intro) roles should not be used to convert an interactive element to a non-interactive element. Non-interactive ARIA roles include `article`, `banner`, `complementary`, `img`, `listitem`, `main`, `region` and `tooltip`.
```sv
```svelte
<!-- A11y: <textarea> cannot have role 'listitem' -->
<textarea role="listitem" />
```
---
### `a11y-no-noninteractive-element-to-interactive-role`
## `a11y-no-noninteractive-element-to-interactive-role`
[WAI-ARIA](https://www.w3.org/TR/wai-aria-1.1/#usage_intro) roles should not be used to convert a non-interactive element to an interactive element. Interactive ARIA roles include `button`, `link`, `checkbox`, `menuitem`, `menuitemcheckbox`, `menuitemradio`, `option`, `radio`, `searchbox`, `switch` and `textbox`.
```sv
```svelte
<!-- A11y: Non-interactive element <h3> cannot have interactive role 'searchbox' -->
<h3 role="searchbox">Button</h3>
```
---
### `a11y-no-noninteractive-tabindex`
## `a11y-no-noninteractive-tabindex`
Tab key navigation should be limited to elements on the page that can be interacted with.
```sv
<!-- A11y: noninteractive element cannot have nonnegative tabIndex value -->
<div tabindex='0' />
```svelte
<!-- A11y: noninteractive element cannot have nonnegative tabIndex value --><div tabindex="0" />
```
---
### `a11y-positive-tabindex`
## `a11y-positive-tabindex`
Avoid positive `tabindex` property values. This will move elements out of the expected tab order, creating a confusing experience for keyboard users.
```sv
<!-- A11y: avoid tabindex values above zero -->
<div tabindex='1'/>
```svelte
<!-- A11y: avoid tabindex values above zero --><div tabindex="1" />
```
---
### `a11y-role-has-required-aria-props`
## `a11y-role-has-required-aria-props`
Elements with ARIA roles must have all required attributes for that role.
```sv
```svelte
<!-- A11y: A11y: Elements with the ARIA role "checkbox" must have the following attributes defined: "aria-checked" -->
<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>
<span role="checkbox" aria-labelledby="foo" tabindex="0" />
```
---
### `a11y-role-supports-aria-props`
## `a11y-role-supports-aria-props`
Elements with explicit or implicit roles defined contain only `aria-*` properties supported by that role.
```sv
```svelte
<!-- A11y: The attribute 'aria-multiline' is not supported by the role 'link'. -->
<div role="link" aria-multiline />
@ -344,37 +288,30 @@ Elements with explicit or implicit roles defined contain only `aria-*` propertie
<li aria-required />
```
---
### `a11y-structure`
## `a11y-structure`
Enforce that certain DOM elements have the correct structure.
```sv
```svelte
<!-- A11y: <figcaption> must be an immediate child of <figure> -->
<div>
<figcaption>Image caption</figcaption>
</div>
```
---
### `a11y-unknown-aria-attribute`
## `a11y-unknown-aria-attribute`
Enforce that only known ARIA attributes are used. This is based on the [WAI-ARIA States and Properties spec](https://www.w3.org/WAI/PF/aria-1.1/states_and_properties).
```sv
```svelte
<!-- A11y: Unknown aria attribute 'aria-labeledby' (did you mean 'labelledby'?) -->
<input type="image" aria-labeledby="foo">
<input type="image" aria-labeledby="foo" />
```
---
### `a11y-unknown-role`
## `a11y-unknown-role`
Elements with ARIA roles must use a valid, non-abstract ARIA role. A reference to role definitions can be found at [WAI-ARIA](https://www.w3.org/TR/wai-aria/#role_definitions) site.
```sv
<!-- A11y: Unknown role 'toooltip' (did you mean 'tooltip'?) -->
<div role="toooltip"></div>
```svelte
<!-- A11y: Unknown role 'toooltip' (did you mean 'tooltip'?) --><div role="toooltip" />
```

@ -0,0 +1,6 @@
---
title: TypeScript
draft: true
---
TODO

@ -0,0 +1,5 @@
---
title: Migrating from v3 to v4
---
TODO

@ -0,0 +1,3 @@
{
"title": "Misc"
}

@ -0,0 +1,26 @@
---
title: 'svelte/register'
---
To render Svelte components in Node.js without bundling, use `require('svelte/register')`. After that, you can use `require` to include any `.svelte` file.
```js
require('svelte/register');
const App = require('./App.svelte').default;
...
const { html, css, head } = App.render({ answer: 42 });
```
> The `.default` is necessary because we're converting from native JavaScript modules to the CommonJS modules recognised by Node. Note that if your component imports JavaScript modules, they will fail to load in Node and you will need to use a bundler instead.
To set compile options, or to use a custom file extension, call the `register` hook as a function:
```js
require('svelte/register')({
extensions: ['.customextension'], // defaults to ['.html', '.svelte']
preserveComments: true
});
```

@ -0,0 +1,3 @@
{
"title": "Legacy"
}

@ -4,7 +4,7 @@ question: How do I document my components?
In editors which use the Svelte Language Server you can document Components, functions and exports using specially formatted comments.
````sv
````svelte
<script>
/** What should we call the user? */
export let name = 'world';

@ -4,7 +4,7 @@ title: Basics
Welcome to the Svelte tutorial. This will teach you everything you need to know to build fast, small web applications easily.
You can also consult the [API docs](/docs) and the [examples](/examples), or — if you're impatient to start hacking on your machine locally — the [60-second quickstart](/docs/getting-started).
You can also consult the [API docs](/docs) and the [examples](/examples), or — if you're impatient to start hacking on your machine locally — the [60-second quickstart](/docs/introduction).
## What is Svelte?

@ -34,4 +34,4 @@ const app = new App({
});
```
You can then interact with `app` using the [component API](/docs/run-time#client-side-component-api) if you need to.
You can then interact with `app` using the [component API](/docs/client-side-component-api) if you need to.

@ -8,4 +8,4 @@ You can create a store whose value is based on the value of one or more _other_
export const elapsed = derived(time, ($time) => Math.round(($time - start) / 1000));
```
> It's possible to derive a store from multiple inputs, and to explicitly `set` a value instead of returning it (which is useful for deriving values asynchronously). Consult the [API reference](/docs/run-time#svelte-store-derived) for more information.
> It's possible to derive a store from multiple inputs, and to explicitly `set` a value instead of returning it (which is useful for deriving values asynchronously). Consult the [API reference](/docs/svelte-store#derived) for more information.

@ -29,4 +29,4 @@ let coords = spring(
Waggle your mouse around, and try dragging the sliders to get a feel for how they affect the spring's behaviour. Notice that you can adjust the values while the spring is still in motion.
Consult the [API reference](/docs/run-time#svelte-motion-spring) for more information.
Consult the [API reference](/docs/svelte-motion#spring) for more information.

@ -6,12 +6,12 @@ The `<svelte:fragment>` element allows you to place content in a named slot with
In the example notice how we applied a flex layout with a gap of `1em` to the box.
```sv
```svelte
<!-- Box.svelte -->
<div class="box">
<slot name="header">No header was provided</slot>
<p>Some content between header and footer</p>
<slot name="footer"></slot>
<slot name="footer" />
</div>
<style>
@ -27,7 +27,7 @@ However, the content in the footer is not spaced out according to this rhythm be
We can solve this by changing `<div slot="footer">` in the `App` component. Replace the `<div>` with `<svelte:fragment>`:
```sv
```svelte
<svelte:fragment slot="footer">
<p>All rights reserved.</p>
<p>Copyright (c) 2019 Svelte Industries</p>

@ -4,7 +4,7 @@ title: Congratulations!
You've now finished the Svelte tutorial and are ready to start building apps. You can refer back to individual chapters at any time (click the title above to reveal a dropdown) or continue your learning via the [API reference](/docs), [Examples](/examples) and [Blog](/blog). If you're a Twitter user, you can get updates via [@sveltejs](https://twitter.com/sveltejs).
To get set up in your local development environment, check out [the quickstart guide](/docs/getting-started).
To get set up in your local development environment, check out [the quickstart guide](/docs/introduction).
If you're looking for a more expansive framework that includes routing, server-side rendering and everything else, take a look at [SvelteKit](https://kit.svelte.dev).

@ -0,0 +1,105 @@
// @ts-check
import { base as app_base } from '$app/paths';
import fs from 'fs';
import {
escape,
extract_frontmatter,
normalizeSlugify,
removeMarkdown,
transform
} from '../markdown';
const BASE = '../../site/content/docs/';
/** @return {import('./types').DocsData} */
export function get_docs_data(base = BASE) {
/** @type {import('./types').DocsData} */
const docs_data = [];
for (const category_dir of fs.readdirSync(base)) {
const match = /\d{2}-(.+)/.exec(category_dir);
if (!match) continue;
const category_slug = match[1];
// Read the meta.json
const { title: category_title, draft = 'false' } = JSON.parse(
fs.readFileSync(`${base}/${category_dir}/meta.json`, 'utf-8')
);
if (draft === 'true') continue;
/** @type {import('./types').Category} */
const category = {
title: category_title,
slug: category_slug,
pages: []
};
for (const page_md of fs
.readdirSync(`${base}/${category_dir}`)
.filter((filename) => filename !== 'meta.json')) {
const match = /\d{2}-(.+)/.exec(page_md);
if (!match) continue;
const page_slug = match[1].replace('.md', '');
const page_data = extract_frontmatter(
fs.readFileSync(`${base}/${category_dir}/${page_md}`, 'utf-8')
);
if (page_data.metadata.draft === 'true') continue;
const page_title = page_data.metadata.title;
const page_content = page_data.body;
category.pages.push({
title: page_title,
slug: page_slug,
content: page_content,
sections: get_sections(page_content),
path: `${app_base}/docs/${page_slug}`,
file: `${category_dir}/${page_md}`
});
}
docs_data.push(category);
}
return docs_data;
}
/** @param {import('./types').DocsData} docs_data */
export function get_docs_list(docs_data) {
return docs_data.map((category) => ({
title: category.title,
pages: category.pages.map((page) => ({
title: page.title,
path: page.path
}))
}));
}
/** @param {string} markdown */
function get_sections(markdown) {
const headingRegex = /^##\s+(.*)$/gm;
/** @type {import('./types').Section[]} */
const secondLevelHeadings = [];
let match;
while ((match = headingRegex.exec(markdown)) !== null) {
secondLevelHeadings.push({
title: removeMarkdown(
escape(transform(match[1], { paragraph: (txt) => txt }))
.replace(/<\/?code>/g, '')
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/<(\/)?(em|b|strong|code)>/g, '')
),
slug: normalizeSlugify(match[1])
});
}
return secondLevelHeadings;
}

@ -1,56 +1,14 @@
import * as fs from 'fs';
import * as path from 'path';
import { renderCodeToHTML, runTwoSlash, createShikiHighlighter } from 'shiki-twoslash';
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash.js';
import 'prismjs/components/prism-diff.js';
import 'prismjs/components/prism-typescript.js';
import 'prism-svelte';
import { escape, extract_frontmatter, transform } from '../markdown';
// import 'prism-svelte';
// import 'prismjs/components/prism-bash.js';
// import 'prismjs/components/prism-diff.js';
// import 'prismjs/components/prism-typescript.js';
import { createShikiHighlighter } from 'shiki-twoslash';
import { normalizeSlugify, transform } from '../markdown';
// import { render, replace_placeholders } from './render.js';
// import { parse_route_id } from '../../../../../../packages/kit/src/utils/routing.js';
import ts, { ScriptTarget } from 'typescript';
import MagicString from 'magic-string';
import { fileURLToPath } from 'url';
import { createHash } from 'crypto';
/** @param {string} title */
export function slugify(title) {
return title
.toLowerCase()
.replace(/&#39;/g, '')
.replace(/&lt;/g, '')
.replace(/&gt;/g, '')
.replace(/[^a-z0-9-$]/g, '-')
.replace(/-{2,}/g, '-')
.replace(/^-/, '')
.replace(/-$/, '');
}
/** @param {string} markdown */
export function removeMarkdown(markdown) {
return markdown
.replace(/\*\*(.+?)\*\*/g, '$1')
.replace(/\*(.+?)\*/g, '$1')
.replace(/`(.+?)`/g, '$1')
.replace(/~~(.+?)~~/g, '$1')
.replace(/\[(.+?)\]\(.+?\)/g, '$1')
.replace(/\n/g, ' ')
.replace(/ {2,}/g, ' ')
.trim();
}
/** @param {string} html */
export function removeHTMLEntities(html) {
return html.replace(/&.+?;/g, '');
}
/** @param {string} str */
export const normalizeSlugify = (str) => {
return slugify(removeHTMLEntities(removeMarkdown(str))).replace(/(<([^>]+)>)/gi, '');
};
const base = '../../site/content/docs/';
import MagicString from 'magic-string';
import ts from 'typescript';
const languages = {
bash: 'bash',
@ -66,259 +24,253 @@ const languages = {
};
/**
* @param {string} file
* @param {import('./types').DocsData} docs_data
* @param {string} slug
*/
export async function read_file(file) {
const match = /\d{2}-(.+)\.md/.exec(path.basename(file));
if (!match) return null;
export async function get_parsed_docs(docs_data, slug) {
const page = docs_data
.find(({ pages }) => pages.find((page) => slug === page.slug))
?.pages.find((page) => slug === page.slug);
// const markdown = replace_placeholders(fs.readFileSync(`${base}/${file}`, 'utf-8'));
const markdown = fs.readFileSync(`${base}/${file}`, 'utf-8');
if (!page) return null;
const highlighter = await createShikiHighlighter({ theme: 'css-variables' });
const { metadata, body } = extract_frontmatter(markdown);
const { content, sections } = parse({
file,
body: generate_ts_from_js(body),
code: (source, language, current) => {
const hash = createHash('sha256');
hash.update(source + language + current);
const digest = hash.digest().toString('base64').replace(/\//g, '-');
// TODO: cache
// if (fs.existsSync(`${snippet_cache}/${digest}.html`)) {
// return fs.readFileSync(`${snippet_cache}/${digest}.html`, 'utf-8');
// }
/** @type {Record<string, string>} */
const options = {};
let html = '';
source = source
.replace(/^\/\/\/ (.+?): (.+)\n/gm, (_, key, value) => {
options[key] = value;
return '';
})
.replace(/^([\-\+])?((?: )+)/gm, (match, prefix = '', spaces) => {
if (prefix && language !== 'diff') return match;
// for no good reason at all, marked replaces tabs with spaces
let tabs = '';
for (let i = 0; i < spaces.length; i += 4) {
tabs += ' ';
}
return prefix + tabs;
})
.replace(/\*\\\//g, '*/');
let version_class = '';
if (language === 'generated-ts' || language === 'generated-svelte') {
language = language.replace('generated-', '');
version_class = 'ts-version';
} else if (language === 'original-js' || language === 'original-svelte') {
language = language.replace('original-', '');
version_class = 'js-version';
}
// TODO: Replace later
html = highlighter.codeToHtml(source, { lang: languages[language] });
// if (language === 'dts') {
// // @ts-ignore
// html = renderCodeToHTML(source, 'ts', { twoslash: false }, {}, highlighter);
// } else if (language === 'js' || language === 'ts') {
// try {
// const injected = [];
// if (
// source.includes('$app/') ||
// source.includes('$service-worker') ||
// source.includes('@sveltejs/kit/')
// ) {
// injected.push(
// `// @filename: ambient-kit.d.ts`,
// `/// <reference types="@sveltejs/kit" />`
// );
// }
// if (source.includes('$env/')) {
// // TODO we're hardcoding static env vars that are used in code examples
// // in the types, which isn't... totally ideal, but will do for now
// injected.push(
// `declare module '$env/dynamic/private' { export const env: Record<string, string> }`,
// `declare module '$env/dynamic/public' { export const env: Record<string, string> }`,
// `declare module '$env/static/private' { export const API_KEY: string }`,
// `declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }`
// );
// }
// if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
// const params = parse_route_id(options.file || `+page.${language}`)
// .params.map((param) => `${param.name}: string`)
// .join(', ');
// injected.push(
// `// @filename: $types.d.ts`,
// `import type * as Kit from '@sveltejs/kit';`,
// `export type PageLoad = Kit.Load<{${params}}>;`,
// `export type PageServerLoad = Kit.ServerLoad<{${params}}>;`,
// `export type LayoutLoad = Kit.Load<{${params}}>;`,
// `export type LayoutServerLoad = Kit.ServerLoad<{${params}}>;`,
// `export type RequestHandler = Kit.RequestHandler<{${params}}>;`,
// `export type Action = Kit.Action<{${params}}>;`,
// `export type Actions = Kit.Actions<{${params}}>;`
// );
// }
// // special case — we need to make allowances for code snippets coming
// // from e.g. ambient.d.ts
// if (file.endsWith('30-modules.md')) {
// injected.push('// @errors: 7006 7031');
// }
// // another special case
// if (source.includes('$lib/types')) {
// injected.push(`declare module '$lib/types' { export interface User {} }`);
// }
// if (injected.length) {
// const injected_str = injected.join('\n');
// if (source.includes('// @filename:')) {
// source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`);
// } else {
// source = source.replace(
// /^(?!\/\/ @)/m,
// `${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n`
// );
// }
// }
// const twoslash = runTwoSlash(source, language, {
// defaultCompilerOptions: {
// allowJs: true,
// checkJs: true,
// target: 'es2021',
// },
// });
// html = renderCodeToHTML(
// twoslash.code,
// 'ts',
// { twoslash: true },
// {},
// highlighter,
// twoslash
// );
// } catch (e) {
// console.error(`Error compiling snippet in ${file}`);
// console.error(e.code);
// throw e;
// }
// // we need to be able to inject the LSP attributes as HTML, not text, so we
// // turn &lt; into &amp;lt;
// html = html.replace(
// /<data-lsp lsp='([^']*)'([^>]*)>(\w+)<\/data-lsp>/g,
// (match, lsp, attrs, name) => {
// if (!lsp) return name;
// return `<data-lsp lsp='${lsp.replace(/&/g, '&amp;')}'${attrs}>${name}</data-lsp>`;
// }
// );
// // preserve blank lines in output (maybe there's a more correct way to do this?)
// html = html.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>');
// } else if (language === 'diff') {
// const lines = source.split('\n').map((content) => {
// let type = null;
// if (/^[\+\-]/.test(content)) {
// type = content[0] === '+' ? 'inserted' : 'deleted';
// content = content.slice(1);
// }
// return {
// type,
// content: escape(content),
// };
// });
// html = `<pre class="language-diff"><code>${lines
// .map((line) => {
// if (line.type) return `<span class="${line.type}">${line.content}\n</span>`;
// return line.content + '\n';
// })
// .join('')}</code></pre>`;
// } else {
// const plang = languages[language];
// const highlighted = plang
// ? PrismJS.highlight(source, PrismJS.languages[plang], language)
// : source.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
// html = `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
// }
if (options.file) {
html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`;
}
if (version_class) {
html = html.replace(/class=('|")/, `class=$1${version_class} `);
}
// type_regex.lastIndex = 0;
return {
...page,
content: parse({
file: page.file,
body: generate_ts_from_js(page.content),
code: (source, language, current) => {
const hash = createHash('sha256');
hash.update(source + language + current);
const digest = hash.digest().toString('base64').replace(/\//g, '-');
// TODO: cache
// if (fs.existsSync(`${snippet_cache}/${digest}.html`)) {
// return fs.readFileSync(`${snippet_cache}/${digest}.html`, 'utf-8');
// }
/** @type {Record<string, string>} */
const options = {};
let html = '';
source = source
.replace(/^\/\/\/ (.+?): (.+)\n/gm, (_, key, value) => {
options[key] = value;
return '';
})
.replace(/^([\-\+])?((?: )+)/gm, (match, prefix = '', spaces) => {
if (prefix && language !== 'diff') return match;
// for no good reason at all, marked replaces tabs with spaces
let tabs = '';
for (let i = 0; i < spaces.length; i += 4) {
tabs += ' ';
}
return prefix + tabs;
})
.replace(/\*\\\//g, '*/');
let version_class = '';
if (language === 'generated-ts' || language === 'generated-svelte') {
language = language.replace('generated-', '');
version_class = 'ts-version';
} else if (language === 'original-js' || language === 'original-svelte') {
language = language.replace('original-', '');
version_class = 'js-version';
}
html = html
// .replace(type_regex, (match, prefix, name) => {
// if (options.link === 'false' || name === current) {
// // we don't want e.g. RequestHandler to link to RequestHandler
// return match;
// TODO: Replace later
html = highlighter.codeToHtml(source, { lang: languages[language] });
// if (language === 'dts') {
// // @ts-ignore
// html = renderCodeToHTML(source, 'ts', { twoslash: false }, {}, highlighter);
// } else if (language === 'js' || language === 'ts') {
// try {
// const injected = [];
// if (
// source.includes('$app/') ||
// source.includes('$service-worker') ||
// source.includes('@sveltejs/kit/')
// ) {
// injected.push(
// `// @filename: ambient-kit.d.ts`,
// `/// <reference types="@sveltejs/kit" />`
// );
// }
// if (source.includes('$env/')) {
// // TODO we're hardcoding static env vars that are used in code examples
// // in the types, which isn't... totally ideal, but will do for now
// injected.push(
// `declare module '$env/dynamic/private' { export const env: Record<string, string> }`,
// `declare module '$env/dynamic/public' { export const env: Record<string, string> }`,
// `declare module '$env/static/private' { export const API_KEY: string }`,
// `declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }`
// );
// }
// if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
// const params = parse_route_id(options.file || `+page.${language}`)
// .params.map((param) => `${param.name}: string`)
// .join(', ');
// injected.push(
// `// @filename: $types.d.ts`,
// `import type * as Kit from '@sveltejs/kit';`,
// `export type PageLoad = Kit.Load<{${params}}>;`,
// `export type PageServerLoad = Kit.ServerLoad<{${params}}>;`,
// `export type LayoutLoad = Kit.Load<{${params}}>;`,
// `export type LayoutServerLoad = Kit.ServerLoad<{${params}}>;`,
// `export type RequestHandler = Kit.RequestHandler<{${params}}>;`,
// `export type Action = Kit.Action<{${params}}>;`,
// `export type Actions = Kit.Actions<{${params}}>;`
// );
// }
// // special case — we need to make allowances for code snippets coming
// // from e.g. ambient.d.ts
// if (file.endsWith('30-modules.md')) {
// injected.push('// @errors: 7006 7031');
// }
// // another special case
// if (source.includes('$lib/types')) {
// injected.push(`declare module '$lib/types' { export interface User {} }`);
// }
// if (injected.length) {
// const injected_str = injected.join('\n');
// if (source.includes('// @filename:')) {
// source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`);
// } else {
// source = source.replace(
// /^(?!\/\/ @)/m,
// `${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n`
// );
// }
// }
// const twoslash = runTwoSlash(source, language, {
// defaultCompilerOptions: {
// allowJs: true,
// checkJs: true,
// target: 'es2021',
// },
// });
// html = renderCodeToHTML(
// twoslash.code,
// 'ts',
// { twoslash: true },
// {},
// highlighter,
// twoslash
// );
// } catch (e) {
// console.error(`Error compiling snippet in ${file}`);
// console.error(e.code);
// throw e;
// }
// const link = `<a href="${type_links.get(name)}">${name}</a>`;
// return `${prefix || ''}${link}`;
// })
.replace(
/^(\s+)<span class="token comment">([\s\S]+?)<\/span>\n/gm,
(match, intro_whitespace, content) => {
// we use some CSS trickery to make comments break onto multiple lines while preserving indentation
const lines = (intro_whitespace + content).split('\n');
return lines
.map((line) => {
const match = /^(\s*)(.*)/.exec(line);
const indent = (match[1] ?? '').replace(/\t/g, ' ').length;
return `<span class="token comment wrapped" style="--indent: ${indent}ch">${
line ?? ''
}</span>`;
})
.join('');
}
)
.replace(/\/\*…\*\//g, '…');
// // we need to be able to inject the LSP attributes as HTML, not text, so we
// // turn &lt; into &amp;lt;
// html = html.replace(
// /<data-lsp lsp='([^']*)'([^>]*)>(\w+)<\/data-lsp>/g,
// (match, lsp, attrs, name) => {
// if (!lsp) return name;
// return `<data-lsp lsp='${lsp.replace(/&/g, '&amp;')}'${attrs}>${name}</data-lsp>`;
// }
// );
// // preserve blank lines in output (maybe there's a more correct way to do this?)
// html = html.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>');
// } else if (language === 'diff') {
// const lines = source.split('\n').map((content) => {
// let type = null;
// if (/^[\+\-]/.test(content)) {
// type = content[0] === '+' ? 'inserted' : 'deleted';
// content = content.slice(1);
// }
// return {
// type,
// content: escape(content),
// };
// });
// html = `<pre class="language-diff"><code>${lines
// .map((line) => {
// if (line.type) return `<span class="${line.type}">${line.content}\n</span>`;
// return line.content + '\n';
// })
// .join('')}</code></pre>`;
// } else {
// const plang = languages[language];
// const highlighted = plang
// ? PrismJS.highlight(source, PrismJS.languages[plang], language)
// : source.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
// html = `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
// }
if (options.file) {
html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`;
}
// fs.writeFileSync(`${snippet_cache}/${digest}.html`, html);
return html;
},
codespan: (text) => {
return (
'<code>' +
text +
// text.replace(type_regex, (match, prefix, name) => {
// const link = `<a href="${type_links.get(name)}">${name}</a>`;
// return `${prefix || ''}${link}`;
// }) +
'</code>'
);
}
});
if (version_class) {
html = html.replace(/class=('|")/, `class=$1${version_class} `);
}
return {
file,
slug: match[1],
title: metadata.title,
content,
sections
// type_regex.lastIndex = 0;
html = html
// .replace(type_regex, (match, prefix, name) => {
// if (options.link === 'false' || name === current) {
// // we don't want e.g. RequestHandler to link to RequestHandler
// return match;
// }
// const link = `<a href="${type_links.get(name)}">${name}</a>`;
// return `${prefix || ''}${link}`;
// })
.replace(
/^(\s+)<span class="token comment">([\s\S]+?)<\/span>\n/gm,
(match, intro_whitespace, content) => {
// we use some CSS trickery to make comments break onto multiple lines while preserving indentation
const lines = (intro_whitespace + content).split('\n');
return lines
.map((line) => {
const match = /^(\s*)(.*)/.exec(line);
const indent = (match[1] ?? '').replace(/\t/g, ' ').length;
return `<span class="token comment wrapped" style="--indent: ${indent}ch">${
line ?? ''
}</span>`;
})
.join('');
}
)
.replace(/\/\*…\*\//g, '…');
// fs.writeFileSync(`${snippet_cache}/${digest}.html`, html);
return html;
},
codespan: (text) => {
return (
'<code>' +
text +
// text.replace(type_regex, (match, prefix, name) => {
// const link = `<a href="${type_links.get(name)}">${name}</a>`;
// return `${prefix || ''}${link}`;
// }) +
'</code>'
);
}
})
};
}
@ -330,15 +282,9 @@ export async function read_file(file) {
* codespan: (source: string) => string;
* }} opts
*/
function parse({ file, body, code, codespan }) {
function parse({ body, code, codespan }) {
const headings = [];
/** @type {import('./types').Section[]} */
const sections = [];
/** @type {import('./types').Section} */
let section;
// this is a bit hacky, but it allows us to prevent type declarations
// from linking to themselves
let current = '';
@ -360,43 +306,21 @@ function parse({ file, body, code, codespan }) {
const normalized = normalizeSlugify(raw);
headings[level - 1] = normalized;
headings[level] = normalized;
headings.length = level;
const slug = headings.filter(Boolean).join('-');
// TODO: All this will need to change when headings in documentation are restructured to be h2+ instead of h3+ as they are right now
if (level === 3) {
section = {
title,
slug,
sections: []
};
sections.push(section);
} else if (level === 4 || level === 5) {
(section?.sections ?? sections).push({
title,
slug
});
} else {
throw new Error(`Unexpected <h${level}> in ${file}`);
}
const slug = normalizeSlugify(raw);
return `<h${
level - 1
} id="${slug}">${html}<a href="#${slug}" class="permalink"><span class="visually-hidden">permalink</span></a></h${level}>`;
return `<h${level} id="${slug}">${html}<a href="#${slug}" class="permalink"><span class="visually-hidden">permalink</span></a></h${level}>`;
},
code: (source, language) => code(source, language, current),
codespan
});
return {
sections,
content
};
return content;
}
/** @param {string} markdown */
export function generate_ts_from_js(markdown) {
return markdown
.replaceAll(/```js\n([\s\S]+?)\n```/g, (match, code) => {

@ -1,9 +1,27 @@
export type DocsData = Category[];
export interface Section {
title: string;
slug: string;
// Currently, we are only going with 2 level headings, so this will be undefined. In future, we may want to support 3 levels, in which case this will be a list of sections
sections?: Section[];
}
export type Category = {
title: string;
slug: string;
pages: Page[];
};
export type Page = {
title: string;
slug: string;
file: string;
path: string;
content: string;
sections: Section[];
};
export interface Type {
name: string;
comment: string;

@ -19,7 +19,8 @@ export function get_examples_data(base = BASE) {
if (!(fs.statSync(`${base}/${subdir}`).isDirectory() || subdir.endsWith('meta.json'))) continue;
if (!subdir.endsWith('meta.json'))
section.title = JSON.parse(fs.readFileSync(`${base}/${subdir}/meta.json`, 'utf-8')).title;
section.title =
JSON.parse(fs.readFileSync(`${base}/${subdir}/meta.json`, 'utf-8')).title ?? 'Embeds';
for (const section_dir of fs.readdirSync(`${base}/${subdir}`)) {
const match = /\d{2}-(.+)/.exec(section_dir);

@ -16,7 +16,7 @@ export function get_faq_data() {
faqs.push({
title: metadata.question, // Initialise with empty
slug: file.split('-').slice(1).join('-').replace('.md', ''),
content: body,
content: body
});
}

@ -12,7 +12,7 @@ const languages = {
css: 'css',
diff: 'diff',
ts: 'typescript',
'': '',
'': ''
};
/**
@ -79,8 +79,8 @@ export async function get_parsed_faq(faq_data) {
return html;
},
codespan: (text) => '<code>' + text + '</code>',
}),
codespan: (text) => '<code>' + text + '</code>'
})
};
})
);

@ -35,6 +35,43 @@ export function escape(html, encode = false) {
return html;
}
/** @param {string} title */
export function slugify(title) {
return title
.toLowerCase()
.replace(/&#39;/g, '')
.replace(/&lt;/g, '')
.replace(/&gt;/g, '')
.replace(/[^a-z0-9-$]/g, '-')
.replace(/-{2,}/g, '-')
.replace(/^-/, '')
.replace(/-$/, '');
}
/** @param {string} markdown */
export function removeMarkdown(markdown) {
return markdown
.replace(/\*\*(.+?)\*\*/g, '$1') // bold
.replace(/_(.+?)_/g, '$1') // Italics
.replace(/\*(.+?)\*/g, '$1') // Italics
.replace(/`(.+?)`/g, '$1') // Inline code
.replace(/~~(.+?)~~/g, '$1') // Strikethrough
.replace(/\[(.+?)\]\(.+?\)/g, '$1') // Link
.replace(/\n/g, ' ') // New line
.replace(/ {2,}/g, ' ')
.trim();
}
/** @param {string} html */
export function removeHTMLEntities(html) {
return html.replace(/&.+?;/g, '');
}
/** @param {string} str */
export const normalizeSlugify = (str) => {
return slugify(removeHTMLEntities(removeMarkdown(str))).replace(/(<([^>]+)>)/gi, '');
};
/** @type {Partial<import('marked').Renderer>} */
const default_renderer = {
code(code, infostring, escaped) {
@ -184,8 +221,11 @@ export function extract_frontmatter(markdown) {
const metadata = {};
frontmatter.split('\n').forEach((pair) => {
const i = pair.indexOf(':');
metadata[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();
metadata[pair.slice(0, i).trim()] = removeQuotes(pair.slice(i + 1).trim());
});
return { metadata, body };
}
/** @param {string} str */
const removeQuotes = (str) => str.replace(/(^["']|["']$)/g, '');

@ -2,6 +2,6 @@ import * as session from '$lib/db/session';
export async function load({ request }) {
return {
user: session.from_cookie(request.headers.get('cookie')),
user: session.from_cookie(request.headers.get('cookie'))
};
}

@ -11,6 +11,6 @@ export async function load({ fetch, params, url }) {
return {
gist,
version: url.searchParams.get('version') || '3',
version: url.searchParams.get('version') || '3'
};
}

@ -14,8 +14,8 @@
{/if}
</svelte:head>
<Shell nav_visible={$page.url.pathname !== '/repl/embed'}>
{#if $page.url.pathname !== '/repl/embed'}
<div style:display={$page.url.pathname !== '/docs' ? 'contents' : 'none'}>
<Shell nav_visible={$page.url.pathname !== '/repl/embed'}>
<Nav logo="/svelte-logo.svg">
<svelte:fragment slot="nav-center">
{#if $page.url.pathname !== '/search'}
@ -46,10 +46,10 @@
</NavItem>
</svelte:fragment>
</Nav>
{/if}
<slot />
</Shell>
<slot />
</Shell>
</div>
{#if browser}
<SearchBox />

@ -18,7 +18,7 @@
<meta name="og:image" content="https://svelte.dev/blog/{$page.params.slug}/card.png" />
</svelte:head>
<article class="post listify">
<article class="post listify text">
<h1>{data.post.title}</h1>
<p class="standfirst">{data.post.description}</p>

@ -1,5 +1,9 @@
import { normalizeSlugify, removeMarkdown } from '$lib/server/docs';
import { extract_frontmatter, transform } from '$lib/server/markdown';
import {
extract_frontmatter,
normalizeSlugify,
removeMarkdown,
transform
} from '$lib/server/markdown';
import fs from 'node:fs';
import path from 'node:path';
import glob from 'tiny-glob/sync.js';
@ -11,8 +15,7 @@ const categories = [
slug: 'docs',
label: null,
/** @param {string[]} parts */
href: (parts) =>
parts.length > 1 ? `/docs/${parts[0]}#${parts.slice(1).join('-')}` : `/docs/${parts[0]}`
href: (parts) => (parts.length > 1 ? `/docs/${parts[0]}#${parts.at(-1)}` : `/docs/${parts[0]}`)
},
{
slug: 'faq',
@ -42,7 +45,7 @@ export function content() {
const { body, metadata } = extract_frontmatter(markdown);
const sections = body.trim().split(/^### /m);
const sections = body.trim().split(/^## /m);
const intro = sections.shift().trim();
const rank = +metadata.rank || undefined;
@ -55,32 +58,32 @@ export function content() {
for (const section of sections) {
const lines = section.split('\n');
const h3 = lines.shift();
const h2 = lines.shift();
const content = lines.join('\n');
const subsections = content.trim().split('### ');
const subsections = content.trim().split('## ');
const intro = subsections.shift().trim();
blocks.push({
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title), removeMarkdown(h3)],
href: category.href([slug, normalizeSlugify(h3)]),
breadcrumbs: [...breadcrumbs, removeMarkdown(metadata.title), removeMarkdown(h2)],
href: category.href([slug, normalizeSlugify(h2)]),
content: plaintext(intro),
rank
});
for (const subsection of subsections) {
const lines = subsection.split('\n');
const h4 = lines.shift();
const h3 = lines.shift();
blocks.push({
breadcrumbs: [
...breadcrumbs,
removeMarkdown(metadata.title),
removeMarkdown(h3),
removeMarkdown(h4)
removeMarkdown(h2),
removeMarkdown(h3)
],
href: category.href([slug, normalizeSlugify(h3), normalizeSlugify(h4)]),
href: category.href([slug, normalizeSlugify(h2), normalizeSlugify(h3)]),
content: plaintext(lines.join('\n').trim()),
rank
});

@ -1,22 +1,9 @@
import { extract_frontmatter } from '$lib/server/markdown';
import fs from 'fs';
import { base } from '$app/paths';
import { get_docs_data, get_docs_list } from '$lib/server/docs/get-docs';
export const prerender = true;
const base_dir = '../../site/content/docs/';
export function load() {
const sections = fs.readdirSync(base_dir).map((file) => {
const { title } = extract_frontmatter(fs.readFileSync(`${base_dir}/${file}`, 'utf-8')).metadata;
return {
title,
path: `${base}/docs/${file.slice(3, -3)}`
};
});
return {
sections
sections: get_docs_list(get_docs_data())
};
}

@ -2,10 +2,11 @@
import { page } from '$app/stores';
import Contents from './Contents.svelte';
/** @type {import('./$types').LayoutServerData}*/
export let data;
$: title = data.sections.find((val) => val.path === $page.url.pathname)?.title;
$: title = data.sections
.find(({ pages }) => pages.find(({ path }) => path === $page.url.pathname))
?.pages.find(({ path }) => path === $page.url.pathname).title;
</script>
<div class="container">
@ -38,6 +39,10 @@
display: none;
}
.page :global(:where(h2, h3) code) {
all: unset;
}
/* .content {
width: 100%;
margin: 0;
@ -53,191 +58,6 @@
}
}
.content :global(h1) {
font-size: 3.2rem;
margin: 0 0 0.5em 0;
}
.content :global(h2) {
margin-top: 8rem;
padding: 2rem 1.6rem 2rem 0.2rem;
border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
line-height: 1;
font-size: var(--sk-text-m);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.content :global(section):first-of-type > :global(h2) {
margin-top: 0;
}
.content :global(h4) {
margin: 2em 0 1em 0;
}
.content :global(.offset-anchor) {
position: relative;
display: block;
top: calc(-1 * var(--sk-page-padding-top));
width: 0;
height: 0;
}
.content :global(a.permalink) {
position: absolute;
display: block;
background: url(/icons/link.svg) 0 50% no-repeat;
background-size: 1em 1em;
width: 1.4em;
height: 1em;
left: -1.3em;
bottom: 0.3rem;
opacity: 0;
transition: opacity 0.2s;
user-select: none;
}
.content :global(h2) :global(.permalink) {
bottom: 2rem;
}
.content :global(h3) :global(.permalink) {
bottom: 1rem;
}
@media (min-width: 400px) {
.content :global(h1) {
font-size: 4.2rem;
}
}
@media (min-width: 768px) {
.content :global(h1) {
font-size: 5.4rem;
}
.content :global(.anchor:focus),
.content :global(:where(h2, h3, h4, h5, h6):hover .permalink) {
opacity: 1;
}
}
.content :global(h3),
.content :global(h3 > code) {
margin: 6.4rem 0 1rem 0;
padding: 0 0 1rem 0;
color: var(--sk-text-2);
max-width: var(--sk-line-max-width);
border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
background: transparent;
line-height: 1;
}
.content :global(h3):first-child {
border: none;
margin: 0;
}
/* avoid doubled border-top */
.content :global(h3 > code) {
border-radius: 0 0 0 0;
border: none;
font-size: inherit;
}
.content :global(h4),
.content :global(h4 > code) {
font-family: inherit;
font-weight: 600;
font-size: 2.4rem;
color: var(--sk-text-2);
margin: 6.4rem 0 1.6rem 0;
padding-left: 0;
background: transparent;
line-height: 1;
padding-top: 0;
top: 0;
}
.content :global(h4::before) {
display: inline;
content: ' ';
block-size: var(--sk-nav-height);
margin-block-start: calc(-1 * var(--sk-nav-height));
}
.content :global(h4 > em) {
opacity: 0.7;
}
.content :global(h4 > .anchor) {
top: 0.05em;
}
.content :global(h5) {
font-size: 2.4rem;
margin: 2em 0 0.5em 0;
}
.content :global(code) {
/* padding: 0.4rem; */
margin: 0 0.2rem;
top: -0.1rem;
background: var(--sk-back-4);
}
.content :global(pre) :global(code) {
padding: 0;
margin: 0;
top: 0;
background: transparent;
}
.content :global(pre) {
margin: 0 0 2rem 0;
width: 100%;
max-width: var(--sk-line-max-width);
padding: 1rem 1rem;
box-shadow: inset 1px 1px 6px hsla(205.7, 63.6%, 30.8%, 0.06);
}
.content :global(.icon) {
width: 2rem;
height: 2rem;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
.content :global(table) {
margin: 0 0 2em 0;
}
.content :global(section) :global(p) {
max-width: var(--sk-line-max-width);
margin: 1em 0;
}
.content :global(small) {
font-size: var(--sk-text-s);
float: right;
pointer-events: all;
color: var(--sk-theme-1);
cursor: pointer;
}
/* this replaces the offset-anchor hack, which we should remove from this CSS
once https://github.com/sveltejs/action-deploy-docs/issues/1 is closed */
.content :global(h2[id]),
.content :global(h3[id]) {
padding-top: 10rem;
margin-top: -2rem;
border-top: none;
}
/* .content :global(h2[id])::after {
content: '';
position: absolute;

@ -1 +0,0 @@
export const prerender = true;

@ -3,23 +3,214 @@
import { page } from '$app/stores';
import { onMount } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
const OLD_IDs = [
'before-we-begin',
'getting-started',
'component-format',
'component-format-script',
'component-format-script-1-export-creates-a-component-prop',
'component-format-script-2-assignments-are-reactive',
'component-format-script-3-$-marks-a-statement-as-reactive',
'component-format-script-4-prefix-stores-with-$-to-access-their-values',
'component-format-script-context-module',
'component-format-style',
'template-syntax',
'template-syntax-tags',
'template-syntax-attributes-and-props',
'template-syntax-text-expressions',
'template-syntax-comments',
'template-syntax-if',
'template-syntax-each',
'template-syntax-await',
'template-syntax-key',
'template-syntax-html',
'template-syntax-debug',
'template-syntax-const',
'template-syntax-element-directives',
'template-syntax-element-directives-on-eventname',
'template-syntax-element-directives-bind-property',
'template-syntax-element-directives-bind-group',
'template-syntax-element-directives-bind-this',
'template-syntax-element-directives-class-name',
'template-syntax-element-directives-style-property',
'template-syntax-element-directives-use-action',
'template-syntax-element-directives-transition-fn',
'template-syntax-element-directives-in-fn-out-fn',
'template-syntax-element-directives-animate-fn',
'template-syntax-component-directives',
'template-syntax-component-directives-on-eventname',
'template-syntax-component-directives---style-props',
'template-syntax-component-directives-bind-property',
'template-syntax-component-directives-bind-this',
'template-syntax-slot',
'template-syntax-slot-slot-name-name',
'template-syntax-slot-$$slots',
'template-syntax-slot-slot-key-value',
'template-syntax-svelte-self',
'template-syntax-svelte-component',
'template-syntax-svelte-element',
'template-syntax-svelte-window',
'template-syntax-svelte-document',
'template-syntax-svelte-body',
'template-syntax-svelte-head',
'template-syntax-svelte-options',
'template-syntax-svelte-fragment',
'run-time',
'run-time-svelte',
'run-time-svelte-onmount',
'run-time-svelte-beforeupdate',
'run-time-svelte-afterupdate',
'run-time-svelte-ondestroy',
'run-time-svelte-tick',
'run-time-svelte-setcontext',
'run-time-svelte-getcontext',
'run-time-svelte-hascontext',
'run-time-svelte-getallcontexts',
'run-time-svelte-createeventdispatcher',
'run-time-svelte-store',
'run-time-svelte-store-writable',
'run-time-svelte-store-readable',
'run-time-svelte-store-derived',
'run-time-svelte-store-get',
'run-time-svelte-store-readonly',
'run-time-svelte-motion',
'run-time-svelte-motion-tweened',
'run-time-svelte-motion-spring',
'run-time-svelte-transition',
'run-time-svelte-transition-fade',
'run-time-svelte-transition-blur',
'run-time-svelte-transition-fly',
'run-time-svelte-transition-slide',
'run-time-svelte-transition-scale',
'run-time-svelte-transition-draw',
'run-time-svelte-transition-crossfade',
'run-time-svelte-animate',
'run-time-svelte-animate-flip',
'run-time-svelte-easing',
'run-time-svelte-register',
'run-time-client-side-component-api',
'run-time-client-side-component-api-creating-a-component',
'run-time-client-side-component-api-$set',
'run-time-client-side-component-api-$on',
'run-time-client-side-component-api-$destroy',
'run-time-client-side-component-api-component-props',
'run-time-custom-element-api',
'run-time-server-side-component-api',
'compile-time',
'compile-time-svelte-compile',
'compile-time-svelte-parse',
'compile-time-svelte-preprocess',
'compile-time-svelte-walk',
'compile-time-svelte-version',
'accessibility-warnings',
'accessibility-warnings-a11y-accesskey',
'accessibility-warnings-a11y-aria-activedescendant-has-tabindex',
'accessibility-warnings-a11y-aria-attributes',
'accessibility-warnings-a11y-autofocus',
'accessibility-warnings-a11y-click-events-have-key-events',
'accessibility-warnings-a11y-distracting-elements',
'accessibility-warnings-a11y-hidden',
'accessibility-warnings-a11y-img-redundant-alt',
'accessibility-warnings-a11y-incorrect-aria-attribute-type',
'accessibility-warnings-a11y-invalid-attribute',
'accessibility-warnings-a11y-interactive-supports-focus',
'accessibility-warnings-a11y-label-has-associated-control',
'accessibility-warnings-a11y-media-has-caption',
'accessibility-warnings-a11y-misplaced-role',
'accessibility-warnings-a11y-misplaced-scope',
'accessibility-warnings-a11y-missing-attribute',
'accessibility-warnings-a11y-missing-content',
'accessibility-warnings-a11y-mouse-events-have-key-events',
'accessibility-warnings-a11y-no-redundant-roles',
'accessibility-warnings-a11y-no-interactive-element-to-noninteractive-role',
'accessibility-warnings-a11y-no-noninteractive-element-to-interactive-role',
'accessibility-warnings-a11y-no-noninteractive-tabindex',
'accessibility-warnings-a11y-positive-tabindex',
'accessibility-warnings-a11y-role-has-required-aria-props',
'accessibility-warnings-a11y-role-supports-aria-props',
'accessibility-warnings-a11y-structure',
'accessibility-warnings-a11y-unknown-aria-attribute',
'accessibility-warnings-a11y-unknown-role',
];
/** @type {Map<RegExp, string>}*/
const pages_regex_map = new Map([
// Basic ones
[/(before-we-begin|getting-started)$/i, 'introduction'],
[/(component-format|template-syntax)$/i, 'dot-svelte-files'],
[/run-time$/i, 'svelte'],
[/compile-time$/i, 'svelte-compiler'],
[/(accessibility-warnings)$/i, '$1'],
// component-format-
[/component-format-(script|style|script-context-module)$/i, 'dot-svelte-files#$1'],
[/component-format-(?:script)(?:-?(.*))$/i, 'dot-svelte-files#$1'],
// template-syntax
[/template-syntax-((?:element|component)-directives)-?(.*)/i, '$1#$2'],
[/template-syntax-slot$/i, 'special-elements#slot'],
[/template-syntax-(?:slot)-?(.*)/i, 'special-elements#$1'],
[/template-syntax-(if|each|await|key)$/i, 'logic-blocks#$1'],
[/template-syntax-(const|debug|html)$/i, 'special-tags#$1'],
[
/template-syntax-(tags|attributes-and-props|text-expressions|comments)$/i,
'dot-svelte-files#$1',
],
// !!!! This one should stay at the bottom of `template-syntax`, or it may end up hijacking logic blocks and special tags
[/template-syntax-(.+)/i, 'special-elements#$1'],
// run-time
[/run-time-(svelte-(?:store|motion|transition|animate))-?(.*)/i, '$1#$2'],
[/run-time-(client-side-component-api)-?(.*)/i, '$1#$2'],
[
/run-time-(svelte-easing|server-side-component-api|custom-element-api|svelte-register)$/i,
'$1',
],
// Catch all, should be at the end or will include store, motion, transition and other modules starting with svelte
[/run-time-(svelte)(?:-(.+))?/i, '$1#$2'],
// Compile time
[/compile-time-?(.*)/i, 'svelte-compiler#$1'],
// Accessibility warnings
[/(accessibility-warnings)-?(.+)/i, '$1#$2'],
]);
function get_old_new_ids_map() {
/** @type {Map<string, string>} */
const new_ids = new Map();
old_id_block: for (const old_id of OLD_IDs) {
for (const [regex, replacement] of pages_regex_map) {
if (regex.test(old_id)) {
new_ids.set(
old_id,
old_id
.replace(regex, replacement)
.replace(/#$/, '') // Replace trailing # at the end
.replace('#--', '#') // have to do the -- replacement because of `--style-props` in old being `style-props` in new
);
continue old_id_block;
}
}
}
return new_ids;
}
function getURlToRedirectTo() {
const section = data.sections.find((val) =>
$page.url.hash.replace('#', '').startsWith(val.path.split('/').at(-1))
);
console.log(get_old_new_ids_map());
const hash = $page.url.hash.replace(/^#/i, '');
if (!hash) return '/docs/introduction';
if (!section) return '/docs/introduction';
const old_new_map = get_old_new_ids_map();
// Remove the section name from hash, then redirect to that
const hash = $page.url.hash.replace(`#${section.path.split('/').at(-1)}-`, '');
// ID doesn't match anything, take the user to intro page only
if (!old_new_map.has(hash)) return '/docs/introduction';
return `${section.path}#${hash}`;
return `/docs/${old_new_map.get(hash)}`;
}
onMount(() => {
goto(getURlToRedirectTo(), { replaceState: true });
});
onMount(() => goto(getURlToRedirectTo(), { replaceState: true }));
</script>

@ -1,22 +1,32 @@
<script>
import { page } from '$app/stores';
/** @type {{title: string, path: string}[]} */
export let contents;
/** @type {ReturnType<typeof import('$lib/server/docs/get-docs').get_docs_list>}*/
export let contents = [];
</script>
<nav aria-label="Docs">
<ul class="sidebar">
{#each contents as { title, path }}
{#each contents as section}
<li>
<a
data-sveltekit-preload-data
class="page"
class:active={path === $page.url.pathname}
href={path}
>
{title}
</a>
<span class="section">
{section.title}
</span>
<ul>
{#each section.pages as { title, path }}
<li>
<a
data-sveltekit-preload-data
class="page"
class:active={path === $page.url.pathname}
href={path}
>
{title}
</a>
</li>
{/each}
</ul>
</li>
{/each}
</ul>
@ -43,7 +53,7 @@
display: block;
line-height: 1.2;
margin: 0;
margin-bottom: 1rem;
margin-bottom: 4rem;
}
li:last-child {
@ -59,6 +69,15 @@
user-select: none;
}
.section {
display: block;
padding-bottom: 0.8rem;
font-size: var(--sk-text-xs);
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 600;
}
.page {
display: block;
font-size: 1.6rem;
@ -71,6 +90,7 @@
color: var(--sk-text-1);
}
ul ul,
ul ul li {
margin: 0;
}
@ -78,7 +98,7 @@
@media (min-width: 600px) {
.sidebar {
columns: 2;
/* padding-left: var(--sk-page-padding-side); */
padding-left: var(--sk-page-padding-side);
padding-right: var(--sk-page-padding-side);
}
}

@ -1,24 +1,13 @@
import fs from 'fs';
// import { read_file } from '$lib/server/docs';
import { get_parsed_docs } from '$lib/server/docs';
import { get_docs_data } from '$lib/server/docs/get-docs';
import { error } from '@sveltejs/kit';
import { read_file } from '$lib/server/docs';
export const prerender = true;
const base = '../../site/content/docs/';
/**
* ASSUMPTION FOR FUTURE: This assumes the directory structure of docs is flat. AKA, no nested folders
*/
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
for (const file of fs.readdirSync(`${base}`)) {
if (file.slice(3, -3) === params.slug) {
return {
page: await read_file(file)
};
}
}
const processed_page = get_parsed_docs(get_docs_data(), params.slug);
if (!processed_page) throw error(404);
throw error(404);
return { page: processed_page };
}

@ -1,7 +1,6 @@
<script>
import OnThisPage from './OnThisPage.svelte';
/** @type {import('./$types').PageData}*/
export let data;
</script>
@ -13,6 +12,4 @@
{@html data.page.content}
</div>
{#if data.page.sections.length !== 0}
<OnThisPage details={data.page} />
{/if}
<OnThisPage details={data.page} />

@ -1,8 +1,8 @@
<script>
import { onMount } from 'svelte';
import { afterNavigate } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { afterUpdate, onMount } from 'svelte';
/** @type {import('./$types').PageData['page']} */
export let details;
@ -22,9 +22,13 @@
/** @type {number[]} */
let positions = [];
/** @type {HTMLElement} */
let containerEl;
let show_contents = false;
onMount(async () => {
await document.fonts.ready;
update();
highlight();
});
@ -37,21 +41,17 @@
function update() {
content = document.querySelector('.content');
const { top } = content.getBoundingClientRect();
headings = content.querySelectorAll('h2[id]');
positions = Array.from(headings).map((heading) => {
const style = getComputedStyle(heading);
return heading.getBoundingClientRect().top - parseFloat(style.scrollMarginTop) - top;
});
height = window.innerHeight;
}
function highlight() {
const { top, bottom } = content.getBoundingClientRect();
let i = headings.length;
while (i--) {
if (bottom - height < 50 || positions[i] + top < 100) {
const heading = headings[i];
@ -59,7 +59,6 @@
return;
}
}
hash = '';
}
@ -69,7 +68,6 @@
setTimeout(() => {
hash = url.hash;
});
// ...and braces
window.addEventListener(
'scroll',
@ -79,11 +77,37 @@
{ once: true }
);
}
afterUpdate(() => {
// bit of a hack — prevent sidebar scrolling if
// TOC is open on mobile, or scroll came from within sidebar
if (show_contents && window.innerWidth < 832) return;
const active = containerEl.querySelector('.active');
if (active) {
const { top, bottom } = active.getBoundingClientRect();
const min = 100;
const max = window.innerHeight - 100;
if (top > max) {
containerEl.scrollBy({
top: top - max,
left: 0,
behavior: 'smooth',
});
} else if (bottom < min) {
containerEl.scrollBy({
top: bottom - min,
left: 0,
behavior: 'smooth',
});
}
}
});
</script>
<svelte:window on:scroll={highlight} on:resize={update} on:hashchange={() => select($page.url)} />
<aside class="on-this-page">
<aside class="on-this-page" bind:this={containerEl}>
<h2>On this page</h2>
<nav>
<ul>
@ -99,10 +123,12 @@
.on-this-page {
display: var(--on-this-page-display);
position: fixed;
padding: 0 var(--sk-page-padding-side) 0 0;
padding: var(--sk-page-padding-top) var(--sk-page-padding-side) 0 0;
width: min(280px, calc(var(--sidebar-width) - var(--sk-page-padding-side)));
height: calc(100vh - var(--sk-nav-height) - var(--sk-page-padding-top));
top: var(--sk-nav-height);
left: calc(100vw - (var(--sidebar-width)));
overflow-y: auto;
}
h2 {

@ -14,7 +14,8 @@
<ul class="examples-toc">
{#each sections as section}
{#if section.title !== undefined}
<!-- Avoid embeds -->
{#if section.title !== 'Embeds'}
<li>
<span class="section-title">{section.title}</span>

@ -3,6 +3,8 @@ import { get_example } from '$lib/server/examples';
import { get_examples_list } from '$lib/server/examples/get-examples';
import { error, json } from '@sveltejs/kit';
export const prerender = true;
export const GET = ({ params }) => {
const examples = new Set(
get_examples_list(examples_data)

@ -9,7 +9,7 @@ export default {
adapter: adapter(),
prerender: {
// TODO use route entries instead, once https://github.com/sveltejs/kit/pull/9571 is merged
entries: ['*', ...repl_json_entries(), ...tutorial_entries()]
entries: ['*', ...repl_json_entries(), ...tutorial_entries(), ...examples_json_entries()]
}
}
};
@ -29,3 +29,11 @@ function tutorial_entries() {
tutorials.map(({ slug }) => /** @type {(`/${string}`)} */ (`/tutorial/${slug}`))
);
}
function examples_json_entries() {
return get_examples_list(
get_examples_data(new URL('../../site/content/examples', import.meta.url).pathname)
).flatMap(({ examples }) =>
examples.map(({ slug }) => /** @type {(`/${string}`)} */ (`/examples/api/${slug}.json`))
);
}

Loading…
Cancel
Save