diff --git a/site/README.md b/site/README.md index 1ba94e8b56..c438bda5ca 100644 --- a/site/README.md +++ b/site/README.md @@ -17,13 +17,17 @@ By default, the REPL will fetch the most recent version of Svelte from https://u ## REPL GitHub integration -In order for the REPL's GitHub integration to work properly when running locally, you will need to create a GitHub OAuth app. Set its authorization callback URL to `http://localhost:3000/auth/callback`, and in this project, create `site/.env` containing: - -``` -GITHUB_CLIENT_ID=[your app's client id] -GITHUB_CLIENT_SECRET=[your app's client secret] -BASEURL=http://localhost:3000 -``` +In order for the REPL's GitHub integration to work properly when running locally, you will need to: +- [create a GitHub OAuth app](https://github.com/settings/developers): + - set `Authorization callback URL` to `http://localhost:3000/auth/callback`; + - set `Application name` as you like, and `Homepage URL` as `http://localhost:3000/`; + - create the app and take note of `Client ID` and `Client Secret` +- in this repo, create `site/.env` containing: + ``` + GITHUB_CLIENT_ID=[your app's Client ID] + GITHUB_CLIENT_SECRET=[your app's Client Secret] + BASEURL=http://localhost:3000 + ``` ## Translating the API docs diff --git a/site/content/docs/01-component-format.md b/site/content/docs/01-component-format.md index d116dd8ff4..93af09b283 100644 --- a/site/content/docs/01-component-format.md +++ b/site/content/docs/01-component-format.md @@ -56,6 +56,8 @@ To change component state and trigger a re-render, just assign to a locally decl Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. +Because Svelte's reactivity is based on assignments, using array methods like `.push()` and `.splice()` won't automatically trigger updates. Options for getting around this can be found in the [tutorial](tutorial/updating-arrays-and-objects). + ```html + +``` + +--- + +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. + +```html + + + +``` ### `svelte/store` @@ -475,13 +506,51 @@ TODO * fade, fly, slide, scale, draw * crossfade... -### `svelte/animation` +### `svelte/animate` -TODO +The `svelte/animate` module exports one function for use with svelte [animations](docs#Animations). + +#### `flip` + +```sv +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`](docs#cubicOut)) — an [easing function](docs#svelte_easing) + + +`duration` can be 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) + + +```html + + +{#each list as n (n)} +
+ {n} +
+{/each} +``` -* TODO this doesn't even exist yet -TODO ### `svelte/easing` diff --git a/site/content/tutorial/01-introduction/01-basics/text.md b/site/content/tutorial/01-introduction/01-basics/text.md index 95375edb25..c0fa2f48a3 100644 --- a/site/content/tutorial/01-introduction/01-basics/text.md +++ b/site/content/tutorial/01-introduction/01-basics/text.md @@ -29,4 +29,4 @@ Each tutorial chapter will have a 'Show me' button that you can click if you get ## Understanding components -In Svelte, an application is composed from one or more *components*. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a `.svelte` file. The 'hello world' example on the right is a simple component. \ No newline at end of file +In Svelte, an application is composed from one or more *components*. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a `.svelte` file. The 'hello world' example in the code editor is a simple component. diff --git a/site/src/routes/docs/_GuideContents.svelte b/site/src/routes/docs/_GuideContents.svelte new file mode 100644 index 0000000000..f5407164e4 --- /dev/null +++ b/site/src/routes/docs/_GuideContents.svelte @@ -0,0 +1,140 @@ + + + + + diff --git a/site/src/routes/examples/_TableOfContents.svelte b/site/src/routes/examples/_TableOfContents.svelte index 0de742b172..f89a9741ae 100644 --- a/site/src/routes/examples/_TableOfContents.svelte +++ b/site/src/routes/examples/_TableOfContents.svelte @@ -22,7 +22,7 @@ .section-title { display: block; - padding: 0 0 .8rem 0; + padding: 0 0 0.8rem 0; font: 400 var(--h6) var(--font); text-transform: uppercase; letter-spacing: 0.12em; @@ -32,7 +32,7 @@ a { display: flex; position: relative; - color: white; + color: var(--sidebar-text); border-bottom: none; padding: 0.2rem 3rem; margin: 0 -3rem; @@ -42,17 +42,19 @@ } a:hover { - color: var(--flash); + color: white; } a.active { - background: rgba(255, 255, 255, 0.1) calc(100% - 3rem) 50% no-repeat url(/icons/arrow-right.svg); + background: rgba(0, 0, 0, 0.15) calc(100% - 3rem) 50% no-repeat + url(/icons/arrow-right.svg); background-size: 1em 1em; color: white; } a.active.loading { - background: rgba(255, 255, 255, 0.1) calc(100% - 3rem) 50% no-repeat url(/icons/loading.svg); + background: rgba(0, 0, 0, 0.1) calc(100% - 3rem) 50% no-repeat + url(/icons/loading.svg); background-size: 1em 1em; color: white; } @@ -63,34 +65,34 @@ width: 5rem; height: 5rem; border-radius: 2px; - box-shadow: 1px 1px 3px rgba(0,0,0,0.13); + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.13); margin: 0.2em 0.5em 0.2em 0; } diff --git a/site/src/routes/repl/_components/AppControls/index.svelte b/site/src/routes/repl/_components/AppControls/index.svelte index 446d1a2781..deeae78623 100644 --- a/site/src/routes/repl/_components/AppControls/index.svelte +++ b/site/src/routes/repl/_components/AppControls/index.svelte @@ -6,6 +6,7 @@ import downloadBlob from '../../_utils/downloadBlob.js'; import { user } from '../../../../user.js'; import { enter } from '../../../../utils/events.js'; + import { isMac } from '../../../../utils/compat.js'; const dispatch = createEventDispatcher(); @@ -20,8 +21,6 @@ let justSaved = false; let justForked = false; - const isMac = typeof navigator !== 'undefined' && navigator.platform === 'MacIntel'; - function wait(ms) { return new Promise(f => setTimeout(f, ms)); } diff --git a/site/src/routes/repl/index.svelte b/site/src/routes/repl/index.svelte index e89c3e0587..39dcfd78f9 100644 --- a/site/src/routes/repl/index.svelte +++ b/site/src/routes/repl/index.svelte @@ -27,7 +27,7 @@ let repl; let gist; - let name = 'loading...'; + let name = 'Loading...'; let zen_mode = false; let relaxed = false; let width = process.browser ? window.innerWidth : 1000; @@ -58,7 +58,9 @@ if (gist_id) { relaxed = false; - fetch(`gist/${gist_id}`).then(r => r.json()).then(data => { + fetch(`gist/${gist_id}`) + .then(r => r.json()) + .then(data => { gist = data; const { description, files } = data; @@ -201,7 +203,7 @@ - REPL • Svelte + {name} • REPL • Svelte @@ -212,9 +214,9 @@
diff --git a/site/src/routes/tutorial/[slug]/_TableOfContents.svelte b/site/src/routes/tutorial/[slug]/_TableOfContents.svelte index 7491ebca87..7b0d4b5de0 100644 --- a/site/src/routes/tutorial/[slug]/_TableOfContents.svelte +++ b/site/src/routes/tutorial/[slug]/_TableOfContents.svelte @@ -30,13 +30,17 @@ display: block; padding: 0.7em 0; text-align: center; - opacity: 0.7; + opacity: 0.75; color: white; } + a:hover { + opacity: 1; + } + a.disabled, a.disabled:hover, a.disabled:active { color: white; - opacity: 0.4; + opacity: 0.3; } span { diff --git a/site/src/routes/tutorial/[slug]/index.svelte b/site/src/routes/tutorial/[slug]/index.svelte index 17d8d95cbc..34c9d38f79 100644 --- a/site/src/routes/tutorial/[slug]/index.svelte +++ b/site/src/routes/tutorial/[slug]/index.svelte @@ -151,7 +151,7 @@ height: 100%; border-right: 1px solid var(--second); background-color: var(--second); - color: white; + color: var(--sidebar-text); } .chapter-markup { @@ -165,6 +165,7 @@ margin: 4rem 0 1.6rem 0; font-size: var(--h3); line-height: 1; + font-weight: 400; color: white; } @@ -173,16 +174,21 @@ } .chapter-markup :global(a) { + color: var(--sidebar-text); + } + + .chapter-markup :global(a:hover) { color: white; } + .chapter-markup :global(ul) { padding: 0 0 0 2em; } .chapter-markup :global(blockquote) { - background-color: rgba(255,255,255,.1); - color: white; + background-color: rgba(0,0,0,.17); + color: var(--sidebar-text); } .chapter-markup::-webkit-scrollbar { @@ -198,22 +204,22 @@ .chapter-markup :global(p) > :global(code), .chapter-markup :global(ul) :global(code) { - color: white; - background: rgba(255,255,255,.1); - padding: .2em .4em; + color: var(--sidebar-text); + background: rgba(0,0,0,.12); + padding: .2em .4em .3em; white-space: nowrap; position: relative; top: -0.1em; } .controls { - border-top: 1px solid rgba(255,255,255,.1); + border-top: 1px solid rgba(255,255,255,.15); padding: 1em 0 0 0; display: flex; } .show { - background: rgba(255,255,255,.1); + background: rgba(0,0,0,.4); padding: .2em .7em .3em; border-radius: var(--border-r); top: .1em; @@ -223,22 +229,17 @@ } .show:hover { - background: rgba(255,255,255,.2); + background: rgba(0,0,0,.65); + color: white; } a.next { - /* border-bottom: none; */ padding-right: 1.2em; background: no-repeat 100% 50% url(/icons/arrow-right.svg); background-size: 1em 1em; margin-left: auto; } - a.next:hover { - /* border-bottom: 2px solid currentColor; */ - /* text-decoration: underline; */ - } - .improve-chapter { padding: 1em 0 .5em 0; } diff --git a/site/src/server.js b/site/src/server.js index 3413e218d8..fabda7c0ce 100644 --- a/site/src/server.js +++ b/site/src/server.js @@ -82,11 +82,12 @@ if (process.env.GITHUB_CLIENT_ID) { res.end(`

Missing .env file

-

In order to use GitHub authentication, you will need to register an OAuth application with gist and read:user scopes, and create a .env file:

+

In order to use GitHub authentication, you will need to register an OAuth application and create a local .env file:

GITHUB_CLIENT_ID=[YOUR_APP_ID]\nGITHUB_CLIENT_SECRET=[YOUR_APP_SECRET]\nBASEURL=http://localhost:3000

The BASEURL variable should match the callback URL specified for your app.

+

See also here

`); }); diff --git a/site/src/utils/compat.js b/site/src/utils/compat.js new file mode 100644 index 0000000000..0b0f1e954b --- /dev/null +++ b/site/src/utils/compat.js @@ -0,0 +1 @@ +export const isMac = typeof navigator !== 'undefined' && navigator.platform === 'MacIntel'; diff --git a/site/static/global.css b/site/static/global.css new file mode 100644 index 0000000000..6c84f1abee --- /dev/null +++ b/site/static/global.css @@ -0,0 +1,497 @@ +/* +----------------------------------------------- + vars – css custom-properties + + NOTE + - some vars change inside media-queries! + - under normal conditions, there's no need to touch these +----------------------------------------------- +*/ +:root { + --nav-h: 6rem; + --top-offset: 6rem; + --sidebar-w: 30rem; + --sidebar-mid-w: 36rem; + --sidebar-large-w: 48rem; + --main-width: 80rem; + --code-w: 72em; + --side-nav: 3.2rem; + --side-page: var(--side-nav); + + /* easings */ + --in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19); + --out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1); + --inout-cubic: cubic-bezier(0.645, 0.045, 0.355, 1); + + --in-back: cubic-bezier(0.6, -0.28, 0.735, 0.045); + --out-back: cubic-bezier(0.175, 0.885, 0.32, 1.275); + --inout-back: cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +@media screen and (min-width: 768px) { + :root { + --side-page: 14vw; + --top-offset: 10rem; + --side-nav: 4.8rem; + } +} + +/* theme vars */ +.theme-default { + --back: #ffffff; + --back-light: #f6fafd; + --back-api: #eff8ff; + --prime: #ff3e00; + --second: #676778; + --flash: #40b3ff; + --heading: var(--second); + --text: #444; + --sidebar-text: rgba(255, 255, 255, .75); + --border-w: .3rem; /* border-width */ + --border-r: .4rem; /* border-radius */ +} + +/* typo vars */ +.typo-default { + --unit: .8rem; + --code-fs: 1.3rem; + --h6: 1.4rem; + --h5: 1.6rem; + --h4: 1.8rem; /* default font-size */ + --h3: 2.6rem; + --h2: 3rem; + --h1: 3.2rem; + --linemax: 42em; /* max line-length */ + --lh: 1.5; /* base line-height */ +} + +body { + --font: 'Overpass', sans-serif; + --font-mono: 'Fira Mono', monospace; + --font-ui: var(--font-mono); + --font-system: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; +} + + +/* fonts ---------------------------------- */ +/* overpass-300normal - latin */ +@font-face { + font-family: 'Overpass'; + font-style: normal; + font-weight: 300; + src: + local('Overpass Light '), + local('Overpass-Light'), + url('fonts/overpass/overpass-latin-300.woff2') format('woff2'); +} + +/* overpass-600normal - latin */ +@font-face { + font-family: 'Overpass'; + font-style: normal; + font-weight: 600; + src: + local('Overpass Bold '), + local('Overpass-Bold'), + url('fonts/overpass/overpass-latin-600.woff2') format('woff2'); +} + +/* fira-mono-400normal - latin */ +@font-face { + font-family: 'Fira Mono'; + font-style: normal; + font-weight: 400; + src: + local('Fira Mono Regular '), + local('Fira Mono-Regular'), + url('fonts/fira-mono/fira-mono-latin-400.woff2') format('woff2'); +} + +/* base reset ----------------------------- */ +html { + font-size: 62.5%; + -ms-text-size-adjust: 62.5%; + -webkit-text-size-adjust: 62.5%; + -ms-overflow-style: -ms-autohiding-scrollbar; + box-sizing: border-box; + border-collapse: collapse; +} + +html, +body, +#sapper { + width: 100%; + height: 100%; +} + +* { + box-sizing: inherit; + margin: 0; + padding: 0; +} + +/* link reset ----------------------------- */ +a { + text-decoration: none; + cursor: pointer; + color: inherit; +} + +a:hover, a:active { color: var(--flash) } +a:focus { outline: none } + +/* +----------------------------------------------- + global styles +----------------------------------------------- +*/ + +/* typography ----------------------------- */ +body { + font: 300 var(--h4)/var(--lh) var(--font); + background-color: var(--back); + color: var(--text); + + /* default spacing of Overpass is a bit too airy */ + /* letter-spacing: -.013em; */ +} + +h1, h2, h3, h4, h5, h6, blockquote { + position: relative; + margin: 0; + color: var(--heading); +} + +/* h1, h2, h3, h4, h5, h6 { font-weight: 600 } */ +h6 { font-size: var(--h6) } +h5 { font-size: var(--h5) } +h4 { font-size: var(--h4) } +h3 { font-size: var(--h3) } +h2 { font-size: var(--h2) } +h1 { font-size: var(--h1) } + +h1, h2 { + font-family: var(--font); + line-height: 1.25; +} + +h3 { font-weight: 300 } + +p, ol, ul { + margin: 0 0 1em 0; +} + +.b, b, strong { font-weight: 600 } + +tt, code, kbd, samp { + font: 400 var(--code-fs)/1.7 var(--font-mono); +} + +code { + position: relative; + border-radius: .3em; + white-space: nowrap; + color: #444; + -webkit-font-smoothing: initial; +} + +pre code { + top: 0; + white-space: inherit; + background-color: none; +} + +/* sync CodeMirror with prism */ +.CodeMirror { + font-size: var(--code-fs) !important; +} + +::selection { + background: var(--flash); + color: white; +} + +/* opinionated styles --------------------- */ + +li:not(.white) > h2 { + color: var(--second) +} + +blockquote { + position: relative; + margin: 1.6rem 0 2.4rem; + padding: 2rem 2.4rem 1.8rem 2.4rem; + border-radius: var(--border-r); + font-family: var(--font); + max-width: var(--linemax); +} + +blockquote p { + font-size: var(--h5); +} + +blockquote :last-child { + margin: 0; +} + +/* buttons -------------------------------- */ +button { + font-family: inherit; + font-size: inherit; + background-color: transparent; + border: none; + color: currentColor; + cursor: pointer; +} + +button:focus, +.btn:focus { outline: 0 } + +button[disabled], +.btn[disabled], +.btn:hover[disabled] { + opacity: .55; + pointer-events: none; +} + +button > svg, +.btn > svg { + position: relative; + top: -.1rem; + width: 2rem !important; + height: 2rem !important; + stroke: currentColor !important; +} + +/* reset ------- */ +.btn { + --btn-h: 4rem; + --btn-outline: .2rem; + --btn-font: var(--font); + --btn-calc-h: calc(var(--btn-h) - var(--btn-outline) * 2); + --btn-hover: linear-gradient(to top, rgba(0,0,0,.07), rgba(0,0,0,.07)); + + position: relative; + margin: 0 .8rem .8rem 0; + vertical-align: middle; + white-space: nowrap; + display: inline-block; + zoom: 1; + border: none transparent; + font: var(--h4) var(--btn-font); + border-radius: var(--border-r); + color: currentColor; + cursor: pointer; +} + +/* default */ +.btn { + line-height: var(--btn-h); + height: var(--btn-h); + padding: 0 1.6rem; + transition: all .1s; +} + +.btn:hover { + transform: scale(.98); + mix-blend-mode: multiply; + background-image: var(--btn-hover); +} + +/* optional */ +.btn[outline] { + line-height: var(--btn-calc-h); + height: var(--btn-calc-h); + border: var(--btn-outline) solid currentColor; + background-color: white; + color: currentColor; +} + +/* links ------------------------------------- */ +a { + position: relative; + padding: 0 0 1px 0; + border-bottom: 1px solid currentColor; + user-select: none; + color: var(--prime); + transition: color .2s, border .2s, padding .2s; +} + +a:hover { + color: var(--flash); +} + +a:hover { + padding: 0; + border-bottom: 2px solid currentColor; +} + +a.no-underline { + border-bottom: none; + padding: 0; +} + +/* a:hover:not(.disabled) > .icon { stroke: var(--flash) } */ + +/* lists ---------------------------------- */ +.listify ol, +.listify ul { + --list-padding: 2.9rem; + + list-style: none; + color: currentColor; + margin-left: var(--list-padding); +} + +.listify ol > li, +.listify ul > li { + max-width: calc(var(--linemax) - var(--list-padding)); + line-height: 1.5; + margin: 0 0 0.4rem 0; +} + +.listify ul > li:before { + content: ''; + position: absolute; + margin-top: 1.1rem; + margin-left: -1.8rem; + background-color: var(--second); + width: .6rem; + height: .6rem; + border-radius: 2px; + opacity: 0.7; +} + +.listify ol { list-style: decimal } + +/* tables --------------------------------- */ +table { + width: 100%; + font-size: var(--h5); +} + +td, th { + text-align: left; + border-bottom: 1px solid #eee; + padding: 0.4rem 0.8rem 0.4rem 0; +} + +table code, table span { + white-space: pre; +} + +/* grid ----------------------------------- */ +.grid, .grid.half { + display: grid; + grid-gap: 2.4rem; + grid-template-columns: 1fr; + align-items: center; +} + +.grid.stretch { align-items: stretch } + +.grid > .cols-2, +.grid > .cols-3 { grid-column: span 1 } + +@media screen and (min-width: 840px) { + .grid.half, + .grid { grid-template-columns: repeat(2, 1fr) } + .grid > .cols-2, + .grid > .cols-3 { grid-column: span 2 } +} + +@media screen and (min-width: 1100px) { + .grid { grid-template-columns: repeat(3, 1fr) } + .grid > .cols-2 { grid-column: span 2 } + .grid > .cols-3 { grid-column: span 3 } +} + +/* helper styles -------------------------- */ +.flex-auto { flex: 1 0 auto } + +.py0 { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.legend, figcaption, .post aside { + max-width: none; + margin: 0 auto; + padding: 1.6rem 0 0 .8rem; + font: 1.2rem/1.6 var(--font-ui); +} + +.filename { + display: inline-block; + padding: 1.6rem 0 0 1rem; + font: var(--h6) var(--font-ui); +} + +.box { + padding: 2.4rem 3.2rem; + border-radius: var(--border-r); +} + +/* theme colors --------------------------- */ +.prime { color: var(--prime) !important } +.second { color: var(--second) !important } +.flash { color: var(--flash) !important } +.black { color: black !important } +.white { color: white !important } + +.back { background-color: var(--back) !important } +.back-light { background-color: var(--back-light) !important } +.bg-prime { background-color: var(--prime) !important } +.bg-second { background-color: var(--second) !important } +.bg-flash { background-color: var(--flash) !important } + +/* inputs --------------------------------- */ +input[type="checkbox"] { + /* display: block; */ + position: relative; + height: 1em; + width: calc(100% - 0.6em); + max-width: 2em; + top: -2px; + border-radius: 0.5em; + -webkit-appearance: none; + outline: none; + margin: 0 0.6em 0 0; +} + +input[type="checkbox"]::before { + content: ""; + position: absolute; + display: block; + height: 100%; + width: 100%; + padding: 2px; + border-radius: 1em; + top: 0; + left: 0; + background: var(--second); + /* box-sizing: border-box; */ + box-sizing: content-box; +} + +input[type="checkbox"]:checked::before { + background: var(--prime); +} + +input[type="checkbox"]::after { + content: ""; + position: absolute; + display: block; + height: 1em; + width: 1em; + top: 2px; + left: 2px; + border-radius: 1em; + background: white; + box-shadow: 0 0px 1px rgba(0,0,0,.4), 0 4px 2px rgba(0,0,0,.1); + -webkit-transition: background .2s ease-out, left .2s ease-out; +} + +input[type="checkbox"]:checked::after { + left: calc(100% - 9px); +} diff --git a/src/parse/state/mustache.ts b/src/parse/state/mustache.ts index 1acae36c9c..48a467a5c1 100644 --- a/src/parse/state/mustache.ts +++ b/src/parse/state/mustache.ts @@ -36,7 +36,7 @@ export default function mustache(parser: Parser) { parser.allow_whitespace(); - // {/if} or {/each} + // {/if}, {/each} or {/await} if (parser.eat('/')) { let block = parser.current(); let expected; @@ -287,13 +287,6 @@ export default function mustache(parser: Parser) { parser.allow_whitespace(); parser.eat(')', true); parser.allow_whitespace(); - } else if (parser.eat('@')) { - block.key = parser.read_identifier(); - if (!block.key) parser.error({ - code: `expected-name`, - message: `Expected name` - }); - parser.allow_whitespace(); } }