├── .gitignore ├── .prettierrc ├── README.md ├── __sapper__ ├── build │ ├── build.json │ ├── client │ │ ├── 1dde4b077584459886d3 │ │ │ ├── index.0.js │ │ │ ├── main.js │ │ │ └── vendors~index.2.js │ │ └── shimport@1.0.1.js │ └── template.html └── dev │ ├── build.json │ ├── server │ └── server.js │ └── service-worker.js ├── cypress.json ├── package-lock.json ├── package.json ├── public ├── favicon.png ├── global.css └── index.html ├── scripts └── setupTypeScript.js ├── src ├── App.svelte ├── client.js ├── components │ ├── CopyButtons.svelte │ ├── CustomCollapsible.svelte │ ├── DocTemplate.svelte │ ├── Header.svelte │ ├── Template.svelte │ ├── Templates.svelte │ ├── TemplatesNew.svelte │ ├── Tooltip.svelte │ ├── sections │ │ ├── DataCode.svelte │ │ ├── Decoration.svelte │ │ ├── PageStructure.svelte │ │ ├── PaginationUtil.svelte │ │ └── UI.svelte │ └── templates-code.js ├── examples │ ├── AutocompleteExample.svelte │ ├── BriefMessageExample.svelte │ ├── CheckboxExample.svelte │ ├── CodeExample.svelte │ ├── CollapsibleSectionExample.svelte │ ├── ConfettiExample.svelte │ ├── DataDownloadExample.svelte │ ├── IconExample.svelte │ ├── NumberExample.svelte │ ├── PaginationExample.svelte │ ├── ProgressDotExample.svelte │ ├── RadioExample.svelte │ ├── ScatterplotExample.svelte │ ├── SetExample.svelte │ ├── SwitchExample.svelte │ └── TableExample.svelte ├── main.js ├── node_modules │ └── @sapper │ │ ├── app.mjs │ │ ├── internal │ │ ├── App.svelte │ │ ├── error.svelte │ │ ├── layout.svelte │ │ ├── manifest-client.mjs │ │ ├── manifest-server.mjs │ │ └── shared.mjs │ │ ├── server.mjs │ │ └── service-worker.js ├── routes │ ├── _error.svelte │ ├── _layout.svelte │ └── index.svelte ├── server.js ├── service-worker.js ├── template.html └── utils │ └── highlight-svelte.js ├── static ├── favicon.ico ├── favicon.png ├── fonts.css ├── fonts │ ├── national │ │ ├── National2NarrowWeb-Black.woff │ │ ├── National2NarrowWeb-Black.woff2 │ │ ├── National2NarrowWeb-Bold.woff │ │ ├── National2NarrowWeb-Bold.woff2 │ │ ├── National2NarrowWeb-Extralight.woff │ │ ├── National2NarrowWeb-Extralight.woff2 │ │ ├── National2NarrowWeb-Regular.woff │ │ ├── National2NarrowWeb-Regular.woff2 │ │ ├── National2Web-Bold.woff │ │ ├── National2Web-Bold.woff2 │ │ ├── National2Web-Regular.woff │ │ └── National2Web-Regular.woff2 │ └── tiempos │ │ ├── TiemposHeadlineWeb-Regular.woff │ │ ├── TiemposHeadlineWeb-Regular.woff2 │ │ ├── TiemposTextWeb-Bold.woff │ │ ├── TiemposTextWeb-Bold.woff2 │ │ ├── TiemposTextWeb-Regular.woff │ │ └── TiemposTextWeb-Regular.woff2 ├── global.css ├── logo-192.png ├── logo-512.png ├── manifest.json └── wordmark.svg ├── templates ├── Autocomplete.svelte ├── BriefMessage.svelte ├── ButtonSet.svelte ├── Checkbox.svelte ├── CodeBlock.svelte ├── CollapsibleSection.svelte ├── Confetti.svelte ├── DataDownload.svelte ├── Icon.svelte ├── IconLocal.svelte ├── InView.svelte ├── Number.svelte ├── Pagination.svelte ├── ProgressDots.svelte ├── Radio.svelte ├── Scatterplot.svelte ├── Switch.svelte ├── Table.svelte ├── Toggle.svelte ├── icon-paths.js ├── move.js └── tweened-staggered.js ├── webpack.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | /__sapper__/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "svelteSortOrder": "scripts-markup-styles" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte Templates 2 | 3 | This is a collection of svelte components for you to use and modify for your projects. 4 | 5 | [See the components](https://pudding-svelte-templates.netlify.app/) 6 | 7 | All of the templates live in the `/templates` folder. 8 | 9 | ## Template guidelines 10 | 11 | By default, create an unstyled / MVP styled version. For an opinionated styling, put all CSS below the `/* gravy */` comment in your style tag. 12 | 13 | Make sure any UI elements either [two-way bind](https://svelte.dev/docs#bind_component_property) or [dispatch an event](https://svelte.dev/docs#createEventDispatcher) so the component can expose values. 14 | 15 | ## Adding a template 16 | 17 | As you create new basic components for svelte projects, add them to the `/templates` folder! 18 | 19 | Then, you can add documentation about your fancy new component to the site! 20 | 21 | Inside `src/components/sections` you'll find an individual svelte file for each top-level section on our site. Currently, they are: 22 | * UI Elements 23 | * Pagination 24 | * Page Structure 25 | * Data & Code 26 | * Decoration 27 | 28 | If your new component fits well under one of those categories, jump ahead to [Adding Documentation for a New Template](#newTemplate). Otherwise, start at [Adding Documentation for a New Section](#newSection). 29 | 30 | ## Adding Documentation for a New Section 31 | 32 | Each section should have its own file in `src/components/sections` so go ahead and add one. In it, you'll want to import the component `DocTemplate`. 33 | 34 | ```svelte 35 | import DocTemplate from './../DocTemplate.svelte' 36 | ``` 37 | 38 | Then, add a new `h2` element with the title for your section. 39 | 40 | ``` 41 |

UI Elements

42 | ``` 43 | 44 | Now that your section component is all set up, let's make sure it'll render on the site. Import your new component in `src/routes/index.svelte`: 45 | 46 | ``` 47 | import UI from './../components/sections/UI.svelte' 48 | ``` 49 | 50 | and adding the component to the list of other components: 51 | ``` 52 | 53 | 54 | ``` 55 | 56 | Great! Now you can add documentation for your component to your newly created section. 57 | 58 | ## Adding Documentation for a New Template 59 | 60 | To keep component documentation consistent, we'll use the component `DocTemplate` for each. This template expects 4 props of required information, and then contains slots for you to enter whatever information is relevant to your component. 61 | 62 | It also requires that you add the location of your code to the `components/templates-code.js` file, to enable code copying from the site. 63 | 64 | ### Props 65 | 66 | | Name | Type | Description | 67 | |------|------|-------------| 68 | |componentLabel| string | The human readable label for your component that will appear on the site.| 69 | |propDesc| Object of Objects | A series of objects, where each includes the `name`, `type`, and `description` of each prop your component expects | 70 | |code|string|A string of code to be rendered for your code example (see below)| 71 | |name|string|The filename of your component, without file extension | 72 | ___ 73 | 74 | If your code example is brief, you can type it directly into the ` 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scripts/setupTypeScript.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** This script modifies the project to support TS code in .svelte files like: 4 | 5 | 8 | 9 | As well as validating the code for CI. 10 | */ 11 | 12 | /** To work on this script: 13 | rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template 14 | */ 15 | 16 | const fs = require("fs") 17 | const path = require("path") 18 | const { argv } = require("process") 19 | 20 | const projectRoot = argv[2] || path.join(__dirname, "..") 21 | 22 | // Add deps to pkg.json 23 | const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) 24 | packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { 25 | "svelte-check": "^1.0.0", 26 | "svelte-preprocess": "^4.0.0", 27 | "@rollup/plugin-typescript": "^6.0.0", 28 | "typescript": "^3.9.3", 29 | "tslib": "^2.0.0", 30 | "@tsconfig/svelte": "^1.0.0" 31 | }) 32 | 33 | // Add script for checking 34 | packageJSON.scripts = Object.assign(packageJSON.scripts, { 35 | "validate": "svelte-check" 36 | }) 37 | 38 | // Write the package JSON 39 | fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) 40 | 41 | // mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too 42 | const beforeMainJSPath = path.join(projectRoot, "src", "main.js") 43 | const afterMainTSPath = path.join(projectRoot, "src", "main.ts") 44 | fs.renameSync(beforeMainJSPath, afterMainTSPath) 45 | 46 | // Switch the app.svelte file to use TS 47 | const appSveltePath = path.join(projectRoot, "src", "App.svelte") 48 | let appFile = fs.readFileSync(appSveltePath, "utf8") 49 | appFile = appFile.replace(" 4 | 5 |
6 |

Hi friend!

7 |

These templates are yours to grab and modify as you need!

8 |

Just copy the source code & paste into a new .svelte file.

9 |

10 | These can do basic color theming, if you set the css variables in a parent 11 | component: 12 |

13 |
    14 |
  • 15 | --accent-color 16 |
  • 17 |
  • 18 | --gray 19 |
  • 20 |
21 | 22 | 23 |
24 | 25 | 47 | -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import * as sapper from '@sapper/app'; 2 | 3 | sapper.start({ 4 | target: document.querySelector('#sapper') 5 | }); -------------------------------------------------------------------------------- /src/components/CopyButtons.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 | 54 | {#if isRawCodeDifferent} 55 | 68 | {/if} 69 | 70 | -------------------------------------------------------------------------------- /src/components/CustomCollapsible.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |

11 | 17 |

18 | 19 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /src/components/DocTemplate.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | 19 |
20 |
21 |

Component Description

22 | What you see is what you get! 23 |
24 |
25 |

View Code

26 | on GitHub 27 | 28 |
29 |
30 |

Use This Component

31 | 32 | 33 |
34 | 35 |
36 |

Examples

37 | 38 |
39 | 40 |
41 |

Props

42 |

This component takes {propNumber} {propNumber > 1 ? 'props' : 'prop'}:

43 | 44 | 45 | 46 |
47 |

Bound Values

48 | 49 | This component doesn't expose any props for binding. 50 | 51 |
52 | 53 |
54 |

Example Use

55 | 56 |
57 | 58 |
59 |

Accessibility

60 | 61 | This component hasn't been evaluated for accessibility concerns. 62 | 63 |
64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/components/Header.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 | 34 | -------------------------------------------------------------------------------- /src/components/Template.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 |
42 |
43 |

{name}

44 | 46 | Code 47 | 48 |
49 | 62 | {#if isRawCodeDifferent} 63 | 76 | {/if} 77 | 78 |
79 | 80 |
81 | 82 | 83 |
84 | 85 | 114 | -------------------------------------------------------------------------------- /src/components/Templates.svelte: -------------------------------------------------------------------------------- 1 | 50 | 51 |

UI Elements

52 | 53 | 71 | 72 | 94 | 95 | 102 | 103 | 122 | 123 | 154 | 155 | 225 | 226 | 287 | 288 | 364 | 365 | 410 | 411 |

Utilities

412 | 413 | 434 | 435 |

Decorations

436 | 437 | 469 | 470 |

Charts

471 | 472 | 486 | 487 | 504 | -------------------------------------------------------------------------------- /src/components/TemplatesNew.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 25 | 26 | -------------------------------------------------------------------------------- /src/components/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 | 40 | -------------------------------------------------------------------------------- /src/components/sections/DataCode.svelte: -------------------------------------------------------------------------------- 1 | 67 | 68 |

Data & Code

69 | 70 | 71 | 72 | 75 |

A component to create sortable, semantic HTML tables.

76 | on Svelte REPL 77 |

⚠️ This component also requires the Icon.svelte component (not automatically copied from the above buttons).

78 |
79 |
80 | 81 | 82 |

This component uses semantic HTML tables and button elements to make it as keyboard and screen reader navigable as possible. The sort buttons can be accessed via TAB, and selected via SPACE or ENTER.

83 | 84 | 85 | 86 | 87 | 88 | 91 |

A component to create syntax highlighted code.

92 | 93 |

⚠️ This component also requires the Highlight.js, Highlight JS Svelte, and raw loader packages to be installed to your project.

94 |
95 | 96 |
97 | 98 |
99 | 100 | 101 | 102 | 103 | 106 |

A component to create a button that, when clicked, downloads a .csv file of data.

107 | 108 |
109 | 110 |
111 | 112 |

This component uses a semantic button element to trigger the download to make it as keyboard and screen reader navigable as possible. The button can be accessed via TAB, and selected via SPACE or ENTER.

113 | 114 |

To improve accessibility even further, the data should be exported in a format that is human readable (e.g., a value of 8743804 could be exported as 8.7 million). The raw values could be retained in a second column, if preferred.

115 | 116 |
117 | 118 | 119 | 120 | 121 | 124 |

The basics for a scatterplot. This uses svelte spring, which animates the dots on change, but requires a consistent number of items in the data array.

125 | 126 |
127 | 128 | 129 |
130 | 131 |
132 | 133 | 134 | -------------------------------------------------------------------------------- /src/components/sections/Decoration.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 |

Decoration

42 | 43 | 44 | 45 | 46 |

47 | This is a message that will pop up to give quick user feedback, and 48 | disappears after a short amount of time. The message shows up whenever the text or iteration changes. Changing the iteration is a good way to make sure the message shows up when needed, even if the text doesn't change.

49 | 50 |
51 | 55 | 59 | 60 | 61 | 65 |
66 | 67 |

Be cautious when using this component with non-decorative content for accessibility reasons. Some users may not be able to read quickly enough to process the information before it disappears. Others may have read it and not retained the information. In its current form, screen reader or assistive technology users aren't identified of the notification at all. Find more information about ensuring these types of messages are as accessible as possible in this article.

68 | 69 |
70 | 71 | 72 | 73 | 74 |

75 | Displays a number with an interpolated animation when changed.

76 | 77 |
78 | 82 | 86 | 90 | 91 | 92 | 93 |
94 | 95 |

Use this component sparingly. Too much animation can be disorienting or distracting for some users.

96 | 97 |
98 | 99 | 100 | 101 | 102 | 103 |

Throw in some festive confetti!

104 | on Svelte REPL 105 |
106 | 113 | 120 | 127 | 128 |
131 | 132 |
133 |
134 | 135 |

Use this component sparingly. Too much animation can be disorienting or distracting for some users.

136 | 137 |
138 | 139 | -------------------------------------------------------------------------------- /src/components/sections/PageStructure.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |

Page Structure

12 | 13 | 14 | 15 | 16 |

A component to create sections of content that can be hidden (visually and to keyboards/screen readers) and made visible again. This component uses unnamed slots, so any content can be wrapped within it and hidden and made visible.

17 | on Svelte REPL 18 |
19 | 20 |
21 | Look at all this fun content 22 |
23 |
24 | 25 | 26 |
27 | Look at all this fun content 28 |
29 |
30 |
31 | 32 |

This component is keyboard and screen-reader accessible by default. This component uses a button element for navigation, so they can each be individually accessed via TAB, and selected via SPACE or ENTER. By default, this button is wrapped in an h3 element. If that isn't the next heading level for your page, it'll need to be manually changed in the component itself.

33 | 34 |
-------------------------------------------------------------------------------- /src/components/sections/PaginationUtil.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 |

Pagination

27 | 28 | 29 | 30 | 31 | 32 |

A component to create a series of circular buttons. These are, by default, presented as a way to navigate through a stepper or paginated content. Ideal for processes with a few steps where steps can be skipped.

33 | on Svelte REPL 34 |
35 |
36 | 37 |

Step {dotValue + 1} is selected

38 |
39 | 40 |
41 |

This component exposes the variable activeIndex to the parent component. A string containing the selected button's index can be accessed in the parent component using bind:activeIndex

42 | 43 |

This component is keyboard and screen-reader accessible by default. This component uses individual button elements for navigation, so they can each be individually accessed via TAB, and selected via SPACE or ENTER.

44 | 45 |
46 | 47 | 48 | 49 | 50 |

A component to automaically split and paginate data or content. This can be used to paginate any sort of output, including data tables.

51 |

⚠️ This component also requires the Icon.svelte component (not automatically copied from the above buttons).

52 | on Svelte REPL 53 |
54 | {#if paginationValues} 55 | {#each paginationValues as value} 56 |

57 | {value} 58 |

59 | {/each} 60 | {/if} 61 | 62 |
63 | 64 |

This component exposes the variable trimmedRows to the parent component. An array containing the trimmed data to be displayed on the selected "page" can be accessed in the parent component using bind:trimmedRows.

65 | 66 |

This component is keyboard and screen-reader accessible by default. This component uses individual button elements for navigation, so they can each be individually accessed via TAB, and selected via SPACE or ENTER.

67 | 68 |
69 | 70 | -------------------------------------------------------------------------------- /src/components/sections/UI.svelte: -------------------------------------------------------------------------------- 1 | 112 | 113 | 114 |

UI Elements

115 | 116 | 117 | 118 | 119 |

A component to create a single checkbox or group of checkboxes. These should be used when a user can select more than one item out of a group of options or to turn a single feature on or off.

120 | on Svelte REPL 121 |
122 |
123 | 124 |

{checkValue} is selected

125 |
126 | 127 |
128 |

This component exposes the variable value to the parent component. An array of selected check values can be accessed in the parent component using bind:value

129 | 130 |

This component is keyboard and screen-reader accessible by default. TAB to select element, SPACE to toggle checkbox.

131 | 132 |
133 | 134 | 135 | 136 | 137 | 138 |

A component to create a group of radio buttons. These should be used when a user can select one (and only one) item out of a group of options.

139 | on Svelte REPL 140 |
141 |
142 | 143 |

{radioValue} is selected

144 |
145 | 146 |
147 |

This component exposes the variable userSelected to the parent component. An array of selected radio values can be accessed in the parent component using bind:userSelected

148 | 149 |

This component is keyboard and screen-reader accessible by default. TAB to select element, Arrow Keys to toggle between radio buttons.

150 | 151 |
152 | 153 | 154 | 155 | 156 |

A component to create a single toggle switch (with 3 style options). The Inner or Slider design options should be used when a user can toggle an option between an on or off (or any other binary) state. The Multi design option should be used when the user is toggling between two possible options that are not binary.

157 | on Svelte REPL 158 |
159 |

Inner style option

160 |
161 | 162 |

Switch is {switchValue}

163 |
164 |

Slider style option

165 |
166 | 167 |

Switch is {sliderValue}

168 |
169 |

Multi style option

170 |
171 | 172 |

Switch is {multiValue}

173 |
174 | 175 |
176 |

This component exposes the variable userSelected to the parent component. An array of selected radio values can be accessed in the parent component using bind:userSelected

177 | 178 |

These components are keyboard and screen-reader accessible by default. On the back-end, the Inner and Slider designs are switch elements. You can TAB to the element and press either SPACE or ENTER to toggle. The Multi design, is a series of radio buttons, and thus can be accessed via TAB and toggled with Arrow keys.

179 | 180 |
181 | 182 | 183 | 184 | 185 | 186 |

A component to create a group of UI elements designed to look like a set of buttons. These should be used when a user can select one (and only one) item out of a group of options.

187 | on Svelte REPL 188 |
189 |
190 | 191 |

{setValue} is selected

192 |
193 | 194 |
195 |

This component exposes the variable userSelected to the parent component. A string with the selected button value can be accessed in the parent component using bind:userSelected

196 | 197 |

This component is keyboard and screen-reader accessible by default. On the back end, this is using a group of radio buttons. TAB to select element, Arrow Keys to toggle between radio buttons.

198 | 199 |
200 | 201 | 202 | 203 |

A component to add and resize icons to other elements. All possible icons can be viewed on the Feather Icons Site.

204 |

This component requires that you have incorporated Feather Icons in your project.

205 | 206 |

This component is included by default in our Pudding Svelte Starter Template. You can find it in src/components/helpers/Icon.svelte.

207 | 208 |
209 |
210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 |
218 | 219 |
220 | 221 |
222 | 223 | 224 | 225 |

A component to add an autocomplete search bar. These can be helpful when there are many options in a select menu, and you want to keep your users from needing to scroll endlessly.

226 |

⚠️ This component also requires the Icon.svelte component (not automatically copied from the above buttons).

227 | 228 |
229 | 230 | 231 | {#if acSelected.length} 232 |

{acSelected} is selected

233 | {/if} 234 |
235 | 236 |

This component exposes the variable selected to the parent component. A string with the selected button value can be accessed in the parent component using bind:selected

237 | 238 |

This component is keyboard and screen-reader accessible by default. Users can TAB to select the input element, use their Arrow Keys to move up and down the rendered list of options, and Enter to select one. Pressing the Escape key closes the menu.

239 | 240 |
241 | 242 | 265 | -------------------------------------------------------------------------------- /src/components/templates-code.js: -------------------------------------------------------------------------------- 1 | import BriefMessage from '!!raw-loader!./../../templates/BriefMessage.svelte'; 2 | import Confetti from '!!raw-loader!./../../templates/Confetti.svelte'; 3 | import Icon from '!!raw-loader!./../../templates/Icon.svelte'; 4 | import InView from '!!raw-loader!./../../templates/InView.svelte'; 5 | import Number from '!!raw-loader!./../../templates/Number.svelte'; 6 | import ProgressDots from '!!raw-loader!./../../templates/ProgressDots.svelte'; 7 | import Scatterplot from '!!raw-loader!./../../templates/Scatterplot.svelte'; 8 | import ButtonSet from '!!raw-loader!./../../templates/ButtonSet.svelte'; 9 | import Checkbox from '!!raw-loader!./../../templates/Checkbox.svelte'; 10 | import Radio from '!!raw-loader!./../../templates/Radio.svelte'; 11 | import Switch from '!!raw-loader!./../../templates/Switch.svelte'; 12 | import CollapsibleSection from '!!raw-loader!./../../templates/CollapsibleSection.svelte'; 13 | import Autocomplete from '!!raw-loader!./../../templates/Autocomplete.svelte'; 14 | import Pagination from '!!raw-loader!./../../templates/Pagination.svelte'; 15 | import Table from '!!raw-loader!./../../templates/Table.svelte'; 16 | import CodeBlock from '!!raw-loader!./../../templates/CodeBlock.svelte'; 17 | import DataDownload from '!!raw-loader!./../../templates/DataDownload.svelte'; 18 | 19 | export default { 20 | BriefMessage, 21 | Confetti, 22 | Icon, 23 | InView, 24 | Number, 25 | ProgressDots, 26 | Scatterplot, 27 | ButtonSet, 28 | Checkbox, 29 | Radio, 30 | Switch, 31 | CollapsibleSection, 32 | Autocomplete, 33 | Pagination, 34 | Table, 35 | CodeBlock, 36 | DataDownload 37 | }; 38 | -------------------------------------------------------------------------------- /src/examples/AutocompleteExample.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 |

30 | {#if selected.length} 31 | {selected} was selected 32 | {/if} 33 |

-------------------------------------------------------------------------------- /src/examples/BriefMessageExample.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /src/examples/CheckboxExample.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 |

24 | Selected are: {value}; 25 |

-------------------------------------------------------------------------------- /src/examples/CodeExample.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/examples/CollapsibleSectionExample.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | Look at all this fun content 9 |
10 |
11 | 12 | 13 |
14 | Look at all this fun content 15 |
16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /src/examples/ConfettiExample.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
-------------------------------------------------------------------------------- /src/examples/DataDownloadExample.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | -------------------------------------------------------------------------------- /src/examples/IconExample.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /src/examples/NumberExample.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/examples/PaginationExample.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if values} 10 | {#each values as value} 11 |

12 | {value} 13 |

14 | {/each} 15 | {/if} 16 | 17 | -------------------------------------------------------------------------------- /src/examples/ProgressDotExample.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 |

10 | Step {dotValue + 1} is selected. 11 |

-------------------------------------------------------------------------------- /src/examples/RadioExample.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |

20 | {radioValue} is selected 21 |

-------------------------------------------------------------------------------- /src/examples/ScatterplotExample.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /src/examples/SetExample.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |

20 | {radioValue} is selected 21 |

-------------------------------------------------------------------------------- /src/examples/SwitchExample.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 |

Switch is {switchValue}

11 | 12 | 13 |

Switch is {sliderValue}

14 | 15 | 16 |

Switch is {multiValue}

17 | -------------------------------------------------------------------------------- /src/examples/TableExample.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 |
-------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: { 6 | name: 'world' 7 | } 8 | }); 9 | 10 | export default app; -------------------------------------------------------------------------------- /src/node_modules/@sapper/app.mjs: -------------------------------------------------------------------------------- 1 | import { getContext } from 'svelte'; 2 | import { CONTEXT_KEY } from './internal/shared'; 3 | import { writable } from 'svelte/store'; 4 | import App from './internal/App.svelte'; 5 | import { ignore, routes, root_preload, components, ErrorComponent } from './internal/manifest-client'; 6 | 7 | function goto(href, opts = { replaceState: false }) { 8 | const target = select_target(new URL(href, document.baseURI)); 9 | 10 | if (target) { 11 | _history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href); 12 | return navigate(target, null).then(() => {}); 13 | } 14 | 15 | location.href = href; 16 | return new Promise(f => {}); // never resolves 17 | } 18 | 19 | /** Callback to inform of a value updates. */ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | function page_store(value) { 40 | const store = writable(value); 41 | let ready = true; 42 | 43 | function notify() { 44 | ready = true; 45 | store.update(val => val); 46 | } 47 | 48 | function set(new_value) { 49 | ready = false; 50 | store.set(new_value); 51 | } 52 | 53 | function subscribe(run) { 54 | let old_value; 55 | return store.subscribe((value) => { 56 | if (old_value === undefined || (ready && value !== old_value)) { 57 | run(old_value = value); 58 | } 59 | }); 60 | } 61 | 62 | return { notify, set, subscribe }; 63 | } 64 | 65 | const initial_data = typeof __SAPPER__ !== 'undefined' && __SAPPER__; 66 | 67 | let ready = false; 68 | let root_component; 69 | let current_token; 70 | let root_preloaded; 71 | let current_branch = []; 72 | let current_query = '{}'; 73 | 74 | const stores = { 75 | page: page_store({}), 76 | preloading: writable(null), 77 | session: writable(initial_data && initial_data.session) 78 | }; 79 | 80 | let $session; 81 | let session_dirty; 82 | 83 | stores.session.subscribe(async value => { 84 | $session = value; 85 | 86 | if (!ready) return; 87 | session_dirty = true; 88 | 89 | const target = select_target(new URL(location.href)); 90 | 91 | const token = current_token = {}; 92 | const { redirect, props, branch } = await hydrate_target(target); 93 | if (token !== current_token) return; // a secondary navigation happened while we were loading 94 | 95 | await render(redirect, branch, props, target.page); 96 | }); 97 | 98 | let prefetching 99 | 100 | 101 | = null; 102 | function set_prefetching(href, promise) { 103 | prefetching = { href, promise }; 104 | } 105 | 106 | let target; 107 | function set_target(element) { 108 | target = element; 109 | } 110 | 111 | let uid = 1; 112 | function set_uid(n) { 113 | uid = n; 114 | } 115 | 116 | let cid; 117 | function set_cid(n) { 118 | cid = n; 119 | } 120 | 121 | const _history = typeof history !== 'undefined' ? history : { 122 | pushState: (state, title, href) => {}, 123 | replaceState: (state, title, href) => {}, 124 | scrollRestoration: '' 125 | }; 126 | 127 | const scroll_history = {}; 128 | 129 | function extract_query(search) { 130 | const query = Object.create(null); 131 | if (search.length > 0) { 132 | search.slice(1).split('&').forEach(searchParam => { 133 | let [, key, value = ''] = /([^=]*)(?:=(.*))?/.exec(decodeURIComponent(searchParam.replace(/\+/g, ' '))); 134 | if (typeof query[key] === 'string') query[key] = [query[key]]; 135 | if (typeof query[key] === 'object') (query[key] ).push(value); 136 | else query[key] = value; 137 | }); 138 | } 139 | return query; 140 | } 141 | 142 | function select_target(url) { 143 | if (url.origin !== location.origin) return null; 144 | if (!url.pathname.startsWith(initial_data.baseUrl)) return null; 145 | 146 | let path = url.pathname.slice(initial_data.baseUrl.length); 147 | 148 | if (path === '') { 149 | path = '/'; 150 | } 151 | 152 | // avoid accidental clashes between server routes and page routes 153 | if (ignore.some(pattern => pattern.test(path))) return; 154 | 155 | for (let i = 0; i < routes.length; i += 1) { 156 | const route = routes[i]; 157 | 158 | const match = route.pattern.exec(path); 159 | 160 | if (match) { 161 | const query = extract_query(url.search); 162 | const part = route.parts[route.parts.length - 1]; 163 | const params = part.params ? part.params(match) : {}; 164 | 165 | const page = { host: location.host, path, query, params }; 166 | 167 | return { href: url.href, route, match, page }; 168 | } 169 | } 170 | } 171 | 172 | function handle_error(url) { 173 | const { host, pathname, search } = location; 174 | const { session, preloaded, status, error } = initial_data; 175 | 176 | if (!root_preloaded) { 177 | root_preloaded = preloaded && preloaded[0]; 178 | } 179 | 180 | const props = { 181 | error, 182 | status, 183 | session, 184 | level0: { 185 | props: root_preloaded 186 | }, 187 | level1: { 188 | props: { 189 | status, 190 | error 191 | }, 192 | component: ErrorComponent 193 | }, 194 | segments: preloaded 195 | 196 | }; 197 | const query = extract_query(search); 198 | render(null, [], props, { host, path: pathname, query, params: {} }); 199 | } 200 | 201 | function scroll_state() { 202 | return { 203 | x: pageXOffset, 204 | y: pageYOffset 205 | }; 206 | } 207 | 208 | async function navigate(target, id, noscroll, hash) { 209 | if (id) { 210 | // popstate or initial navigation 211 | cid = id; 212 | } else { 213 | const current_scroll = scroll_state(); 214 | 215 | // clicked on a link. preserve scroll state 216 | scroll_history[cid] = current_scroll; 217 | 218 | id = cid = ++uid; 219 | scroll_history[cid] = noscroll ? current_scroll : { x: 0, y: 0 }; 220 | } 221 | 222 | cid = id; 223 | 224 | if (root_component) stores.preloading.set(true); 225 | 226 | const loaded = prefetching && prefetching.href === target.href ? 227 | prefetching.promise : 228 | hydrate_target(target); 229 | 230 | prefetching = null; 231 | 232 | const token = current_token = {}; 233 | const { redirect, props, branch } = await loaded; 234 | if (token !== current_token) return; // a secondary navigation happened while we were loading 235 | 236 | await render(redirect, branch, props, target.page); 237 | if (document.activeElement) document.activeElement.blur(); 238 | 239 | if (!noscroll) { 240 | let scroll = scroll_history[id]; 241 | 242 | if (hash) { 243 | // scroll is an element id (from a hash), we need to compute y. 244 | const deep_linked = document.getElementById(hash.slice(1)); 245 | 246 | if (deep_linked) { 247 | scroll = { 248 | x: 0, 249 | y: deep_linked.getBoundingClientRect().top + scrollY 250 | }; 251 | } 252 | } 253 | 254 | scroll_history[cid] = scroll; 255 | if (scroll) scrollTo(scroll.x, scroll.y); 256 | } 257 | } 258 | 259 | async function render(redirect, branch, props, page) { 260 | if (redirect) return goto(redirect.location, { replaceState: true }); 261 | 262 | stores.page.set(page); 263 | stores.preloading.set(false); 264 | 265 | if (root_component) { 266 | root_component.$set(props); 267 | } else { 268 | props.stores = { 269 | page: { subscribe: stores.page.subscribe }, 270 | preloading: { subscribe: stores.preloading.subscribe }, 271 | session: stores.session 272 | }; 273 | props.level0 = { 274 | props: await root_preloaded 275 | }; 276 | props.notify = stores.page.notify; 277 | 278 | // first load — remove SSR'd contents 279 | const start = document.querySelector('#sapper-head-start'); 280 | const end = document.querySelector('#sapper-head-end'); 281 | 282 | if (start && end) { 283 | while (start.nextSibling !== end) detach(start.nextSibling); 284 | detach(start); 285 | detach(end); 286 | } 287 | 288 | root_component = new App({ 289 | target, 290 | props, 291 | hydrate: true 292 | }); 293 | } 294 | 295 | current_branch = branch; 296 | current_query = JSON.stringify(page.query); 297 | ready = true; 298 | session_dirty = false; 299 | } 300 | 301 | function part_changed(i, segment, match, stringified_query) { 302 | // TODO only check query string changes for preload functions 303 | // that do in fact depend on it (using static analysis or 304 | // runtime instrumentation) 305 | if (stringified_query !== current_query) return true; 306 | 307 | const previous = current_branch[i]; 308 | 309 | if (!previous) return false; 310 | if (segment !== previous.segment) return true; 311 | if (previous.match) { 312 | if (JSON.stringify(previous.match.slice(1, i + 2)) !== JSON.stringify(match.slice(1, i + 2))) { 313 | return true; 314 | } 315 | } 316 | } 317 | 318 | async function hydrate_target(target) 319 | 320 | 321 | 322 | { 323 | const { route, page } = target; 324 | const segments = page.path.split('/').filter(Boolean); 325 | 326 | let redirect = null; 327 | 328 | const props = { error: null, status: 200, segments: [segments[0]] }; 329 | 330 | const preload_context = { 331 | fetch: (url, opts) => fetch(url, opts), 332 | redirect: (statusCode, location) => { 333 | if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) { 334 | throw new Error(`Conflicting redirects`); 335 | } 336 | redirect = { statusCode, location }; 337 | }, 338 | error: (status, error) => { 339 | props.error = typeof error === 'string' ? new Error(error) : error; 340 | props.status = status; 341 | } 342 | }; 343 | 344 | if (!root_preloaded) { 345 | root_preloaded = initial_data.preloaded[0] || root_preload.call(preload_context, { 346 | host: page.host, 347 | path: page.path, 348 | query: page.query, 349 | params: {} 350 | }, $session); 351 | } 352 | 353 | let branch; 354 | let l = 1; 355 | 356 | try { 357 | const stringified_query = JSON.stringify(page.query); 358 | const match = route.pattern.exec(page.path); 359 | 360 | let segment_dirty = false; 361 | 362 | branch = await Promise.all(route.parts.map(async (part, i) => { 363 | const segment = segments[i]; 364 | 365 | if (part_changed(i, segment, match, stringified_query)) segment_dirty = true; 366 | 367 | props.segments[l] = segments[i + 1]; // TODO make this less confusing 368 | if (!part) return { segment }; 369 | 370 | const j = l++; 371 | 372 | if (!session_dirty && !segment_dirty && current_branch[i] && current_branch[i].part === part.i) { 373 | return current_branch[i]; 374 | } 375 | 376 | segment_dirty = false; 377 | 378 | const { default: component, preload } = await load_component(components[part.i]); 379 | 380 | let preloaded; 381 | if (ready || !initial_data.preloaded[i + 1]) { 382 | preloaded = preload 383 | ? await preload.call(preload_context, { 384 | host: page.host, 385 | path: page.path, 386 | query: page.query, 387 | params: part.params ? part.params(target.match) : {} 388 | }, $session) 389 | : {}; 390 | } else { 391 | preloaded = initial_data.preloaded[i + 1]; 392 | } 393 | 394 | return (props[`level${j}`] = { component, props: preloaded, segment, match, part: part.i }); 395 | })); 396 | } catch (error) { 397 | props.error = error; 398 | props.status = 500; 399 | branch = []; 400 | } 401 | 402 | return { redirect, props, branch }; 403 | } 404 | 405 | function load_css(chunk) { 406 | const href = `client/${chunk}`; 407 | if (document.querySelector(`link[href="${href}"]`)) return; 408 | 409 | return new Promise((fulfil, reject) => { 410 | const link = document.createElement('link'); 411 | link.rel = 'stylesheet'; 412 | link.href = href; 413 | 414 | link.onload = () => fulfil(); 415 | link.onerror = reject; 416 | 417 | document.head.appendChild(link); 418 | }); 419 | } 420 | 421 | function load_component(component) 422 | 423 | 424 | { 425 | // TODO this is temporary — once placeholders are 426 | // always rewritten, scratch the ternary 427 | const promises = (typeof component.css === 'string' ? [] : component.css.map(load_css)); 428 | promises.unshift(component.js()); 429 | return Promise.all(promises).then(values => values[0]); 430 | } 431 | 432 | function detach(node) { 433 | node.parentNode.removeChild(node); 434 | } 435 | 436 | function prefetch(href) { 437 | const target = select_target(new URL(href, document.baseURI)); 438 | 439 | if (target) { 440 | if (!prefetching || href !== prefetching.href) { 441 | set_prefetching(href, hydrate_target(target)); 442 | } 443 | 444 | return prefetching.promise; 445 | } 446 | } 447 | 448 | function start(opts 449 | 450 | ) { 451 | if ('scrollRestoration' in _history) { 452 | _history.scrollRestoration = 'manual'; 453 | } 454 | 455 | // Adopted from Nuxt.js 456 | // Reset scrollRestoration to auto when leaving page, allowing page reload 457 | // and back-navigation from other pages to use the browser to restore the 458 | // scrolling position. 459 | addEventListener('beforeunload', () => { 460 | _history.scrollRestoration = 'auto'; 461 | }); 462 | 463 | // Setting scrollRestoration to manual again when returning to this page. 464 | addEventListener('load', () => { 465 | _history.scrollRestoration = 'manual'; 466 | }); 467 | 468 | set_target(opts.target); 469 | 470 | addEventListener('click', handle_click); 471 | addEventListener('popstate', handle_popstate); 472 | 473 | // prefetch 474 | addEventListener('touchstart', trigger_prefetch); 475 | addEventListener('mousemove', handle_mousemove); 476 | 477 | return Promise.resolve().then(() => { 478 | const { hash, href } = location; 479 | 480 | _history.replaceState({ id: uid }, '', href); 481 | 482 | const url = new URL(location.href); 483 | 484 | if (initial_data.error) return handle_error(); 485 | 486 | const target = select_target(url); 487 | if (target) return navigate(target, uid, true, hash); 488 | }); 489 | } 490 | 491 | let mousemove_timeout; 492 | 493 | function handle_mousemove(event) { 494 | clearTimeout(mousemove_timeout); 495 | mousemove_timeout = setTimeout(() => { 496 | trigger_prefetch(event); 497 | }, 20); 498 | } 499 | 500 | function trigger_prefetch(event) { 501 | const a = find_anchor(event.target); 502 | if (!a || a.rel !== 'prefetch') return; 503 | 504 | prefetch(a.href); 505 | } 506 | 507 | function handle_click(event) { 508 | // Adapted from https://github.com/visionmedia/page.js 509 | // MIT license https://github.com/visionmedia/page.js#license 510 | if (which(event) !== 1) return; 511 | if (event.metaKey || event.ctrlKey || event.shiftKey) return; 512 | if (event.defaultPrevented) return; 513 | 514 | const a = find_anchor(event.target); 515 | if (!a) return; 516 | 517 | if (!a.href) return; 518 | 519 | // check if link is inside an svg 520 | // in this case, both href and target are always inside an object 521 | const svg = typeof a.href === 'object' && a.href.constructor.name === 'SVGAnimatedString'; 522 | const href = String(svg ? (a).href.baseVal : a.href); 523 | 524 | if (href === location.href) { 525 | if (!location.hash) event.preventDefault(); 526 | return; 527 | } 528 | 529 | // Ignore if tag has 530 | // 1. 'download' attribute 531 | // 2. rel='external' attribute 532 | if (a.hasAttribute('download') || a.getAttribute('rel') === 'external') return; 533 | 534 | // Ignore if has a target 535 | if (svg ? (a).target.baseVal : a.target) return; 536 | 537 | const url = new URL(href); 538 | 539 | // Don't handle hash changes 540 | if (url.pathname === location.pathname && url.search === location.search) return; 541 | 542 | const target = select_target(url); 543 | if (target) { 544 | const noscroll = a.hasAttribute('sapper-noscroll'); 545 | navigate(target, null, noscroll, url.hash); 546 | event.preventDefault(); 547 | _history.pushState({ id: cid }, '', url.href); 548 | } 549 | } 550 | 551 | function which(event) { 552 | return event.which === null ? event.button : event.which; 553 | } 554 | 555 | function find_anchor(node) { 556 | while (node && node.nodeName.toUpperCase() !== 'A') node = node.parentNode; // SVG elements have a lowercase name 557 | return node; 558 | } 559 | 560 | function handle_popstate(event) { 561 | scroll_history[cid] = scroll_state(); 562 | 563 | if (event.state) { 564 | const url = new URL(location.href); 565 | const target = select_target(url); 566 | if (target) { 567 | navigate(target, event.state.id); 568 | } else { 569 | location.href = location.href; 570 | } 571 | } else { 572 | // hashchange 573 | set_uid(uid + 1); 574 | set_cid(uid); 575 | _history.replaceState({ id: cid }, '', location.href); 576 | } 577 | } 578 | 579 | function prefetchRoutes(pathnames) { 580 | return routes 581 | .filter(pathnames 582 | ? route => pathnames.some(pathname => route.pattern.test(pathname)) 583 | : () => true 584 | ) 585 | .reduce((promise, route) => promise.then(() => { 586 | return Promise.all(route.parts.map(part => part && load_component(components[part.i]))); 587 | }), Promise.resolve()); 588 | } 589 | 590 | const stores$1 = () => getContext(CONTEXT_KEY); 591 | 592 | export { goto, prefetch, prefetchRoutes, start, stores$1 as stores }; 593 | -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/App.svelte: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | {#if error} 22 | 23 | {:else} 24 | 25 | {/if} 26 | -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/error.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |

{status}

7 | 8 |

{error.message}

9 | 10 | {#if process.env.NODE_ENV === 'development'} 11 |
{error.stack}
12 | {/if} -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/layout.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/manifest-client.mjs: -------------------------------------------------------------------------------- 1 | // This file is generated by Sapper — do not edit it! 2 | export { default as Root } from '../../../routes/_layout.svelte'; 3 | export { preload as root_preload } from './shared'; 4 | export { default as ErrorComponent } from '../../../routes/_error.svelte'; 5 | 6 | export const ignore = []; 7 | 8 | export const components = [ 9 | { 10 | js: () => import(/* webpackChunkName: "index" */ "../../../routes/index.svelte"), 11 | css: "__SAPPER_CSS_PLACEHOLDER:index.svelte__" 12 | } 13 | ]; 14 | 15 | export const routes = [ 16 | { 17 | // index.svelte 18 | pattern: /^\/$/, 19 | parts: [ 20 | { i: 0 } 21 | ] 22 | } 23 | ]; 24 | 25 | if (typeof window !== 'undefined') { 26 | import("/Users/amberthomas/Documents/dataScience/pudding/svelte-templates/node_modules/sapper/sapper-dev-client.js").then(client => { 27 | client.connect(10000); 28 | }); 29 | } -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/manifest-server.mjs: -------------------------------------------------------------------------------- 1 | // This file is generated by Sapper — do not edit it! 2 | import component_0 from "../../../routes/index.svelte"; 3 | import root from "../../../routes/_layout.svelte"; 4 | import error from "../../../routes/_error.svelte"; 5 | 6 | const d = decodeURIComponent; 7 | 8 | export const manifest = { 9 | server_routes: [ 10 | 11 | ], 12 | 13 | pages: [ 14 | { 15 | // index.svelte 16 | pattern: /^\/$/, 17 | parts: [ 18 | { name: "index", file: "index.svelte", component: component_0 } 19 | ] 20 | } 21 | ], 22 | 23 | root, 24 | root_preload: () => {}, 25 | error 26 | }; 27 | 28 | export const build_dir = "__sapper__/dev"; 29 | 30 | export const src_dir = "src"; 31 | 32 | export const dev = true; -------------------------------------------------------------------------------- /src/node_modules/@sapper/internal/shared.mjs: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const CONTEXT_KEY = {}; 4 | 5 | export const preload = () => ({}); -------------------------------------------------------------------------------- /src/node_modules/@sapper/service-worker.js: -------------------------------------------------------------------------------- 1 | // This file is generated by Sapper — do not edit it! 2 | export const timestamp = 1616793435007; 3 | 4 | export const files = [ 5 | "service-worker-index.html", 6 | "favicon.ico", 7 | "favicon.png", 8 | "fonts/.DS_Store", 9 | "fonts/national/National2NarrowWeb-Black.woff", 10 | "fonts/national/National2NarrowWeb-Black.woff2", 11 | "fonts/national/National2NarrowWeb-Bold.woff", 12 | "fonts/national/National2NarrowWeb-Bold.woff2", 13 | "fonts/national/National2NarrowWeb-Extralight.woff", 14 | "fonts/national/National2NarrowWeb-Extralight.woff2", 15 | "fonts/national/National2NarrowWeb-Regular.woff", 16 | "fonts/national/National2NarrowWeb-Regular.woff2", 17 | "fonts/national/National2Web-Bold.woff", 18 | "fonts/national/National2Web-Bold.woff2", 19 | "fonts/national/National2Web-Regular.woff", 20 | "fonts/national/National2Web-Regular.woff2", 21 | "fonts/tiempos/TiemposHeadlineWeb-Regular.woff", 22 | "fonts/tiempos/TiemposHeadlineWeb-Regular.woff2", 23 | "fonts/tiempos/TiemposTextWeb-Bold.woff", 24 | "fonts/tiempos/TiemposTextWeb-Bold.woff2", 25 | "fonts/tiempos/TiemposTextWeb-Regular.woff", 26 | "fonts/tiempos/TiemposTextWeb-Regular.woff2", 27 | "fonts.css", 28 | "global.css", 29 | "logo-192.png", 30 | "logo-512.png", 31 | "manifest.json", 32 | "wordmark.svg" 33 | ]; 34 | export { files as assets }; // legacy 35 | 36 | export const shell = [ 37 | "client/ca8416d816aea76f4c41/0.0.js", 38 | "client/ca8416d816aea76f4c41/index.index.js", 39 | "client/ca8416d816aea76f4c41/main.js", 40 | "client/ca8416d816aea76f4c41/vendors~index.vendors~index.js" 41 | ]; 42 | 43 | export const routes = [ 44 | { pattern: /^\/$/ } 45 | ]; -------------------------------------------------------------------------------- /src/routes/_error.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 | 31 | {status} 32 | 33 | 34 |

{status}

35 | 36 |

{error.message}

37 | 38 | {#if dev && error.stack} 39 |
{error.stack}
40 | {/if} 41 | -------------------------------------------------------------------------------- /src/routes/_layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | 9 | 19 | -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
19 |

Hey friend!

20 |

These templates are yours to grab and modify as you need!

21 |

Just copy the source code & paste into a new .svelte file.

22 |

23 | These can do basic color theming, if you set the css variables in a parent 24 | component: 25 |

26 | 27 | 28 |

29 | If you don't define an --accent-color and --gray, the accent defaults to #282828 and gray defaults to #ccc. 30 |

31 | 32 |

33 | Whenever possible, component code is available both with opinionated styling (e.g., animations, rounded corners etc.) and without it. Choose whichever better suits your purpose! 34 |

35 |
36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | 70 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import sirv from 'sirv'; 2 | import polka from 'polka'; 3 | import compression from 'compression'; 4 | import * as sapper from '@sapper/server'; 5 | 6 | const { PORT, NODE_ENV } = process.env; 7 | const dev = NODE_ENV === 'development'; 8 | 9 | polka() // You can also use Express 10 | .use( 11 | compression({ threshold: 0 }), 12 | sirv('static', { dev }), 13 | sapper.middleware() 14 | ) 15 | .listen(PORT, err => { 16 | if (err) console.log('error', err); 17 | }); 18 | -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | import { timestamp, files, shell, routes } from '@sapper/service-worker'; 2 | 3 | const ASSETS = `cache${timestamp}`; 4 | 5 | // `shell` is an array of all the files generated by the bundler, 6 | // `files` is an array of everything in the `static` directory 7 | const to_cache = shell.concat(files); 8 | const cached = new Set(to_cache); 9 | 10 | self.addEventListener('install', event => { 11 | event.waitUntil( 12 | caches 13 | .open(ASSETS) 14 | .then(cache => cache.addAll(to_cache)) 15 | .then(() => { 16 | self.skipWaiting(); 17 | }) 18 | ); 19 | }); 20 | 21 | self.addEventListener('activate', event => { 22 | event.waitUntil( 23 | caches.keys().then(async keys => { 24 | // delete old caches 25 | for (const key of keys) { 26 | if (key !== ASSETS) await caches.delete(key); 27 | } 28 | 29 | self.clients.claim(); 30 | }) 31 | ); 32 | }); 33 | 34 | self.addEventListener('fetch', event => { 35 | if (event.request.method !== 'GET' || event.request.headers.has('range')) return; 36 | 37 | const url = new URL(event.request.url); 38 | 39 | // don't try to handle e.g. data: URIs 40 | if (!url.protocol.startsWith('http')) return; 41 | 42 | // ignore dev server requests 43 | if (url.hostname === self.location.hostname && url.port !== self.location.port) return; 44 | 45 | // always serve static files and bundler-generated assets from cache 46 | if (url.host === self.location.host && cached.has(url.pathname)) { 47 | event.respondWith(caches.match(event.request)); 48 | return; 49 | } 50 | 51 | // for pages, you might want to serve a shell `service-worker-index.html` file, 52 | // which Sapper has generated for you. It's not right for every 53 | // app, but if it's right for yours then uncomment this section 54 | /* 55 | if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) { 56 | event.respondWith(caches.match('/service-worker-index.html')); 57 | return; 58 | } 59 | */ 60 | 61 | if (event.request.cache === 'only-if-cached') return; 62 | 63 | // for everything else, try the network first, falling back to 64 | // cache if the user is offline. (If the pages never change, you 65 | // might prefer a cache-first approach to a network-first one.) 66 | event.respondWith( 67 | caches 68 | .open(`offline${timestamp}`) 69 | .then(async cache => { 70 | try { 71 | const response = await fetch(event.request); 72 | cache.put(event.request, response.clone()); 73 | return response; 74 | } catch(err) { 75 | const response = await cache.match(event.request); 76 | if (response) return response; 77 | 78 | throw err; 79 | } 80 | }) 81 | ); 82 | }); 83 | -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Svelte Templates 9 | 10 | %sapper.base% 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | %sapper.styles% 21 | 22 | 24 | %sapper.head% 25 | 26 | 27 | 29 |
%sapper.html%
30 | 31 | 34 | %sapper.scripts% 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/utils/highlight-svelte.js: -------------------------------------------------------------------------------- 1 | /* 2 | tweaked from Alexey Schebelev's script 3 | https://github.com/AlexxNB/highlightjs-svelte/blob/master/src/svelte.js 4 | */ 5 | 6 | const IDENT_RE = "[A-Za-z$_][0-9A-Za-z$_]*"; 7 | 8 | function hljsDefineSvelte(hljs) { 9 | return { 10 | subLanguage: "xml", 11 | contains: [ 12 | hljs.COMMENT("", { 13 | relevance: 10, 14 | }), 15 | { 16 | begin: /^(\s*)()/gm, 17 | end: /^(\s*)(<\/script>)/gm, 18 | subLanguage: "javascript", 19 | excludeBegin: true, 20 | excludeEnd: true, 21 | contains: [ 22 | { 23 | begin: /^(\s*)(\$:)/m, 24 | end: /\s/, 25 | excludeEnd: true, 26 | className: "keyword", 27 | relevance: 0, 28 | }, 29 | { 30 | begin: /^(\s*)(export)*(\s)(let|const|var|function|\$:)(\s)/gm, 31 | end: /\s/, 32 | className: "variable", 33 | excludeBegin: true, 34 | excludeEnd: true, 35 | relevance: 10, 36 | }, 37 | // { 38 | // begin: /(\s)(\$)(\w)/gm, 39 | // end: /\s/gm, 40 | // className: "variable", 41 | // // excludeBegin: true, 42 | // // excludeEnd: true, 43 | // relevance: 10, 44 | // }, 45 | { 46 | begin: /(scaleLinear|scaleSqrt|colorScale|extent)/gm, 47 | // end: /\s/, 48 | className: "d3", 49 | excludeEnd: true, 50 | relevance: 10, 51 | }, 52 | { 53 | begin: /(let|const)(\s)+.+(\s\=\s)(\()*/gm, 54 | end: /(\s\=\>)/, 55 | className: "d3", 56 | // excludeBegin: true, 57 | // excludeEnd: true, 58 | // relevance: 10, 59 | }, 60 | { 61 | begin: /(d3\-)/gm, 62 | end: /\"/, 63 | className: "d3", 64 | excludeEnd: true, 65 | // relevance: 10, 66 | }, 67 | { 68 | begin: /^(\s*)(\.)(domain|range|interpolate)/gm, 69 | end: /\(/, 70 | className: "d3", 71 | // excludeBegin: true, 72 | excludeEnd: true, 73 | relevance: 10, 74 | }, 75 | { 76 | begin: /^(\s*)(import)(\s)(\{*)/gm, 77 | end: /(\})*(\s)(from)/, 78 | className: "variable", 79 | excludeBegin: true, 80 | excludeEnd: true, 81 | }, 82 | ], 83 | }, 84 | { 85 | begin: /^(\s*)()/gm, 86 | end: /^(\s*)(<\/style>)/gm, 87 | subLanguage: "css", 88 | excludeBegin: true, 89 | excludeEnd: true, 90 | }, 91 | { 92 | begin: /\{/gm, 93 | end: /\}/gm, 94 | subLanguage: "javascript", 95 | contains: [ 96 | { 97 | begin: /[\{]/, 98 | end: /[\}]/, 99 | skip: true, 100 | }, 101 | { 102 | begin: /([#:\/@])(if|else|each|await|then|catch|debug|html)/gm, 103 | className: "keyword", 104 | relevance: 10, 105 | }, 106 | ], 107 | }, 108 | ], 109 | }; 110 | } 111 | 112 | export default hljsDefineSvelte; 113 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/favicon.ico -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/favicon.png -------------------------------------------------------------------------------- /static/fonts.css: -------------------------------------------------------------------------------- 1 | /* 2 | This font software is the property of Commercial Type. 3 | 4 | You may not modify the font software, use it on another website, or install it on a computer. 5 | 6 | License information is available at http://commercialtype.com/eula 7 | For more information please visit Commercial Type at http://commercialtype.com or email us at info[at]commercialtype.com 8 | 9 | Copyright (C) 2017 Schwartzco Inc. 10 | License: 1704-GNCLGK 11 | */ 12 | 13 | /* 14 | 15 | 16 | /* 17 | ******************** 18 | National 19 | ******************** 20 | */ 21 | 22 | @font-face { 23 | font-family: "National 2 Web"; 24 | src: url("./fonts/national/National2NarrowWeb-Regular.woff") 25 | format("woff2"), 26 | url("./fonts/national/National2Web-Regular.woff") format("woff"); 27 | font-weight: 500; 28 | font-style: normal; 29 | font-stretch: normal; 30 | font-display: swap; 31 | } 32 | 33 | @font-face { 34 | font-family: "National 2 Web"; 35 | src: url("./fonts/national/National2Web-Bold.woff2") format("woff2"), 36 | url("./fonts/national/National2Web-Bold.woff") format("woff"); 37 | font-weight: 700; 38 | font-style: normal; 39 | font-stretch: normal; 40 | font-display: swap; 41 | } 42 | 43 | /* 44 | ******************** 45 | National Narrow 46 | ******************** 47 | */ 48 | 49 | @font-face { 50 | font-family: "National 2 Narrow Web"; 51 | src: url("./fonts/national/National2NarrowWeb-Extralight.woff2") 52 | format("woff2"), 53 | url("./fonts/national/National2NarrowWeb-Extralight.woff") 54 | format("woff"); 55 | font-weight: 200; 56 | font-style: normal; 57 | font-stretch: normal; 58 | font-display: swap; 59 | } 60 | 61 | @font-face { 62 | font-family: "National 2 Narrow Web"; 63 | src: url("./fonts/national/National2NarrowWeb-Regular.woff2") 64 | format("woff2"), 65 | url("./fonts/national/National2NarrowWeb-Regular.woff") 66 | format("woff"); 67 | font-weight: 500; 68 | font-style: normal; 69 | font-stretch: normal; 70 | font-display: swap; 71 | } 72 | 73 | @font-face { 74 | font-family: "National 2 Narrow Web"; 75 | src: url("./fonts/national/National2NarrowWeb-Bold.woff2") 76 | format("woff2"), 77 | url("./fonts/national/National2NarrowWeb-Bold.woff") format("woff"); 78 | font-weight: 700; 79 | font-style: normal; 80 | font-stretch: normal; 81 | font-display: swap; 82 | } 83 | 84 | @font-face { 85 | font-family: "National 2 Narrow Web"; 86 | src: url("./fonts/national/National2NarrowWeb-Black.woff2") 87 | format("woff2"), 88 | url("./fonts/national/National2NarrowWeb-Black.woff") 89 | format("woff"); 90 | font-weight: 900; 91 | font-style: normal; 92 | font-stretch: normal; 93 | font-display: swap; 94 | } 95 | 96 | /* 97 | ******************** 98 | Tiempos Text 99 | ******************** 100 | */ 101 | 102 | @font-face { 103 | font-family: "Tiempos Text Web"; 104 | src: url("./fonts/tiempos/TiemposTextWeb-Regular.woff2") 105 | format("woff2"), 106 | url("./fonts/tiempos/TiemposTextWeb-Regular.woff") format("woff"); 107 | font-weight: 500; 108 | font-style: normal; 109 | font-stretch: normal; 110 | font-display: swap; 111 | } 112 | 113 | @font-face { 114 | font-family: "Tiempos Text Web"; 115 | src: url("./fonts/tiempos/TiemposTextWeb-Bold.woff2") format("woff2"), 116 | url("./fonts/tiempos/TiemposTextWeb-Bold.woff") format("woff"); 117 | font-weight: 700; 118 | font-style: normal; 119 | font-stretch: normal; 120 | font-display: swap; 121 | } 122 | 123 | /* 124 | ******************** 125 | Tiempos Headline 126 | ******************** 127 | */ 128 | 129 | @font-face { 130 | font-family: "Tiempos Headline Web"; 131 | src: url("./fonts/tiempos/TiemposHeadlineWeb-Regular.woff2") 132 | format("woff2"), 133 | url("./fonts/tiempos/TiemposHeadlineWeb-Regular.woff") 134 | format("woff"); 135 | font-weight: 500; 136 | font-style: normal; 137 | font-stretch: normal; 138 | font-display: swap; 139 | } 140 | -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Black.woff -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Black.woff2 -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Bold.woff -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Bold.woff2 -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Extralight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Extralight.woff -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Extralight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Extralight.woff2 -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Regular.woff -------------------------------------------------------------------------------- /static/fonts/national/National2NarrowWeb-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2NarrowWeb-Regular.woff2 -------------------------------------------------------------------------------- /static/fonts/national/National2Web-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2Web-Bold.woff -------------------------------------------------------------------------------- /static/fonts/national/National2Web-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2Web-Bold.woff2 -------------------------------------------------------------------------------- /static/fonts/national/National2Web-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2Web-Regular.woff -------------------------------------------------------------------------------- /static/fonts/national/National2Web-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/national/National2Web-Regular.woff2 -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposHeadlineWeb-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposHeadlineWeb-Regular.woff -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposHeadlineWeb-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposHeadlineWeb-Regular.woff2 -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposTextWeb-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposTextWeb-Bold.woff -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposTextWeb-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposTextWeb-Bold.woff2 -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposTextWeb-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposTextWeb-Regular.woff -------------------------------------------------------------------------------- /static/fonts/tiempos/TiemposTextWeb-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/fonts/tiempos/TiemposTextWeb-Regular.woff2 -------------------------------------------------------------------------------- /static/global.css: -------------------------------------------------------------------------------- 1 | /* 2 | ******************** 3 | National 4 | ******************** 5 | */ 6 | 7 | @font-face { 8 | font-family: "National 2 Web"; 9 | src: url("./fonts/national/National2NarrowWeb-Regular.woff") 10 | format("woff2"), 11 | url("./fonts/national/National2Web-Regular.woff") format("woff"); 12 | font-weight: 500; 13 | font-style: normal; 14 | font-stretch: normal; 15 | font-display: swap; 16 | } 17 | 18 | @font-face { 19 | font-family: "National 2 Web"; 20 | src: url("./fonts/national/National2Web-Bold.woff2") format("woff2"), 21 | url("./fonts/national/National2Web-Bold.woff") format("woff"); 22 | font-weight: 700; 23 | font-style: normal; 24 | font-stretch: normal; 25 | font-display: swap; 26 | } 27 | 28 | /* 29 | ******************** 30 | National Narrow 31 | ******************** 32 | */ 33 | 34 | @font-face { 35 | font-family: "National 2 Narrow Web"; 36 | src: url("./fonts/national/National2NarrowWeb-Extralight.woff2") 37 | format("woff2"), 38 | url("./fonts/national/National2NarrowWeb-Extralight.woff") 39 | format("woff"); 40 | font-weight: 200; 41 | font-style: normal; 42 | font-stretch: normal; 43 | font-display: swap; 44 | } 45 | 46 | @font-face { 47 | font-family: "National 2 Narrow Web"; 48 | src: url("./fonts/national/National2NarrowWeb-Regular.woff2") 49 | format("woff2"), 50 | url("./fonts/national/National2NarrowWeb-Regular.woff") 51 | format("woff"); 52 | font-weight: 500; 53 | font-style: normal; 54 | font-stretch: normal; 55 | font-display: swap; 56 | } 57 | 58 | @font-face { 59 | font-family: "National 2 Narrow Web"; 60 | src: url("/fonts/national/National2NarrowWeb-Bold.woff2") 61 | format("woff2"), 62 | url("/fonts/national/National2NarrowWeb-Bold.woff") format("woff"); 63 | font-weight: 700; 64 | font-style: normal; 65 | font-stretch: normal; 66 | font-display: swap; 67 | } 68 | 69 | @font-face { 70 | font-family: "National 2 Narrow Web"; 71 | src: url("/fonts/national/National2NarrowWeb-Black.woff2") 72 | format("woff2"), 73 | url("/fonts/national/National2NarrowWeb-Black.woff") 74 | format("woff"); 75 | font-weight: 900; 76 | font-style: normal; 77 | font-stretch: normal; 78 | font-display: swap; 79 | } 80 | 81 | /* 82 | ******************** 83 | Tiempos Text 84 | ******************** 85 | */ 86 | 87 | @font-face { 88 | font-family: "Tiempos Text Web"; 89 | src: url("/fonts/tiempos/TiemposTextWeb-Regular.woff2") 90 | format("woff2"), 91 | url("/fonts/tiempos/TiemposTextWeb-Regular.woff") format("woff"); 92 | font-weight: 500; 93 | font-style: normal; 94 | font-stretch: normal; 95 | font-display: swap; 96 | } 97 | 98 | @font-face { 99 | font-family: "Tiempos Text Web"; 100 | src: url("/fonts/tiempos/TiemposTextWeb-Bold.woff2") format("woff2"), 101 | url("/fonts/tiempos/TiemposTextWeb-Bold.woff") format("woff"); 102 | font-weight: 700; 103 | font-style: normal; 104 | font-stretch: normal; 105 | font-display: swap; 106 | } 107 | 108 | /* 109 | ******************** 110 | Tiempos Headline 111 | ******************** 112 | */ 113 | 114 | @font-face { 115 | font-family: "Tiempos Headline Web"; 116 | src: url("/fonts/tiempos/TiemposHeadlineWeb-Regular.woff2") 117 | format("woff2"), 118 | url("/fonts/tiempos/TiemposHeadlineWeb-Regular.woff") 119 | format("woff"); 120 | font-weight: 500; 121 | font-style: normal; 122 | font-stretch: normal; 123 | font-display: swap; 124 | } 125 | 126 | 127 | 128 | html, body { 129 | position: relative; 130 | width: 100%; 131 | height: 100%; 132 | } 133 | 134 | body { 135 | color: #3E3454; 136 | margin: 0; 137 | padding: 8px; 138 | box-sizing: border-box; 139 | font-family: 'National 2 Web', BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 140 | } 141 | 142 | a { 143 | color: rgb(0,100,200); 144 | text-decoration: none; 145 | } 146 | 147 | a:hover { 148 | text-decoration: underline; 149 | } 150 | 151 | a:visited { 152 | color: rgb(0,80,160); 153 | } 154 | 155 | label { 156 | display: block; 157 | } 158 | 159 | input, button, select, textarea { 160 | font-family: inherit; 161 | font-size: inherit; 162 | -webkit-padding: 0.4em 0; 163 | padding: 0.4em 0.8em; 164 | margin: 0 0 0.5em 0; 165 | box-sizing: border-box; 166 | border: 1px solid #ccc; 167 | border-radius: 2px; 168 | cursor: pointer; 169 | } 170 | 171 | input:disabled { 172 | color: #ccc; 173 | } 174 | 175 | button { 176 | color: #333; 177 | background-color: #f4f4f4; 178 | outline: none; 179 | } 180 | 181 | button:disabled { 182 | color: #999; 183 | } 184 | 185 | button:not(:disabled):active { 186 | background-color: #ddd; 187 | } 188 | 189 | button:focus { 190 | border-color: #666; 191 | } 192 | 193 | h2 { 194 | font-family: 'Tiempos Text'; 195 | font-size: 2.6em; 196 | font-weight: bold; 197 | margin-bottom: 0; 198 | } 199 | -------------------------------------------------------------------------------- /static/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/logo-192.png -------------------------------------------------------------------------------- /static/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-pudding/svelte-templates/2b5c0ebecdeecdc1c33d970c7d677fe6c748c395/static/logo-512.png -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#ffffff", 3 | "theme_color": "#333333", 4 | "name": "TODO", 5 | "short_name": "TODO", 6 | "display": "minimal-ui", 7 | "start_url": "/", 8 | "icons": [ 9 | { 10 | "src": "logo-192.png", 11 | "sizes": "192x192", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "logo-512.png", 16 | "sizes": "512x512", 17 | "type": "image/png" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /static/wordmark.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 14 | 16 | 19 | 21 | 22 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /templates/Autocomplete.svelte: -------------------------------------------------------------------------------- 1 | 132 | 133 | 134 | 135 |
136 | 139 | 140 | 149 | 150 |
151 | 165 | 166 | 167 |
    170 | {#each filteredData as {label, value}, i} 171 |
  • {handleOptionSelect(value, label)}} 178 | on:keydown={(event) => handleOptionKeydown(event, value, label)} 179 | >{label}
  • 180 | {/each} 181 |
182 | 183 |
186 | {resultCount} results available 187 |
188 | {#if clearButton === true} 189 | 192 | {/if} 193 |
194 |
195 | 196 | -------------------------------------------------------------------------------- /templates/BriefMessage.svelte: -------------------------------------------------------------------------------- 1 | 45 | 46 | {#if text && isShowing} 47 |
50 | {text} 51 |
52 | {/if} 53 | 54 | 108 | -------------------------------------------------------------------------------- /templates/ButtonSet.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |
24 |
{legend} 26 |
27 |
28 | {#each options as { value, label } (value)} 29 |
30 | 37 | 38 |
39 | 40 | {/each} 41 | 42 |
43 | 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /templates/Checkbox.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 |
28 |
{legend} 30 |
31 | 32 | {#each options as {value, label, selected}} 33 |
34 | 39 | 42 |
43 | 44 | {/each} 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /templates/CodeBlock.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
 16 |     
 17 |       {@html highlightedCode}
 18 |     
 19 |   
20 | 21 | -------------------------------------------------------------------------------- /templates/CollapsibleSection.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |

11 | 17 |

18 | 19 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /templates/Confetti.svelte: -------------------------------------------------------------------------------- 1 | 44 | 45 | 46 | {#each allElements as [element, color, scale], i} 47 | 48 | 51 | {@html element} 52 | 53 | 54 | {/each} 55 | 56 | 57 | -------------------------------------------------------------------------------- /templates/DataDownload.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /templates/Icon.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | {#if icon} 19 | 23 | 24 | {@html icon.contents} 25 | 26 | 27 | {/if} 28 | 29 | 37 | -------------------------------------------------------------------------------- /templates/IconLocal.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 24 | 25 | 31 | {#each paths as path} 32 | 33 | {/each} 34 | 35 | -------------------------------------------------------------------------------- /templates/InView.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /templates/Number.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | {Number.isFinite(+displayNumber) ? formatFunction(displayNumber) : '-'} 40 | 41 | 42 | 47 | -------------------------------------------------------------------------------- /templates/Pagination.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | {#if totalRows && totalRows > perPage} 22 | 39 | {/if} 40 | 41 | 42 | -------------------------------------------------------------------------------- /templates/ProgressDots.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | {#each new Array(+numberOfItems).fill(0) as _, i} 12 |
20 | 21 | 70 | -------------------------------------------------------------------------------- /templates/Radio.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
23 |
{legend} 25 |
26 | {#each options as { value, label }} 27 | 34 | 35 | {/each} 36 |
37 | 38 | -------------------------------------------------------------------------------- /templates/Scatterplot.svelte: -------------------------------------------------------------------------------- 1 | 71 | 72 |
73 | 74 | 75 | {#each $dots as { x, y, r }} 76 | 77 | {/each} 78 | 79 | 80 |
81 | -------------------------------------------------------------------------------- /templates/Switch.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | {#if design == 'inner'} 35 |
36 | {label} 37 | 45 |
46 | {:else if design == 'slider'} 47 |
48 | {label} 49 | 55 |
56 | {:else} 57 |
58 |
63 |
{label}
64 | {#each options as option} 65 | 70 | 73 | {/each} 74 |
75 |
76 | 77 | {/if} 78 | 79 | -------------------------------------------------------------------------------- /templates/Table.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 |
60 |
61 | {#if caption} 62 | 63 | {/if} 64 | 65 | 66 | {#each headers as header, i (header)} 67 | 82 | {/each} 83 | 84 | 85 | {#each sortedRows as row (row)} 86 | 87 | {#each row as column, i} 88 | {#if rowHeaders && i === 0} 89 | 92 | {:else} 93 | 94 | {/if} 95 | {/each} 96 | 97 | 98 | {/each} 99 | 100 |
{caption}
68 | {header} 69 | {#if sort} 70 | 80 | {/if} 81 |
90 | {column} 91 | {column}
101 |
102 | 103 | 104 | -------------------------------------------------------------------------------- /templates/Toggle.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | {#each options as option} 14 | 20 | {/each} 21 |
22 | 23 | 61 | -------------------------------------------------------------------------------- /templates/move.js: -------------------------------------------------------------------------------- 1 | export default (x, y) => `transform: translate(${x}px, ${y}px)` 2 | -------------------------------------------------------------------------------- /templates/tweened-staggered.js: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | import { assign, loop, now } from "svelte/internal"; 3 | import { linear } from "svelte/easing"; 4 | import { interpolate, scaleLinear } from "d3"; 5 | 6 | function get_interpolator(a, b, iDelay = 0, length = 0, delay = 0) { 7 | if (a === b || a !== a) return () => a; 8 | const type = typeof a; 9 | if (type !== typeof b || Array.isArray(a) !== Array.isArray(b)) { 10 | throw new Error("Cannot interpolate values of different type"); 11 | } 12 | if (Array.isArray(a)) { 13 | const arr = b.map((bi, i) => { 14 | return get_interpolator(a[i], bi, iDelay, b.length, i * iDelay); 15 | }); 16 | return (t) => arr.map((fn) => fn(t)); 17 | } 18 | if (type === "object") { 19 | if (!a || !b) throw new Error("Object cannot be null"); 20 | const keys = Object.keys(b); 21 | const interpolators = {}; 22 | keys.forEach((key) => { 23 | interpolators[key] = get_interpolator( 24 | a[key], 25 | b[key], 26 | iDelay, 27 | length, 28 | delay 29 | ); 30 | }); 31 | return (t) => { 32 | const result = {}; 33 | keys.forEach((key) => { 34 | result[key] = interpolators[key](t); 35 | }); 36 | return result; 37 | }; 38 | } 39 | if (["string", "number"].includes(type)) { 40 | const allDelays = iDelay * length; 41 | const iDuration = 1 - allDelays; 42 | const scale = scaleLinear() 43 | .domain([delay, delay + iDuration]) 44 | .range([0, 1]) 45 | .clamp(true); 46 | const interpolationFunction = interpolate(a, b); 47 | // console.log() 48 | return (t) => { 49 | return interpolationFunction(scale(t)); 50 | 51 | // a + Math.max(0, t * maxT - delay) * delta; 52 | }; 53 | } 54 | throw new Error(`Cannot interpolate ${type} values`); 55 | } 56 | 57 | export function tweened(value, defaults = {}) { 58 | const store = writable(value); 59 | let task; 60 | let target_value = value; 61 | function set(new_value, opts) { 62 | if (value == null) { 63 | store.set((value = new_value)); 64 | return Promise.resolve(); 65 | } 66 | target_value = new_value; 67 | let previous_task = task; 68 | let started = false; 69 | let { 70 | delay = 0, 71 | duration = 400, 72 | iDelay = 10, 73 | easing = linear, 74 | interpolate = get_interpolator, 75 | } = assign(assign({}, defaults), opts); 76 | if (duration === 0) { 77 | if (previous_task) { 78 | previous_task.abort(); 79 | previous_task = null; 80 | } 81 | store.set((value = target_value)); 82 | return Promise.resolve(); 83 | } 84 | const start = now() + delay; 85 | let fn; 86 | task = loop((now) => { 87 | if (now < start) return true; 88 | if (!started) { 89 | fn = interpolate(value, new_value, iDelay / duration); 90 | if (typeof duration === "function") 91 | duration = duration(value, new_value); 92 | started = true; 93 | } 94 | if (previous_task) { 95 | previous_task.abort(); 96 | previous_task = null; 97 | } 98 | const elapsed = now - start; 99 | if (elapsed > duration) { 100 | store.set((value = new_value)); 101 | return false; 102 | } 103 | 104 | store.set((value = fn(easing(elapsed / duration)))); 105 | return true; 106 | }); 107 | return task.promise; 108 | } 109 | return { 110 | set, 111 | update: (fn, opts) => set(fn(target_value, value), opts), 112 | subscribe: store.subscribe, 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const config = require('sapper/config/webpack.js'); 4 | const pkg = require('./package.json'); 5 | 6 | const mode = process.env.NODE_ENV; 7 | const dev = mode === 'development'; 8 | 9 | const alias = { svelte: path.resolve('node_modules', 'svelte') }; 10 | const extensions = ['.mjs', '.js', '.json', '.svelte', '.html', '.svg']; 11 | const mainFields = ['svelte', 'module', 'browser', 'main']; 12 | 13 | module.exports = { 14 | client: { 15 | entry: config.client.entry(), 16 | output: config.client.output(), 17 | resolve: { alias, extensions, mainFields }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(svelte|html)$/, 22 | use: { 23 | loader: 'svelte-loader', 24 | options: { 25 | dev, 26 | hydratable: true, 27 | hotReload: false, // pending https://github.com/sveltejs/svelte/issues/2377 28 | }, 29 | }, 30 | }, 31 | ], 32 | }, 33 | mode, 34 | plugins: [ 35 | // pending https://github.com/sveltejs/svelte/issues/2377 36 | // dev && new webpack.HotModuleReplacementPlugin(), 37 | new webpack.DefinePlugin({ 38 | 'process.browser': true, 39 | 'process.env.NODE_ENV': JSON.stringify(mode), 40 | }), 41 | ].filter(Boolean), 42 | devtool: dev && 'inline-source-map', 43 | }, 44 | 45 | server: { 46 | entry: config.server.entry(), 47 | output: config.server.output(), 48 | target: 'node', 49 | resolve: { alias, extensions, mainFields }, 50 | externals: Object.keys(pkg.dependencies).concat('encoding'), 51 | module: { 52 | rules: [ 53 | { 54 | test: /\.(svelte|html)$/, 55 | use: { 56 | loader: 'svelte-loader', 57 | options: { 58 | css: false, 59 | generate: 'ssr', 60 | dev, 61 | }, 62 | }, 63 | }, 64 | ], 65 | }, 66 | mode: process.env.NODE_ENV, 67 | performance: { 68 | hints: false, // it doesn't matter if server.js is large 69 | }, 70 | }, 71 | 72 | serviceworker: { 73 | entry: config.serviceworker.entry(), 74 | output: config.serviceworker.output(), 75 | mode: process.env.NODE_ENV, 76 | }, 77 | }; 78 | --------------------------------------------------------------------------------