diff --git a/site/content/tutorial/08-stores/06-store-bindings/app-a/App.svelte b/site/content/tutorial/08-stores/06-store-bindings/app-a/App.svelte
new file mode 100644
index 0000000000..3839224826
--- /dev/null
+++ b/site/content/tutorial/08-stores/06-store-bindings/app-a/App.svelte
@@ -0,0 +1,6 @@
+
+
+
` element:
+
+```html
+
+ Add exclamation mark!
+
+```
+
+The `$name += '!'` assignment is equivalent to `name.set($name + '!')`.
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/App.svelte b/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/App.svelte
deleted file mode 100644
index c46096c204..0000000000
--- a/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/App.svelte
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/crossfade.js b/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/crossfade.js
deleted file mode 100644
index e11e18b60e..0000000000
--- a/site/content/tutorial/10-transitions/07-deferred-transitions/app-a/crossfade.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { quintOut } from 'svelte/easing';
-
-export default function crossfade({ send, receive, fallback }) {
- let requested = new Map();
- let provided = new Map();
-
- function crossfade(from, node) {
- const to = node.getBoundingClientRect();
- const dx = from.left - to.left;
- const dy = from.top - to.top;
-
- const style = getComputedStyle(node);
- const transform = style.transform === 'none' ? '' : style.transform;
-
- return {
- duration: 400,
- easing: quintOut,
- css: (t, u) => `
- opacity: ${t};
- transform: ${transform} translate(${u * dx}px,${u * dy}px);
- `
- };
- }
-
- return {
- send(node, params) {
- provided.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (requested.has(params.key)) {
- const { rect } = requested.get(params.key);
- requested.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- // if the node is disappearing altogether
- // (i.e. wasn't claimed by the other list)
- // then we need to supply an outro
- provided.delete(params.key);
- return fallback(node, params);
- };
- },
-
- receive(node, params) {
- requested.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (provided.has(params.key)) {
- const { rect } = provided.get(params.key);
- provided.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- requested.delete(params.key);
- return fallback(node, params);
- };
- }
- };
-}
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/App.svelte b/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/App.svelte
deleted file mode 100644
index c46096c204..0000000000
--- a/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/App.svelte
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/crossfade.js b/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/crossfade.js
deleted file mode 100644
index e11e18b60e..0000000000
--- a/site/content/tutorial/10-transitions/07-deferred-transitions/app-b/crossfade.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { quintOut } from 'svelte/easing';
-
-export default function crossfade({ send, receive, fallback }) {
- let requested = new Map();
- let provided = new Map();
-
- function crossfade(from, node) {
- const to = node.getBoundingClientRect();
- const dx = from.left - to.left;
- const dy = from.top - to.top;
-
- const style = getComputedStyle(node);
- const transform = style.transform === 'none' ? '' : style.transform;
-
- return {
- duration: 400,
- easing: quintOut,
- css: (t, u) => `
- opacity: ${t};
- transform: ${transform} translate(${u * dx}px,${u * dy}px);
- `
- };
- }
-
- return {
- send(node, params) {
- provided.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (requested.has(params.key)) {
- const { rect } = requested.get(params.key);
- requested.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- // if the node is disappearing altogether
- // (i.e. wasn't claimed by the other list)
- // then we need to supply an outro
- provided.delete(params.key);
- return fallback(node, params);
- };
- },
-
- receive(node, params) {
- requested.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (provided.has(params.key)) {
- const { rect } = provided.get(params.key);
- provided.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- requested.delete(params.key);
- return fallback(node, params);
- };
- }
- };
-}
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-deferred-transitions/text.md b/site/content/tutorial/10-transitions/07-deferred-transitions/text.md
deleted file mode 100644
index e649762f57..0000000000
--- a/site/content/tutorial/10-transitions/07-deferred-transitions/text.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Deferred transitions
----
-
-A particularly powerful feature of Svelte's transition engine is the ability to *defer* transitions, so that they can be coordinated between multiple elements.
-
-TODO https://github.com/sveltejs/svelte/issues/2159
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-local-transitions/app-a/App.svelte b/site/content/tutorial/10-transitions/07-local-transitions/app-a/App.svelte
new file mode 100644
index 0000000000..ed93815c0d
--- /dev/null
+++ b/site/content/tutorial/10-transitions/07-local-transitions/app-a/App.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ show list
+
+
+
+
+
+
+
+{#if showItems}
+ {#each items.slice(0, i) as item}
+
+ {item}
+
+ {/each}
+{/if}
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-local-transitions/app-b/App.svelte b/site/content/tutorial/10-transitions/07-local-transitions/app-b/App.svelte
new file mode 100644
index 0000000000..3d569cfcbb
--- /dev/null
+++ b/site/content/tutorial/10-transitions/07-local-transitions/app-b/App.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ show list
+
+
+
+
+
+
+
+{#if showItems}
+ {#each items.slice(0, i) as item}
+
+ {item}
+
+ {/each}
+{/if}
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/07-local-transitions/text.md b/site/content/tutorial/10-transitions/07-local-transitions/text.md
new file mode 100644
index 0000000000..7a22967ab0
--- /dev/null
+++ b/site/content/tutorial/10-transitions/07-local-transitions/text.md
@@ -0,0 +1,15 @@
+---
+title: Local transitions
+---
+
+Ordinarily, transitions will play on elements when any container block is added or destroyed. In the example here, toggling the visibility of the entire list also applies transitions to individual list elements.
+
+Instead, we'd like transitions to play only when individual items are added and removed — on other words, when the user drags the slider.
+
+We can achieve this with a *local* transition, which only plays when the immediate parent block is added or removed:
+
+```html
+
+ {item}
+
+```
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/08-deferred-transitions/app-a/App.svelte b/site/content/tutorial/10-transitions/08-deferred-transitions/app-a/App.svelte
new file mode 100644
index 0000000000..e869816730
--- /dev/null
+++ b/site/content/tutorial/10-transitions/08-deferred-transitions/app-a/App.svelte
@@ -0,0 +1,148 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/08-deferred-transitions/app-b/App.svelte b/site/content/tutorial/10-transitions/08-deferred-transitions/app-b/App.svelte
new file mode 100644
index 0000000000..3cb21a8ed9
--- /dev/null
+++ b/site/content/tutorial/10-transitions/08-deferred-transitions/app-b/App.svelte
@@ -0,0 +1,155 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/site/content/tutorial/10-transitions/08-deferred-transitions/text.md b/site/content/tutorial/10-transitions/08-deferred-transitions/text.md
new file mode 100644
index 0000000000..49f626554d
--- /dev/null
+++ b/site/content/tutorial/10-transitions/08-deferred-transitions/text.md
@@ -0,0 +1,30 @@
+---
+title: Deferred transitions
+---
+
+A particularly powerful feature of Svelte's transition engine is the ability to *defer* transitions, so that they can be coordinated between multiple elements.
+
+Take this pair of todo lists, in which toggling a todo sends it to the opposite list. In the real world, objects don't behave like that — instead of disappearing and reappearing in another place, they move through a series of intermediate positions. Using motion can go a long way towards helping users understand what's happening in your app.
+
+We can achieve this effect using the `crossfade` function, which creates a pair of transitions 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.
+
+Find the `` element on line 65, and add the `send` and `receive` transitions:
+
+```html
+
+```
+
+Do the same for the next `` element:
+
+```html
+
+```
+
+Now, when you toggle items, they move smoothly to their new location. The non-transitioning items still jump around awkwardly — we can fix that in the next chapter.
\ No newline at end of file
diff --git a/site/content/tutorial/11-animations/01-animate/app-a/App.svelte b/site/content/tutorial/11-animations/01-animate/app-a/App.svelte
index c46096c204..3cb21a8ed9 100644
--- a/site/content/tutorial/11-animations/01-animate/app-a/App.svelte
+++ b/site/content/tutorial/11-animations/01-animate/app-a/App.svelte
@@ -1,8 +1,10 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/site/content/tutorial/11-animations/01-animate/app-a/crossfade.js b/site/content/tutorial/11-animations/01-animate/app-a/crossfade.js
deleted file mode 100644
index e11e18b60e..0000000000
--- a/site/content/tutorial/11-animations/01-animate/app-a/crossfade.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { quintOut } from 'svelte/easing';
-
-export default function crossfade({ send, receive, fallback }) {
- let requested = new Map();
- let provided = new Map();
-
- function crossfade(from, node) {
- const to = node.getBoundingClientRect();
- const dx = from.left - to.left;
- const dy = from.top - to.top;
-
- const style = getComputedStyle(node);
- const transform = style.transform === 'none' ? '' : style.transform;
-
- return {
- duration: 400,
- easing: quintOut,
- css: (t, u) => `
- opacity: ${t};
- transform: ${transform} translate(${u * dx}px,${u * dy}px);
- `
- };
- }
-
- return {
- send(node, params) {
- provided.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (requested.has(params.key)) {
- const { rect } = requested.get(params.key);
- requested.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- // if the node is disappearing altogether
- // (i.e. wasn't claimed by the other list)
- // then we need to supply an outro
- provided.delete(params.key);
- return fallback(node, params);
- };
- },
-
- receive(node, params) {
- requested.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (provided.has(params.key)) {
- const { rect } = provided.get(params.key);
- provided.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- requested.delete(params.key);
- return fallback(node, params);
- };
- }
- };
-}
\ No newline at end of file
diff --git a/site/content/tutorial/11-animations/01-animate/app-b/App.svelte b/site/content/tutorial/11-animations/01-animate/app-b/App.svelte
index c46096c204..6194dd941e 100644
--- a/site/content/tutorial/11-animations/01-animate/app-b/App.svelte
+++ b/site/content/tutorial/11-animations/01-animate/app-b/App.svelte
@@ -1,8 +1,11 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/site/content/tutorial/11-animations/01-animate/app-b/crossfade.js b/site/content/tutorial/11-animations/01-animate/app-b/crossfade.js
deleted file mode 100644
index e11e18b60e..0000000000
--- a/site/content/tutorial/11-animations/01-animate/app-b/crossfade.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { quintOut } from 'svelte/easing';
-
-export default function crossfade({ send, receive, fallback }) {
- let requested = new Map();
- let provided = new Map();
-
- function crossfade(from, node) {
- const to = node.getBoundingClientRect();
- const dx = from.left - to.left;
- const dy = from.top - to.top;
-
- const style = getComputedStyle(node);
- const transform = style.transform === 'none' ? '' : style.transform;
-
- return {
- duration: 400,
- easing: quintOut,
- css: (t, u) => `
- opacity: ${t};
- transform: ${transform} translate(${u * dx}px,${u * dy}px);
- `
- };
- }
-
- return {
- send(node, params) {
- provided.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (requested.has(params.key)) {
- const { rect } = requested.get(params.key);
- requested.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- // if the node is disappearing altogether
- // (i.e. wasn't claimed by the other list)
- // then we need to supply an outro
- provided.delete(params.key);
- return fallback(node, params);
- };
- },
-
- receive(node, params) {
- requested.set(params.key, {
- rect: node.getBoundingClientRect()
- });
-
- return () => {
- if (provided.has(params.key)) {
- const { rect } = provided.get(params.key);
- provided.delete(params.key);
-
- return crossfade(rect, node);
- }
-
- requested.delete(params.key);
- return fallback(node, params);
- };
- }
- };
-}
\ No newline at end of file
diff --git a/site/content/tutorial/11-animations/01-animate/text.md b/site/content/tutorial/11-animations/01-animate/text.md
index 7b8268e37b..9a1808e259 100644
--- a/site/content/tutorial/11-animations/01-animate/text.md
+++ b/site/content/tutorial/11-animations/01-animate/text.md
@@ -2,4 +2,36 @@
title: The animate directive
---
-TODO fix https://github.com/sveltejs/svelte/issues/2159 before working on `animate`
\ No newline at end of file
+In the [previous chapter](tutorial/deferred-transitions), we used deferred transitions to create the illusion of motion as elements move from one todo list to the other.
+
+To complete the illusion, we also need to apply motion to the elements that *aren't* transitioning. For this, we use the `animate` directive.
+
+First, import the `flip` function — flip stands for ['First, Last, Invert, Play'](https://aerotwist.com/blog/flip-your-animations/) — from `svelte/animate`:
+
+```js
+import { flip } from 'svelte/animate';
+```
+
+Then add it to the `` elements:
+
+```html
+
+```
+
+The movement is a little slow in this case, so we can add a `duration` parameter:
+
+```html
+
+```
+
+> `duration` can also be a `d => milliseconds` function, where `d` is the number of pixels the element has to travel
+
+Note that all the transitions and animations are being applied with CSS, rather than JavaScript, meaning they won't block (or be blocked by) the main thread.
\ No newline at end of file