├── cypress.json ├── netlify.toml ├── .gitignore ├── static ├── favicon.png ├── logo-192.png ├── logo-512.png ├── profile-pic.png ├── fonts │ ├── rubik-latin-300.woff │ ├── rubik-latin-300.woff2 │ ├── rubik-latin-400.woff │ ├── rubik-latin-400.woff2 │ ├── rubik-latin-500.woff │ ├── rubik-latin-500.woff2 │ ├── rubik-latin-700.woff │ ├── rubik-latin-700.woff2 │ ├── rubik-latin-900.woff │ ├── rubik-latin-900.woff2 │ ├── merriweather-latin-300.woff │ ├── merriweather-latin-400.woff │ ├── merriweather-latin-700.woff │ ├── merriweather-latin-900.woff │ ├── rubik-latin-300italic.woff │ ├── rubik-latin-300italic.woff2 │ ├── rubik-latin-400italic.woff │ ├── rubik-latin-400italic.woff2 │ ├── rubik-latin-500italic.woff │ ├── rubik-latin-500italic.woff2 │ ├── rubik-latin-700italic.woff │ ├── rubik-latin-700italic.woff2 │ ├── rubik-latin-900italic.woff │ ├── rubik-latin-900italic.woff2 │ ├── merriweather-latin-300.woff2 │ ├── merriweather-latin-400.woff2 │ ├── merriweather-latin-700.woff2 │ ├── merriweather-latin-900.woff2 │ ├── merriweather-latin-300italic.woff │ ├── merriweather-latin-300italic.woff2 │ ├── merriweather-latin-400italic.woff │ ├── merriweather-latin-400italic.woff2 │ ├── merriweather-latin-700italic.woff │ ├── merriweather-latin-700italic.woff2 │ ├── merriweather-latin-900italic.woff │ └── merriweather-latin-900italic.woff2 ├── rsz_florian-klauer-489-unsplash.jpg ├── manifest.json ├── global.css ├── highlight.css ├── fonts.css └── undraw-illustration.svg ├── src ├── client.js ├── components │ ├── Logo.svelte │ ├── Header.svelte │ ├── Bio.svelte │ └── Nav.svelte ├── routes │ ├── blog │ │ ├── _posts.js │ │ ├── posts │ │ │ ├── hello-world.md │ │ │ └── markdown-test.md │ │ ├── index.json.js │ │ ├── [slug].json.js │ │ ├── index.svelte │ │ └── [slug].svelte │ ├── _error.svelte │ ├── _layout.svelte │ ├── index.svelte │ └── about.svelte ├── server.js ├── template.html ├── utils │ └── markdown.js └── service-worker.js ├── cypress ├── fixtures │ └── example.json ├── integration │ └── spec.js ├── plugins │ └── index.js └── support │ ├── index.js │ └── commands.js ├── appveyor.yml ├── LICENSE ├── package.json ├── README.md └── rollup.config.js /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "video": false 4 | } -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "__sapper__/export" 3 | command = "npm run export" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | yarn-error.log 4 | /cypress/screenshots/ 5 | /__sapper__/ 6 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/favicon.png -------------------------------------------------------------------------------- /static/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/logo-192.png -------------------------------------------------------------------------------- /static/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/logo-512.png -------------------------------------------------------------------------------- /static/profile-pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/profile-pic.png -------------------------------------------------------------------------------- /static/fonts/rubik-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-300.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-300.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-400.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-400.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-500.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-500.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-700.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-700.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-900.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-900.woff2 -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import * as sapper from '@sapper/app'; 2 | 3 | sapper.start({ 4 | target: document.querySelector('#sapper') 5 | }); 6 | -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-300.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-400.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-700.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-900.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-300italic.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-300italic.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-400italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-400italic.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-400italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-400italic.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-500italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-500italic.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-500italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-500italic.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-700italic.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-700italic.woff2 -------------------------------------------------------------------------------- /static/fonts/rubik-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-900italic.woff -------------------------------------------------------------------------------- /static/fonts/rubik-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/rubik-latin-900italic.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-300.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-400.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-700.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-900.woff2 -------------------------------------------------------------------------------- /static/rsz_florian-klauer-489-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/rsz_florian-klauer-489-unsplash.jpg -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-300italic.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-300italic.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-400italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-400italic.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-400italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-400italic.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-700italic.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-700italic.woff2 -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-900italic.woff -------------------------------------------------------------------------------- /static/fonts/merriweather-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Charca/sapper-blog-template/HEAD/static/fonts/merriweather-latin-900italic.woff2 -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /src/components/Logo.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | Sapper 11 | 12 | -------------------------------------------------------------------------------- /src/routes/blog/_posts.js: -------------------------------------------------------------------------------- 1 | import all from './posts/*.md'; 2 | 3 | export default all 4 | .map((post) => ({ ...post, html: post.html.replace(/^\t{3}/gm, '') })) 5 | .sort((a, b) => new Date(b.date) - new Date(a.date)); 6 | -------------------------------------------------------------------------------- /src/routes/blog/posts/hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 👋 3 | date: "2019-06-11T08:38:00.000Z" 4 | --- 5 | 6 | Every blog starts with a single post. This is yours. Make it great. 7 | 8 | 9 | 10 | This post intentionally left blank. 11 | 12 | Write what you want. 13 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | shallow_clone: true 4 | 5 | init: 6 | - git config --global core.autocrlf false 7 | 8 | build: off 9 | 10 | environment: 11 | matrix: 12 | # node.js 13 | - nodejs_version: stable 14 | 15 | install: 16 | - ps: Install-Product node $env:nodejs_version 17 | - npm install 18 | -------------------------------------------------------------------------------- /src/components/Header.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 |
19 | 20 |
22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/routes/blog/index.json.js: -------------------------------------------------------------------------------- 1 | import posts from './_posts.js'; 2 | 3 | const contents = JSON.stringify(posts.map(post => { 4 | return { 5 | title: post.title, 6 | slug: post.slug, 7 | excerpt: post.excerpt, 8 | printDate: post.printDate, 9 | }; 10 | })); 11 | 12 | export function get(req, res) { 13 | res.writeHead(200, { 14 | 'Content-Type': 'application/json' 15 | }); 16 | 17 | res.end(contents); 18 | } 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cypress/integration/spec.js: -------------------------------------------------------------------------------- 1 | describe('Sapper template app', () => { 2 | beforeEach(() => { 3 | cy.visit('/') 4 | }); 5 | 6 | it('has the correct

', () => { 7 | cy.contains('h1', 'Welcome to your new Sapper Blog') 8 | }); 9 | 10 | it('navigates to /about', () => { 11 | cy.get('nav a').contains('about').click(); 12 | cy.url().should('include', '/about'); 13 | }); 14 | 15 | it('navigates to /blog', () => { 16 | cy.get('nav a').contains('blog').click(); 17 | cy.url().should('include', '/blog'); 18 | }); 19 | }); -------------------------------------------------------------------------------- /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/blog/[slug].json.js: -------------------------------------------------------------------------------- 1 | import posts from './_posts.js'; 2 | 3 | const lookup = new Map(); 4 | posts.forEach(post => { 5 | lookup.set(post.slug, JSON.stringify(post)); 6 | }); 7 | 8 | export function get(req, res, next) { 9 | // the `slug` parameter is available because 10 | // this file is called [slug].json.js 11 | const { slug } = req.params; 12 | 13 | if (lookup.has(slug)) { 14 | res.writeHead(200, { 15 | 'Content-Type': 'application/json' 16 | }); 17 | 18 | res.end(lookup.get(slug)); 19 | } else { 20 | res.writeHead(404, { 21 | 'Content-Type': 'application/json' 22 | }); 23 | 24 | res.end(JSON.stringify({ 25 | message: `Not found` 26 | })); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | } 18 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /src/components/Bio.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
22 | Maxi Ferreira 23 |

24 | Hi, I'm Maxi. I'm a software engineer and data scientist from Córdoba, Argentina. 25 | You can follow me on Twitter, 26 | see some of my work on GitHub, 27 | or read more about me on my website. 28 |

29 |
30 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Maxi Ferreira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/routes/blog/index.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 31 | 32 | 33 | Blog 34 | 35 | 36 |
37 |

Blog

38 | {#each posts as post, index} 39 | {#if index} 40 |
41 | {/if} 42 |
43 |

44 | {post.title} 45 |

46 |

{post.excerpt}

47 |
48 | — {post.printDate} 49 |
50 |
51 | {/each} 52 |
53 | -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sapper.base% 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | %sapper.styles% 20 | 21 | 23 | %sapper.head% 24 | 25 | 26 | 28 |
%sapper.html%
29 | 30 | 33 | %sapper.scripts% 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/routes/_layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | 40 |
41 |
42 | 43 |
44 | 45 |
46 | 47 | 54 |
55 | -------------------------------------------------------------------------------- /src/components/Nav.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 50 | 51 | 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TODO", 3 | "description": "TODO", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "sapper dev", 7 | "build": "sapper build", 8 | "export": "sapper export", 9 | "start": "node __sapper__/build", 10 | "cy:run": "cypress run", 11 | "cy:open": "cypress open", 12 | "test": "run-p --race dev cy:run" 13 | }, 14 | "dependencies": { 15 | "compression": "^1.7.1", 16 | "date-fns": "^1.30.1", 17 | "gray-matter": "^4.0.2", 18 | "marked": "^0.7.0", 19 | "polka": "^0.5.0", 20 | "prismjs": "^1.23.0", 21 | "reading-time": "^1.2.0", 22 | "sirv": "^0.4.0" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.0.0", 26 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 27 | "@babel/plugin-transform-runtime": "^7.0.0", 28 | "@babel/preset-env": "^7.0.0", 29 | "@babel/runtime": "^7.0.0", 30 | "@rollup/plugin-babel": "^5.2.1", 31 | "@rollup/plugin-commonjs": "^13.0.0", 32 | "@rollup/plugin-node-resolve": "^8.0.1", 33 | "@rollup/plugin-replace": "^2.3.3", 34 | "cypress": "^5.2.0", 35 | "npm-run-all": "^4.1.5", 36 | "rollup": "^2.13.1", 37 | "rollup-plugin-glob": "^1.0.2", 38 | "rollup-plugin-svelte": "^5.0.1", 39 | "rollup-plugin-terser": "^7.0.2", 40 | "sapper": "^0.27.13", 41 | "svelte": "^3.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 62 | 63 | 64 | Sapper Blog Template 65 | 66 | 67 |
68 |
69 |

Welcome to your new Sapper Blog

70 |

Check out the docs on GitHub to get started.

71 |
72 | 73 |
74 | Person typing on laptop 75 |
Illustration thanks to Undraw
76 |
77 |
78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sapper-blog-template 2 | 3 | A starter template for building markdown-powered blogs with [Sapper](https://github.com/sveltejs/sapper) and [Svelte](https://github.com/sveltejs/svelte). 4 | 5 | ### 👉 [Demo](https://sapper-blog-template.netlify.com/) 6 | 7 | ## ✨ Getting started 8 | 9 | ```bash 10 | npx degit Charca/sapper-blog-template my-blog 11 | cd my-blog 12 | npm install # or yarn! 13 | npm run dev 14 | ``` 15 | 16 | Open up [localhost:3000](http://localhost:3000) and start clicking around. 17 | 18 | Consult [sapper.svelte.dev](https://sapper.svelte.dev) for help getting started. 19 | 20 | ## 🏗 Structure 21 | 22 | The base structure of this template is the same as Sapper's [default template](https://github.com/sveltejs/sapper-template/). These are some of the new things you'll find here: 23 | 24 | ### src/routes/blog 25 | 26 | This is the home of your blog. The most important files in here are: 27 | 28 | - `_posts.js`: this module contains the logic for loading and parsing your markdown posts. 29 | - `[slug].svelte`: this is the template of your blog post page. 30 | - `index.svelte`: this is the template of your article list page. 31 | 32 | ### src/routes/blog/posts 33 | 34 | This is where your markdown posts live in. All `.md` files in this directory are treated as blog posts and parsed automatically by the `_posts.js` module. 35 | 36 | - The markdown file name becomes the post slug. For example `hello-world.md` becomes `http://localhost:3000/blog/hello-world`. 37 | - Everything between the start of the post and the `` tag becomes the article's "excerpt". 38 | - Frontmatter properties supported are `title` and `date`. 39 | 40 | ## 🚀 Deploy 41 | 42 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/Charca/sapper-blog-template) 43 | 44 | 45 | ## 🐛 Bugs and feedback 46 | 47 | Sapper is in early development, and may have the odd rough edge here and there. Please be vocal over on the [Sapper issue tracker](https://github.com/sveltejs/sapper/issues). 48 | -------------------------------------------------------------------------------- /src/routes/about.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | About 22 | 23 | 24 |
25 |

About

26 |
27 | A vintage typewriter. 28 |
Photo by Florian Klauer on Unsplash
29 |
30 |

Text placeholder via Jeffsum.

31 |

So you two dig up, dig up dinosaurs? What do they got in there? King Kong? My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard! Life finds a way. God creates dinosaurs. God destroys dinosaurs. God creates Man. Man destroys God. Man creates Dinosaurs.

32 |

You really think you can fly that thing? You know what? It is beets. I've crashed into a beet truck. Forget the fat lady! You're obsessed with the fat lady! Drive us out of here! Is this my espresso machine? Wh-what is-h-how did you get my espresso machine?

33 |

Hey, you know how I'm, like, always trying to save the planet? Here's my chance. Hey, take a look at the earthlings. Goodbye! I was part of something special. Just my luck, no ice. You're a very talented young man, with your own clever thoughts and ideas. Do you need a manager?

34 |

Jaguar shark! So tell me - does it really exist? This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. Yes, Yes, without the oops! You're a very talented young man, with your own clever thoughts and ideas. Do you need a manager?

35 |

Yes, Yes, without the oops! Do you have any idea how long it takes those cups to decompose. They're using our own satellites against us. And the clock is ticking. Do you have any idea how long it takes those cups to decompose. My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard!

36 |
37 | -------------------------------------------------------------------------------- /src/utils/markdown.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const prism = require('prismjs'); 3 | const marked = require('marked'); 4 | const matter = require('gray-matter'); 5 | const formatDate = require('date-fns/format'); 6 | const readingTime = require('reading-time'); 7 | 8 | // Support JSX syntax highlighting 9 | require('prismjs/components/prism-jsx.min'); 10 | 11 | const EXCERPT_SEPARATOR = ''; 12 | const renderer = new marked.Renderer(); 13 | const linkRenderer = renderer.link; 14 | renderer.link = (href, title, text) => { 15 | const html = linkRenderer.call(renderer, href, title, text); 16 | 17 | if (href.indexOf('/') === 0) { 18 | // Do not open internal links on new tab 19 | return html; 20 | } else if (href.indexOf('#') === 0) { 21 | // Handle hash links to internal elements 22 | const html = linkRenderer.call(renderer, 'javascript:;', title, text); 23 | return html.replace( 24 | /^ { 33 | const parser = prism.languages[language] || prism.languages.html; 34 | const highlighted = prism.highlight(code, parser, language); 35 | return `
${highlighted}
`; 36 | }; 37 | 38 | marked.setOptions({ renderer }); 39 | 40 | export default () => ({ 41 | transform(md, id) { 42 | if (!/\.md$/.test(id)) return null; 43 | 44 | const fileName = path.basename(id); 45 | const { data, content: rawContent } = matter(md); 46 | const { title, date } = data; 47 | const slug = fileName.split('.')[0]; 48 | let content = rawContent; 49 | let excerpt = ''; 50 | 51 | if (rawContent.indexOf(EXCERPT_SEPARATOR) !== -1) { 52 | const splittedContent = rawContent.split(EXCERPT_SEPARATOR); 53 | excerpt = splittedContent[0]; 54 | content = splittedContent[1]; 55 | } 56 | 57 | const html = marked(content); 58 | const readingStats = readingTime(content); 59 | const printReadingTime = readingStats.text; 60 | const printDate = formatDate(new Date(date), 'MMMM D, YYYY'); 61 | 62 | const exportFromModule = JSON.stringify({ 63 | title: title || slug, 64 | slug, 65 | html, 66 | date, 67 | excerpt, 68 | printDate, 69 | printReadingTime, 70 | }); 71 | 72 | return { 73 | code: `export default ${exportFromModule}`, 74 | map: { mappings: '' }, 75 | }; 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /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/routes/blog/[slug].svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 20 | 21 | 42 | 43 | 44 | {post.title} 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | 82 | 83 | 84 | 85 |
86 |

{post.printDate} ~ {post.printReadingTime}

87 |

{post.title}

88 |
89 |
90 |
91 |
92 | {@html post.html} 93 |
94 |
95 | 96 |
97 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import replace from '@rollup/plugin-replace' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import svelte from 'rollup-plugin-svelte' 5 | import babel from '@rollup/plugin-babel' 6 | import { terser } from 'rollup-plugin-terser' 7 | import glob from 'rollup-plugin-glob' 8 | import config from 'sapper/config/rollup.js' 9 | import markdown from './src/utils/markdown.js' 10 | import pkg from './package.json' 11 | 12 | const mode = process.env.NODE_ENV 13 | const dev = mode === 'development' 14 | const legacy = !!process.env.SAPPER_LEGACY_BUILD 15 | 16 | const onwarn = (warning, onwarn) => 17 | (warning.code === 'CIRCULAR_DEPENDENCY' && 18 | warning.message.includes('/@sapper/')) || 19 | onwarn(warning) 20 | 21 | export default { 22 | client: { 23 | input: config.client.input(), 24 | output: config.client.output(), 25 | plugins: [ 26 | replace({ 27 | 'process.browser': true, 28 | 'process.env.NODE_ENV': JSON.stringify(mode), 29 | }), 30 | svelte({ 31 | dev, 32 | hydratable: true, 33 | emitCss: true, 34 | }), 35 | resolve(), 36 | commonjs(), 37 | markdown(), 38 | glob(), 39 | legacy && 40 | babel({ 41 | extensions: ['.js', '.mjs', '.html', '.svelte'], 42 | babelHelpers: 'runtime', 43 | exclude: ['node_modules/@babel/**'], 44 | presets: [ 45 | [ 46 | '@babel/preset-env', 47 | { 48 | targets: '> 0.25%, not dead', 49 | }, 50 | ], 51 | ], 52 | plugins: [ 53 | '@babel/plugin-syntax-dynamic-import', 54 | [ 55 | '@babel/plugin-transform-runtime', 56 | { 57 | useESModules: true, 58 | }, 59 | ], 60 | ], 61 | }), 62 | 63 | !dev && 64 | terser({ 65 | module: true, 66 | }), 67 | ], 68 | preserveEntrySignatures: false, 69 | onwarn, 70 | }, 71 | 72 | server: { 73 | input: config.server.input(), 74 | output: config.server.output(), 75 | plugins: [ 76 | replace({ 77 | 'process.browser': false, 78 | 'process.env.NODE_ENV': JSON.stringify(mode), 79 | }), 80 | svelte({ 81 | generate: 'ssr', 82 | dev, 83 | }), 84 | resolve(), 85 | commonjs(), 86 | markdown(), 87 | glob(), 88 | ], 89 | external: Object.keys(pkg.dependencies).concat( 90 | require('module').builtinModules || 91 | Object.keys(process.binding('natives')) 92 | ), 93 | 94 | onwarn, 95 | }, 96 | 97 | serviceworker: { 98 | input: config.serviceworker.input(), 99 | output: config.serviceworker.output(), 100 | plugins: [ 101 | resolve(), 102 | replace({ 103 | 'process.browser': true, 104 | 'process.env.NODE_ENV': JSON.stringify(mode), 105 | }), 106 | commonjs(), 107 | !dev && terser(), 108 | ], 109 | 110 | onwarn, 111 | }, 112 | } 113 | -------------------------------------------------------------------------------- /static/global.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: Merriweather, serif; 8 | font-size: 16px; 9 | line-height: 1.6; 10 | color: #36393b; 11 | } 12 | 13 | h1, h2, h3, h4, h5, h6 { 14 | font-family: Rubik, sans-serif; 15 | font-weight: 700; 16 | line-height: 1.2; 17 | margin: 0 0 0.5em 0; 18 | } 19 | 20 | h1 { 21 | font-family: Merriweather, serif; 22 | font-size: 4em; 23 | margin: 0 0 1em 0; 24 | } 25 | 26 | h2 { 27 | margin: 1.6em 0 0 0; 28 | font-size: 1.8em; 29 | } 30 | 31 | h3 { 32 | font-size: 1.5em; 33 | } 34 | 35 | h4 { 36 | font-size: 1.4em; 37 | } 38 | 39 | h5 { 40 | font-size: 1.3em; 41 | } 42 | 43 | h6 { 44 | font-size: 1.2em; 45 | } 46 | 47 | p, ul, ol { 48 | font-size: 1.3rem; 49 | line-height: 1.75em; 50 | margin: 1.2em 0; 51 | } 52 | 53 | ol, ul { 54 | padding-left: 2rem; 55 | -webkit-padding-start: 5%; 56 | -webkit-padding-end: 5%; 57 | } 58 | 59 | li { 60 | margin: 1rem 0; 61 | } 62 | 63 | li p { 64 | margin-bottom: 0.5rem; 65 | margin-top: 0.5rem; 66 | } 67 | 68 | a { 69 | color: inherit; 70 | transition: color linear .15s; 71 | } 72 | 73 | a:hover { 74 | color: #fd6378; 75 | } 76 | 77 | p a { 78 | text-decoration: none; 79 | box-shadow: inset 0 -0.12em 0 #fd6378; 80 | -webkit-transition: box-shadow .2s ease-in-out, color .2s ease-in-out; 81 | transition: box-shadow .2s ease-in-out, color .2s ease-in-out; 82 | } 83 | 84 | p a:hover { 85 | box-shadow: inset 0 -1.5em 0 #fd6378; 86 | color: #FFF; 87 | } 88 | 89 | img { 90 | max-width: 100%; 91 | } 92 | 93 | hr { 94 | background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(253, 99, 120, .8), rgba(0, 0, 0, 0)); 95 | border: 0; 96 | height: 2px; 97 | margin: 40px auto; 98 | } 99 | 100 | code { 101 | font-family: 'SF Mono', menlo, inconsolata, monospace; 102 | font-size: calc(1em - 2px); 103 | color: #555; 104 | background-color: #f0f0f0; 105 | padding: 0.2em 0.4em; 106 | border-radius: 2px; 107 | -webkit-font-smoothing: antialiased; 108 | -moz-osx-font-smoothing: grayscale; 109 | } 110 | 111 | pre { 112 | border-radius: 8px!important; 113 | margin: 1.2em 0!important; 114 | } 115 | 116 | pre code { 117 | padding: 0; 118 | } 119 | 120 | blockquote { 121 | border-left: 4px solid #cccccc; 122 | font-size: 1.4em; 123 | font-style: italic; 124 | margin: 2rem 0; 125 | padding-left: 2rem; 126 | padding-right: 2rem; 127 | } 128 | 129 | blockquote p { 130 | padding-bottom: 6px; 131 | } 132 | 133 | blockquote footer { 134 | font-size: 1.1rem; 135 | text-align: right; 136 | } 137 | 138 | figure { 139 | padding: 0; 140 | border: 0; 141 | font-size: 100%; 142 | font: inherit; 143 | vertical-align: baseline; 144 | -webkit-margin-start: 0; 145 | -webkit-margin-end: 0; 146 | margin: 0 0 3em 0; 147 | } 148 | 149 | table { 150 | border-collapse: collapse; 151 | font-family: Rubik, sans-serif; 152 | font-size: 1.125em; 153 | margin: 2em 0; 154 | } 155 | 156 | th { 157 | border-bottom: 2px solid #cccccc; 158 | padding: 0.4em 0.8em; 159 | } 160 | 161 | td { 162 | padding: 0.4em 0.8em; 163 | } 164 | 165 | .container { 166 | margin: 0 auto; 167 | max-width: 42em; 168 | width: 100%; 169 | } 170 | 171 | .content h1 { 172 | font-size: 3em; 173 | margin: 1em 0; 174 | } 175 | 176 | @media (max-width: 1020px) { 177 | h1 { 178 | font-size: 3em; 179 | } 180 | 181 | .content h1 { 182 | font-size: 2.4em; 183 | } 184 | } 185 | 186 | @media (max-width: 480px) { 187 | body { 188 | font-size: 14px; 189 | } 190 | 191 | p, ul, ol { 192 | font-size: 1.2rem; 193 | margin: 1em 0; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /static/highlight.css: -------------------------------------------------------------------------------- 1 | code[class*="language-"], pre[class*="language-"] { 2 | text-align: left; 3 | white-space: pre; 4 | word-spacing: normal; 5 | word-break: normal; 6 | word-wrap: normal; 7 | hyphens: none; 8 | color: inherit; 9 | text-shadow: none; 10 | /* font-family: Roboto Mono, monospace; */ 11 | font-size: 1em; 12 | line-height: 1.5em; 13 | tab-size: 4; 14 | } 15 | 16 | code[class*="language-"]::-moz-selection, pre[class*="language-"]::-moz-selection, 17 | code[class*="language-"] ::-moz-selection, 18 | pre[class*="language-"] ::-moz-selection { 19 | text-shadow: none; 20 | opacity: 1; 21 | color: #eee; 22 | background: #363636; 23 | } 24 | 25 | code[class*="language-"]::selection, pre[class*="language-"]::selection, 26 | code[class*="language-"] ::selection, 27 | pre[class*="language-"] ::selection { 28 | text-shadow: none; 29 | opacity: 1; 30 | color: #eee; 31 | background: #363636; 32 | } 33 | 34 | @media print { 35 | code[class*="language-"], pre[class*="language-"] { 36 | text-shadow: none !important; 37 | } 38 | } 39 | 40 | :not(pre) > code[class*="language-"] { 41 | white-space: normal; 42 | color: #c3cee3; 43 | background: #263238; 44 | border-radius: 0.2em; 45 | padding: 0.1em; 46 | } 47 | 48 | pre > code[class*="language-"] { 49 | background: none; 50 | } 51 | 52 | pre[class*="language-"] { 53 | overflow: auto; 54 | position: relative; 55 | color: #c3cee3; 56 | background: #263238; 57 | border-radius: 0; 58 | margin: 0.5em 0; 59 | padding: 1.25em 1em; 60 | } 61 | 62 | pre[class*="language-"].language-css > code, pre[class*="language-"].language-sass > code, pre[class*="language-"].language-scss > code { 63 | color: #fd9170; 64 | } 65 | 66 | [class*="language-"] .namespace { 67 | opacity: 0.7; 68 | } 69 | 70 | .token.atrule { 71 | color: #c792ea; 72 | } 73 | 74 | .token.attr-name { 75 | color: #ffcb6b; 76 | } 77 | 78 | .token.attr-value { 79 | color: #c3e88d; 80 | } 81 | 82 | .token.attribute { 83 | color: #c3e88d; 84 | } 85 | 86 | .token.boolean { 87 | color: #c792ea; 88 | } 89 | 90 | .token.builtin { 91 | color: #ffcb6b; 92 | } 93 | 94 | .token.cdata { 95 | color: #80cbc4; 96 | } 97 | 98 | .token.char { 99 | color: #80cbc4; 100 | } 101 | 102 | .token.class { 103 | color: #ffcb6b; 104 | } 105 | 106 | .token.class-name { 107 | color: #f2ff00; 108 | } 109 | 110 | .token.color { 111 | color: #f2ff00; 112 | } 113 | 114 | .token.comment { 115 | color: #546e7a; 116 | } 117 | 118 | .token.constant { 119 | color: #c792ea; 120 | } 121 | 122 | .token.deleted { 123 | color: #f07178; 124 | } 125 | 126 | .token.doctype { 127 | color: #546e7a; 128 | } 129 | 130 | .token.entity { 131 | color: #f07178; 132 | } 133 | 134 | .token.function { 135 | color: #c792ea; 136 | } 137 | 138 | .token.hexcode { 139 | color: #f2ff00; 140 | } 141 | 142 | .token.id { 143 | color: #c792ea; 144 | font-weight: bold; 145 | } 146 | 147 | .token.important { 148 | color: #c792ea; 149 | font-weight: bold; 150 | } 151 | 152 | .token.inserted { 153 | color: #80cbc4; 154 | } 155 | 156 | .token.keyword { 157 | color: #c792ea; 158 | font-style: italic; 159 | } 160 | 161 | .token.number { 162 | color: #fd9170; 163 | } 164 | 165 | .token.operator { 166 | color: #89ddff; 167 | } 168 | 169 | .token.prolog { 170 | color: #546e7a; 171 | } 172 | 173 | .token.property { 174 | color: #80cbc4; 175 | } 176 | 177 | .token.pseudo-class { 178 | color: #c3e88d; 179 | } 180 | 181 | .token.pseudo-element { 182 | color: #c3e88d; 183 | } 184 | 185 | .token.punctuation { 186 | color: #89ddff; 187 | } 188 | 189 | .token.regex { 190 | color: #f2ff00; 191 | } 192 | 193 | .token.selector { 194 | color: #f07178; 195 | } 196 | 197 | .token.string { 198 | color: #c3e88d; 199 | } 200 | 201 | .token.symbol { 202 | color: #c792ea; 203 | } 204 | 205 | .token.tag { 206 | color: #f07178; 207 | } 208 | 209 | .token.unit { 210 | color: #f07178; 211 | } 212 | 213 | .token.url { 214 | color: #fd9170; 215 | } 216 | 217 | .token.variable { 218 | color: #f07178; 219 | } 220 | -------------------------------------------------------------------------------- /static/fonts.css: -------------------------------------------------------------------------------- 1 | /******** MERRIWEATHER *********/ 2 | 3 | /* merriweather-300normal - latin */ 4 | @font-face { 5 | font-family: 'Merriweather'; 6 | font-style: normal; 7 | font-display: swap; 8 | font-weight: 300; 9 | src: 10 | local('Merriweather Light '), 11 | local('Merriweather-Light'), 12 | url('./fonts/merriweather-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ 13 | url('./fonts/merriweather-latin-300.woff') format('woff'); /* Modern Browsers */ 14 | } 15 | 16 | /* merriweather-300italic - latin */ 17 | @font-face { 18 | font-family: 'Merriweather'; 19 | font-style: italic; 20 | font-display: swap; 21 | font-weight: 300; 22 | src: 23 | local('Merriweather Light italic'), 24 | local('Merriweather-Lightitalic'), 25 | url('./fonts/merriweather-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */ 26 | url('./fonts/merriweather-latin-300italic.woff') format('woff'); /* Modern Browsers */ 27 | } 28 | 29 | /* merriweather-400normal - latin */ 30 | @font-face { 31 | font-family: 'Merriweather'; 32 | font-style: normal; 33 | font-display: swap; 34 | font-weight: 400; 35 | src: 36 | local('Merriweather Regular '), 37 | local('Merriweather-Regular'), 38 | url('./fonts/merriweather-latin-400.woff2') format('woff2'), /* Super Modern Browsers */ 39 | url('./fonts/merriweather-latin-400.woff') format('woff'); /* Modern Browsers */ 40 | } 41 | 42 | /* merriweather-400italic - latin */ 43 | @font-face { 44 | font-family: 'Merriweather'; 45 | font-style: italic; 46 | font-display: swap; 47 | font-weight: 400; 48 | src: 49 | local('Merriweather Regular italic'), 50 | local('Merriweather-Regularitalic'), 51 | url('./fonts/merriweather-latin-400italic.woff2') format('woff2'), /* Super Modern Browsers */ 52 | url('./fonts/merriweather-latin-400italic.woff') format('woff'); /* Modern Browsers */ 53 | } 54 | 55 | /* merriweather-700normal - latin */ 56 | @font-face { 57 | font-family: 'Merriweather'; 58 | font-style: normal; 59 | font-display: swap; 60 | font-weight: 700; 61 | src: 62 | local('Merriweather Bold '), 63 | local('Merriweather-Bold'), 64 | url('./fonts/merriweather-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 65 | url('./fonts/merriweather-latin-700.woff') format('woff'); /* Modern Browsers */ 66 | } 67 | 68 | /* merriweather-700italic - latin */ 69 | @font-face { 70 | font-family: 'Merriweather'; 71 | font-style: italic; 72 | font-display: swap; 73 | font-weight: 700; 74 | src: 75 | local('Merriweather Bold italic'), 76 | local('Merriweather-Bolditalic'), 77 | url('./fonts/merriweather-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */ 78 | url('./fonts/merriweather-latin-700italic.woff') format('woff'); /* Modern Browsers */ 79 | } 80 | 81 | /* merriweather-900normal - latin */ 82 | @font-face { 83 | font-family: 'Merriweather'; 84 | font-style: normal; 85 | font-display: swap; 86 | font-weight: 900; 87 | src: 88 | local('Merriweather Black '), 89 | local('Merriweather-Black'), 90 | url('./fonts/merriweather-latin-900.woff2') format('woff2'), /* Super Modern Browsers */ 91 | url('./fonts/merriweather-latin-900.woff') format('woff'); /* Modern Browsers */ 92 | } 93 | 94 | /* merriweather-900italic - latin */ 95 | @font-face { 96 | font-family: 'Merriweather'; 97 | font-style: italic; 98 | font-display: swap; 99 | font-weight: 900; 100 | src: 101 | local('Merriweather Black italic'), 102 | local('Merriweather-Blackitalic'), 103 | url('./fonts/merriweather-latin-900italic.woff2') format('woff2'), /* Super Modern Browsers */ 104 | url('./fonts/merriweather-latin-900italic.woff') format('woff'); /* Modern Browsers */ 105 | } 106 | 107 | /******** RUBIK *********/ 108 | 109 | /* rubik-300normal - latin */ 110 | @font-face { 111 | font-family: 'Rubik'; 112 | font-style: normal; 113 | font-display: swap; 114 | font-weight: 300; 115 | src: 116 | local('Rubik Light '), 117 | local('Rubik-Light'), 118 | url('./fonts/rubik-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ 119 | url('./fonts/rubik-latin-300.woff') format('woff'); /* Modern Browsers */ 120 | } 121 | 122 | /* rubik-300italic - latin */ 123 | @font-face { 124 | font-family: 'Rubik'; 125 | font-style: italic; 126 | font-display: swap; 127 | font-weight: 300; 128 | src: 129 | local('Rubik Light italic'), 130 | local('Rubik-Lightitalic'), 131 | url('./fonts/rubik-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */ 132 | url('./fonts/rubik-latin-300italic.woff') format('woff'); /* Modern Browsers */ 133 | } 134 | 135 | /* rubik-400normal - latin */ 136 | @font-face { 137 | font-family: 'Rubik'; 138 | font-style: normal; 139 | font-display: swap; 140 | font-weight: 400; 141 | src: 142 | local('Rubik Regular '), 143 | local('Rubik-Regular'), 144 | url('./fonts/rubik-latin-400.woff2') format('woff2'), /* Super Modern Browsers */ 145 | url('./fonts/rubik-latin-400.woff') format('woff'); /* Modern Browsers */ 146 | } 147 | 148 | /* rubik-400italic - latin */ 149 | @font-face { 150 | font-family: 'Rubik'; 151 | font-style: italic; 152 | font-display: swap; 153 | font-weight: 400; 154 | src: 155 | local('Rubik Regular italic'), 156 | local('Rubik-Regularitalic'), 157 | url('./fonts/rubik-latin-400italic.woff2') format('woff2'), /* Super Modern Browsers */ 158 | url('./fonts/rubik-latin-400italic.woff') format('woff'); /* Modern Browsers */ 159 | } 160 | 161 | /* rubik-500normal - latin */ 162 | @font-face { 163 | font-family: 'Rubik'; 164 | font-style: normal; 165 | font-display: swap; 166 | font-weight: 500; 167 | src: 168 | local('Rubik Medium '), 169 | local('Rubik-Medium'), 170 | url('./fonts/rubik-latin-500.woff2') format('woff2'), /* Super Modern Browsers */ 171 | url('./fonts/rubik-latin-500.woff') format('woff'); /* Modern Browsers */ 172 | } 173 | 174 | /* rubik-500italic - latin */ 175 | @font-face { 176 | font-family: 'Rubik'; 177 | font-style: italic; 178 | font-display: swap; 179 | font-weight: 500; 180 | src: 181 | local('Rubik Medium italic'), 182 | local('Rubik-Mediumitalic'), 183 | url('./fonts/rubik-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */ 184 | url('./fonts/rubik-latin-500italic.woff') format('woff'); /* Modern Browsers */ 185 | } 186 | 187 | /* rubik-700normal - latin */ 188 | @font-face { 189 | font-family: 'Rubik'; 190 | font-style: normal; 191 | font-display: swap; 192 | font-weight: 700; 193 | src: 194 | local('Rubik Bold '), 195 | local('Rubik-Bold'), 196 | url('./fonts/rubik-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 197 | url('./fonts/rubik-latin-700.woff') format('woff'); /* Modern Browsers */ 198 | } 199 | 200 | /* rubik-700italic - latin */ 201 | @font-face { 202 | font-family: 'Rubik'; 203 | font-style: italic; 204 | font-display: swap; 205 | font-weight: 700; 206 | src: 207 | local('Rubik Bold italic'), 208 | local('Rubik-Bolditalic'), 209 | url('./fonts/rubik-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */ 210 | url('./fonts/rubik-latin-700italic.woff') format('woff'); /* Modern Browsers */ 211 | } 212 | 213 | /* rubik-900normal - latin */ 214 | @font-face { 215 | font-family: 'Rubik'; 216 | font-style: normal; 217 | font-display: swap; 218 | font-weight: 900; 219 | src: 220 | local('Rubik Black '), 221 | local('Rubik-Black'), 222 | url('./fonts/rubik-latin-900.woff2') format('woff2'), /* Super Modern Browsers */ 223 | url('./fonts/rubik-latin-900.woff') format('woff'); /* Modern Browsers */ 224 | } 225 | 226 | /* rubik-900italic - latin */ 227 | @font-face { 228 | font-family: 'Rubik'; 229 | font-style: italic; 230 | font-display: swap; 231 | font-weight: 900; 232 | src: 233 | local('Rubik Black italic'), 234 | local('Rubik-Blackitalic'), 235 | url('./fonts/rubik-latin-900italic.woff2') format('woff2'), /* Super Modern Browsers */ 236 | url('./fonts/rubik-latin-900italic.woff') format('woff'); /* Modern Browsers */ 237 | } 238 | 239 | -------------------------------------------------------------------------------- /src/routes/blog/posts/markdown-test.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown Test Page 3 | date: 2019-06-16T12:51:00.000Z 4 | --- 5 | 6 | A sample page with the most common elements of an article, including headings, paragraphs, lists, and images. 7 | Use it as a starting point for applying your own styles. 8 | 9 | 10 | 11 | This page is an adapted version of [markdown-test-page](https://github.com/fullpipe/markdown-test-page). It should give you an idea of how different elements are styled on this template. 12 | 13 | ##
Table of Contents 14 | 15 | - [Headings](#Headings) 16 | - [Paragraphs](#Paragraphs) 17 | - [Blockquotes](#Blockquotes) 18 | - [Lists](#Lists) 19 | - [Horizontal rule](#Horizontal) 20 | - [Table](#Table) 21 | - [Code](#Code) 22 | - [Inline elements](#Inline) 23 | 24 | --- 25 | 26 | # Headings 27 | 28 | # Heading one 29 | 30 | Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat commodo id sunt. Nostrud enim ad commodo incididunt cupidatat in ullamco ullamco Lorem cupidatat velit enim et Lorem. Ut laborum cillum laboris fugiat culpa sint irure do reprehenderit culpa occaecat. Exercitation esse mollit tempor magna aliqua in occaecat aliquip veniam reprehenderit nisi dolor in laboris dolore velit. 31 | 32 | ## Heading two 33 | 34 | Aute officia nulla deserunt do deserunt cillum velit magna. Officia veniam culpa anim minim dolore labore pariatur voluptate id ad est duis quis velit dolor pariatur enim. Incididunt enim excepteur do veniam consequat culpa do voluptate dolor fugiat ad adipisicing sit. Labore officia est adipisicing dolore proident eiusmod exercitation deserunt ullamco anim do occaecat velit. Elit dolor consectetur proident sunt aliquip est do tempor quis aliqua culpa aute. Duis in tempor exercitation pariatur et adipisicing mollit irure tempor ut enim esse commodo laboris proident. Do excepteur laborum anim esse aliquip eu sit id Lorem incididunt elit irure ea nulla dolor et. Nulla amet fugiat qui minim deserunt enim eu cupidatat aute officia do velit ea reprehenderit. 35 | 36 | ### Heading three 37 | 38 | Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint. Adipisicing ipsum consectetur proident ullamco magna sit amet aliqua aute fugiat laborum exercitation duis et. 39 | 40 | #### Heading four 41 | 42 | Commodo fugiat aliqua minim quis pariatur mollit id tempor. Non occaecat minim esse enim aliqua adipisicing nostrud duis consequat eu adipisicing qui. Minim aliquip sit excepteur ipsum consequat laborum pariatur excepteur. Veniam fugiat et amet ad elit anim laborum duis mollit occaecat et et ipsum et reprehenderit. Occaecat aliquip dolore adipisicing sint labore occaecat officia fugiat. Quis adipisicing exercitation exercitation eu amet est laboris sunt nostrud ipsum reprehenderit ullamco. Enim sint ut consectetur id anim aute voluptate exercitation mollit dolore magna magna est Lorem. Ut adipisicing adipisicing aliqua ullamco voluptate labore nisi tempor esse magna incididunt. 43 | 44 | ##### Heading five 45 | 46 | Veniam enim esse amet veniam deserunt laboris amet enim consequat. Minim nostrud deserunt cillum consectetur commodo eu enim nostrud ullamco occaecat excepteur. Aliquip et ut est commodo enim dolor amet sint excepteur. Amet ad laboris laborum deserunt sint sunt aliqua commodo ex duis deserunt enim est ex labore ut. Duis incididunt velit adipisicing non incididunt adipisicing adipisicing. Ad irure duis nisi tempor eu dolor fugiat magna et consequat tempor eu ex dolore. Mollit esse nisi qui culpa ut nisi ex proident culpa cupidatat cillum culpa occaecat anim. Ut officia sit ea nisi ea excepteur nostrud ipsum et nulla. 47 | 48 | ###### Heading six 49 | 50 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 51 | 52 | [[Top]](#top) 53 | 54 | # Paragraphs 55 | 56 | Incididunt ex adipisicing ea ullamco consectetur in voluptate proident fugiat tempor deserunt reprehenderit ullamco id dolore laborum. Do laboris laboris minim incididunt qui consectetur exercitation adipisicing dolore et magna consequat magna anim sunt. Officia fugiat Lorem sunt pariatur incididunt Lorem reprehenderit proident irure. Dolore ipsum aliqua mollit ad officia fugiat sit eu aliquip cupidatat ipsum duis laborum laborum fugiat esse. Voluptate anim ex dolore deserunt ea ex eiusmod irure. Occaecat excepteur aliqua exercitation aliquip dolor esse eu eu. 57 | 58 | Officia dolore laborum aute incididunt commodo nisi velit est est elit et dolore elit exercitation. Enim aliquip magna id ipsum aliquip consectetur ad nulla quis. Incididunt pariatur dolor consectetur cillum enim velit cupidatat laborum quis ex. 59 | 60 | Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in. Ex labore nulla velit officia ullamco Lorem Lorem id do. Dolore ullamco ipsum magna dolor pariatur voluptate ipsum id occaecat ipsum. Dolore tempor quis duis commodo quis quis enim. 61 | 62 | [[Top]](#top) 63 | 64 | # Blockquotes 65 | 66 | Ad nisi laborum aute cupidatat magna deserunt eu id laboris id. Aliquip nulla cupidatat sint ex Lorem mollit laborum dolor amet est ut esse aute. Nostrud ex consequat id incididunt proident ipsum minim duis aliqua ut ex et ad quis. Laborum sint esse cillum anim nulla cillum consectetur aliqua sit. Nisi excepteur cillum labore amet excepteur commodo enim occaecat consequat ipsum proident exercitation duis id in. 67 | 68 | > Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure. Eiusmod amet nulla voluptate velit culpa et aliqua ad reprehenderit sit ut. 69 | 70 | Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit. Dolore ad esse commodo et dolore amet est velit ut nisi ea. Excepteur ea nulla commodo dolore anim dolore adipisicing eiusmod labore id enim esse quis mollit deserunt est. Minim ea culpa voluptate nostrud commodo proident in duis aliquip minim. 71 | 72 | > Qui est sit et reprehenderit aute est esse enim aliqua id aliquip ea anim. Pariatur sint reprehenderit mollit velit voluptate enim consectetur sint enim. Quis exercitation proident elit non id qui culpa dolore esse aliquip consequat. 73 | 74 | Ipsum excepteur cupidatat sunt minim ad eiusmod tempor sit. 75 | 76 | > Deserunt excepteur adipisicing culpa pariatur cillum laboris ullamco nisi fugiat cillum officia. In cupidatat nulla aliquip tempor ad Lorem Lorem quis voluptate officia consectetur pariatur ex in est duis. Mollit id esse est elit exercitation voluptate nostrud nisi laborum magna dolore dolore tempor in est consectetur. 77 | 78 | Adipisicing voluptate ipsum culpa voluptate id aute laboris labore esse fugiat veniam ullamco occaecat do ut. Tempor et esse reprehenderit veniam proident ipsum irure sit ullamco et labore ea excepteur nulla labore ut. Ex aute minim quis tempor in eu id id irure ea nostrud dolor esse. 79 | 80 | [[Top]](#top) 81 | 82 | # Lists 83 | 84 | ### Ordered List 85 | 86 | 1. Longan 87 | 2. Lychee 88 | 3. Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis. 89 | Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore. 90 | 4. Marionberry 91 | 5. Melon 92 | - Cantaloupe 93 | - Honeydew 94 | - Watermelon 95 | 6. Miracle fruit 96 | 7. Mulberry 97 | 98 | ### Unordered List 99 | 100 | - Olive 101 | - Orange 102 | - Blood orange 103 | - Clementine 104 | - Papaya 105 | - Ut aute ipsum occaecat nisi culpa Lorem id occaecat cupidatat id id magna laboris ad duis. Fugiat cillum dolore veniam nostrud proident sint consectetur eiusmod irure adipisicing. 106 | - Passionfruit 107 | 108 | [[Top]](#top) 109 | 110 | # Horizontal rule 111 | 112 | In dolore velit aliquip labore mollit minim tempor veniam eu veniam ad in sint aliquip mollit mollit. Ex occaecat non deserunt elit laborum sunt tempor sint consequat culpa culpa qui sit. Irure ad commodo eu voluptate mollit cillum cupidatat veniam proident amet minim reprehenderit. 113 | 114 | --- 115 | 116 | In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi. Dolor amet cillum excepteur consequat sint non sint. 117 | 118 | [[Top]](#top) 119 | 120 | # Table 121 | 122 | Duis sunt ut pariatur reprehenderit mollit mollit magna dolore in pariatur nulla commodo sit dolor ad fugiat. Laboris amet ea occaecat duis eu enim exercitation deserunt ea laborum occaecat reprehenderit. Et incididunt dolor commodo consequat mollit nisi proident non pariatur in et incididunt id. Eu ut et Lorem ea ex magna minim ipsum ipsum do. 123 | 124 | | Table Heading 1 | Table Heading 2 | Center align | Right align | Table Heading 5 | 125 | | :-------------- | :-------------- | :----------: | ----------: | :-------------- | 126 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 127 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 128 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 129 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 130 | | Item 1 | Item 2 | Item 3 | Item 4 | Item 5 | 131 | 132 | Minim id consequat adipisicing cupidatat laborum culpa veniam non consectetur et duis pariatur reprehenderit eu ex consectetur. Sunt nisi qui eiusmod ut cillum laborum Lorem officia aliquip laboris ullamco nostrud laboris non irure laboris. Cillum dolore labore Lorem deserunt mollit voluptate esse incididunt ex dolor. 133 | 134 | [[Top]](#top) 135 | 136 | # Code 137 | 138 | ## Inline code 139 | 140 | Ad amet irure est magna id mollit Lorem in do duis enim. Excepteur velit nisi magna ea pariatur pariatur ullamco fugiat deserunt sint non sint. Duis duis est `code in text` velit velit aute culpa ex quis pariatur pariatur laborum aute pariatur duis tempor sunt ad. Irure magna voluptate dolore consectetur consectetur irure esse. Anim magna `in culpa qui officia` dolor eiusmod esse amet aute cupidatat aliqua do id voluptate cupidatat reprehenderit amet labore deserunt. 141 | 142 | ## Highlighted 143 | 144 | Et fugiat ad nisi amet magna labore do cillum fugiat occaecat cillum Lorem proident. In sint dolor ullamco ad do adipisicing amet id excepteur Lorem aliquip sit irure veniam laborum duis cillum. Aliqua occaecat minim cillum deserunt magna sunt laboris do do irure ea nostrud consequat ut voluptate ex. 145 | 146 | ```html 147 |
148 |
149 | Woman paying for a purchase 154 |
155 |
156 |
157 | Marketing 158 |
159 | Finding customers for your new business 164 |

165 | Getting a new business off the ground is a lot of hard work. Here are five 166 | ideas you can use to find your first customers. 167 |

168 |
169 |
170 | ``` 171 | 172 | Ex amet id ex aliquip id do laborum excepteur exercitation elit sint commodo occaecat nostrud est. Nostrud pariatur esse veniam laborum non sint magna sit laboris minim in id. Aliqua pariatur pariatur excepteur adipisicing irure culpa consequat commodo et ex id ad. 173 | 174 | ```html 175 | 182 | 183 | 186 | ``` 187 | 188 | [[Top]](#top) 189 | 190 | # Inline elements 191 | 192 | Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. Ullamco ad laborum ea qui et anim in laboris exercitation tempor sit officia laborum reprehenderit culpa velit quis. **Consequat commodo** reprehenderit duis [irure](#) esse esse exercitation minim enim Lorem dolore duis irure. Nisi Lorem reprehenderit ea amet excepteur dolor excepteur magna labore proident voluptate ipsum. Reprehenderit ex esse deserunt aliqua ea officia mollit Lorem nulla magna enim. Et ad ipsum labore enim ipsum **cupidatat consequat**. Commodo non ea cupidatat magna deserunt dolore ipsum velit nulla elit veniam nulla eiusmod proident officia. 193 | 194 | ![Super wide](https://placekitten.com/1280/800) 195 | 196 | _Proident sit veniam in est proident officia adipisicing_ ea tempor cillum non cillum velit deserunt. Voluptate laborum incididunt sit consectetur Lorem irure incididunt voluptate nostrud. Commodo ut eiusmod tempor cupidatat esse enim minim ex anim consequat. Mollit sint culpa qui laboris quis consectetur ad sint esse. Amet anim anim minim ullamco et duis non irure. Sit tempor adipisicing ea laboris `culpa ex duis sint` anim aute reprehenderit id eu ea. Aute [excepteur proident](#) Lorem minim adipisicing nostrud mollit ad ut voluptate do nulla esse occaecat aliqua sint anim. 197 | 198 | ![Not so big](https://placekitten.com/480/400) 199 | 200 | Incididunt in culpa cupidatat mollit cillum qui proident sit. In cillum aliquip incididunt voluptate magna amet cupidatat cillum pariatur sint aliqua est _enim **anim** voluptate_. Magna aliquip proident incididunt id duis pariatur eiusmod incididunt commodo culpa dolore sit. Culpa do nostrud elit ad exercitation anim pariatur non minim nisi **adipisicing sunt _officia_**. Do deserunt magna mollit Lorem commodo ipsum do cupidatat mollit enim ut elit veniam ea voluptate. 201 | 202 | 203 | 204 | Reprehenderit non eu quis in ad elit esse qui aute id [incididunt](#) dolore cillum. Esse laboris consequat dolor anim exercitation tempor aliqua deserunt velit magna laboris. Culpa culpa minim duis amet mollit do quis amet commodo nulla irure. 205 | -------------------------------------------------------------------------------- /static/undraw-illustration.svg: -------------------------------------------------------------------------------- 1 | voice interface --------------------------------------------------------------------------------