├── .nvmrc ├── .npmrc ├── src ├── routes │ ├── +page.ts │ ├── +page.svelte │ ├── settings │ │ └── +page.svelte │ └── +layout.svelte ├── lib │ ├── index.ts │ ├── stores │ │ ├── load.ts │ │ ├── tab.ts │ │ └── stackedMessages.ts │ ├── random.ts │ ├── ScrollableView.svelte │ ├── reset.css │ ├── Nav.svelte │ ├── Message.svelte │ ├── OnlineBanner.svelte │ ├── StackedMessages.svelte │ └── MessageWithComments.svelte ├── app.d.ts ├── app.html └── service-worker.ts ├── .gitattributes ├── .prettierignore ├── static ├── favicon.png ├── icon512_rounded.png ├── icon512_maskable.png ├── manifest.json └── avatars │ ├── 7.svg │ ├── 14.svg │ ├── 18.svg │ ├── 2.svg │ ├── 17.svg │ ├── 1.svg │ ├── 15.svg │ ├── 8.svg │ ├── 4.svg │ ├── 12.svg │ ├── 5.svg │ ├── 3.svg │ ├── 19.svg │ ├── 9.svg │ ├── 6.svg │ ├── 11.svg │ ├── 10.svg │ ├── 13.svg │ ├── 16.svg │ └── 20.svg ├── vite.config.ts ├── .prettierrc ├── .gitignore ├── tsconfig.json ├── svelte.config.js ├── eslint.config.js ├── generate-avatars.js ├── package.json └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/routes/+page.ts: -------------------------------------------------------------------------------- 1 | export const ssr = false; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khromov/sparklepost/HEAD/static/favicon.png -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /static/icon512_rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khromov/sparklepost/HEAD/static/icon512_rounded.png -------------------------------------------------------------------------------- /static/icon512_maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khromov/sparklepost/HEAD/static/icon512_maskable.png -------------------------------------------------------------------------------- /src/lib/stores/load.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const spaNavigation = writable(false); -------------------------------------------------------------------------------- /src/lib/stores/tab.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const tabs = ['For you', 'Following', 'Svelte Society']; 4 | export const activeTabIndex = writable(0); -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()] 6 | }); 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/stores/stackedMessages.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | import type { Writable } from 'svelte/store'; 3 | 4 | export const componentsStore: Writable> = writable([]); 5 | 6 | componentsStore.subscribe((components) => { 7 | // console.log('componentsStore:', components); 8 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | /.svelte-kit 7 | /build 8 | 9 | # OS 10 | .DS_Store 11 | Thumbs.db 12 | 13 | # Env 14 | .env 15 | .env.* 16 | !.env.example 17 | !.env.test 18 | 19 | # Vite 20 | vite.config.js.timestamp-* 21 | vite.config.ts.timestamp-* 22 | 23 | codebase.md -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | interface PageState { 9 | stackedComponents: Array<{ componentName: any; props: any }>; 10 | } 11 | // interface Platform {} 12 | } 13 | 14 | interface Document { 15 | startViewTransition(updateCallback: () => Promise): ViewTransition; 16 | } 17 | } 18 | 19 | export {}; 20 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | %sveltekit.head% 12 | 13 | 14 |
%sveltekit.body%
15 | 16 | 17 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#000", 3 | "background_color": "#000", 4 | "icons": [ 5 | { 6 | "purpose": "maskable", 7 | "sizes": "512x512", 8 | "src": "icon512_maskable.png", 9 | "type": "image/png" 10 | }, 11 | { "purpose": "any", "sizes": "512x512", "src": "icon512_rounded.png", "type": "image/png" } 12 | ], 13 | "orientation": "portrait", 14 | "display": "standalone", 15 | "dir": "auto", 16 | "lang": "en-US", 17 | "name": "SparklePost", 18 | "short_name": "SparklePost", 19 | "start_url": "/" 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 15 | // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files 16 | // 17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 18 | // from the referenced tsconfig.json - TypeScript does not merge them in 19 | } 20 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | compilerOptions: { 10 | runes: true 11 | }, 12 | kit: { 13 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 14 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 15 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 16 | adapter: adapter() 17 | } 18 | }; 19 | 20 | export default config; 21 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import ts from 'typescript-eslint'; 3 | import svelte from 'eslint-plugin-svelte'; 4 | import prettier from 'eslint-config-prettier'; 5 | import globals from 'globals'; 6 | 7 | /** @type {import('eslint').Linter.FlatConfig[]} */ 8 | export default [ 9 | js.configs.recommended, 10 | ...ts.configs.recommended, 11 | ...svelte.configs['flat/recommended'], 12 | prettier, 13 | ...svelte.configs['flat/prettier'], 14 | { 15 | languageOptions: { 16 | globals: { 17 | ...globals.browser, 18 | ...globals.node 19 | } 20 | } 21 | }, 22 | { 23 | files: ['**/*.svelte'], 24 | languageOptions: { 25 | parserOptions: { 26 | parser: ts.parser 27 | } 28 | } 29 | }, 30 | { 31 | ignores: ['build/', '.svelte-kit/', 'dist/'] 32 | } 33 | ]; 34 | -------------------------------------------------------------------------------- /src/lib/random.ts: -------------------------------------------------------------------------------- 1 | export function generateRandomComments(minCount = 10, maxCount = 20) { 2 | const commenters = ['Alex', 'Sam', 'Taylor', 'Jordan', 'Casey']; 3 | const contents = [ 4 | 'Interesting point!', 5 | 'I disagree, actually.', 6 | 'Can you elaborate on that?', 7 | 'This thread is getting deep!', 8 | 'I love this discussion!' 9 | ]; 10 | 11 | return Array.from({ length: Math.floor(Math.random() * (maxCount - minCount)) + minCount }, (_, index) => ({ 12 | name: commenters[Math.floor(Math.random() * commenters.length)], 13 | handle: `@user${Math.floor(Math.random() * 1000)}`, 14 | time: `${Math.floor(Math.random() * 59) + 1}m`, 15 | content: contents[Math.floor(Math.random() * contents.length)], 16 | avatarSeed: `comment-${index}-${Date.now()}` 17 | })); 18 | }; -------------------------------------------------------------------------------- /src/lib/ScrollableView.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 |
23 | {#if children} 24 | {@render children()} 25 | {/if} 26 |
27 | 28 | 34 | -------------------------------------------------------------------------------- /src/lib/reset.css: -------------------------------------------------------------------------------- 1 | /* https://www.joshwcomeau.com/css/custom-css-reset/ */ 2 | /* 3 | 1. Use a more-intuitive box-sizing model. 4 | */ 5 | *, *::before, *::after { 6 | box-sizing: border-box; 7 | } 8 | /* 9 | 2. Remove default margin 10 | */ 11 | * { 12 | margin: 0; 13 | } 14 | /* 15 | Typographic tweaks! 16 | 3. Add accessible line-height 17 | 4. Improve text rendering 18 | */ 19 | body { 20 | line-height: 1.5; 21 | -webkit-font-smoothing: antialiased; 22 | } 23 | /* 24 | 5. Improve media defaults 25 | */ 26 | img, picture, video, canvas, svg { 27 | display: block; 28 | max-width: 100%; 29 | } 30 | /* 31 | 6. Remove built-in form typography styles 32 | */ 33 | input, button, textarea, select { 34 | font: inherit; 35 | } 36 | /* 37 | 7. Avoid text overflows 38 | */ 39 | p, h1, h2, h3, h4, h5, h6 { 40 | overflow-wrap: break-word; 41 | } 42 | /* 43 | 8. Create a root stacking context 44 | */ 45 | #root, #__next { 46 | isolation: isolate; 47 | } -------------------------------------------------------------------------------- /generate-avatars.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import * as core from '@dicebear/core'; 5 | import * as collection from '@dicebear/collection'; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | const outputDir = path.join(__dirname, 'static', 'avatars'); 11 | 12 | // Ensure the output directory exists 13 | if (!fs.existsSync(outputDir)) { 14 | fs.mkdirSync(outputDir, { recursive: true }); 15 | } 16 | 17 | // Generate 20 avatars 18 | for (let i = 1; i <= 20; i++) { 19 | const avatar = core.createAvatar(collection.micah, { 20 | seed: `avatar-${i}-2`, 21 | size: 128, 22 | backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'], 23 | baseColor: ['fff'], 24 | mouth: ['smile', 'smirk'] 25 | }); 26 | 27 | const svg = avatar.toString(); 28 | 29 | fs.writeFileSync(path.join(outputDir, `${i}.svg`), svg); 30 | console.log(`Generated avatar ${i}.svg`); 31 | } 32 | 33 | console.log('Avatar generation complete!'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app-transitions-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "lint": "prettier --check . && eslint .", 12 | "format": "prettier --write ." 13 | }, 14 | "devDependencies": { 15 | "@dicebear/collection": "^9.2.0", 16 | "@dicebear/core": "^9.2.0", 17 | "@sveltejs/adapter-auto": "^3.2.2", 18 | "@sveltejs/kit": "^2.5.18", 19 | "@sveltejs/vite-plugin-svelte": "^3.1.1", 20 | "@types/eslint": "^9.6.0", 21 | "animal-avatar-generator": "^1.2.0", 22 | "eslint": "^9.7.0", 23 | "eslint-config-prettier": "^9.1.0", 24 | "eslint-plugin-svelte": "^2.43.0", 25 | "globals": "^15.8.0", 26 | "prettier": "^3.3.3", 27 | "prettier-plugin-svelte": "^3.2.6", 28 | "svelte": "^5.0.0-next.200", 29 | "svelte-check": "^3.8.4", 30 | "swiper": "^11.1.7", 31 | "tslib": "^2.6.3", 32 | "typescript": "^5.5.4", 33 | "typescript-eslint": "^8.0.0-alpha.20", 34 | "vite": "^5.4.6" 35 | }, 36 | "type": "module" 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/Nav.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 21 | 22 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SparklePost 2 | 3 | SparklePost is a demo application that implements interface patterns commonly found in native iOS and Android apps, using modern and performant web development techniques. It is written in SvelteKit, but the same techniques can be implemented in any JavaScript framework. 4 | 5 | Try the demo yourself at: https://sparklepost.vercel.app/ (works best on mobile) 6 | 7 | ![Screen Recording 2024-07-28 at 04 18 14](https://github.com/user-attachments/assets/0054ed3d-c744-4d5d-bdf2-284c6bf8c5a8) 8 | 9 | ## Features 10 | 11 | * Swipeable tabs navigation with maintained scroll position in each tab (SwiperJS) 12 | * Stacked message threads with back support - click on a single tweet to initiate stack (SvelteKit Shallow Routing and partial DOM rendering) 13 | * Scroll & tab restoration when using back navigation (SvelteKit Snapshots) 14 | * Installation & Offline support (PWA, Service Worker & online/offline event) 15 | * Native-like navigation transitions (View Transitions API) 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | nvm use 23 | npm run dev 24 | 25 | # or start the server and open the app in a new browser tab 26 | npm run dev -- --open 27 | ``` 28 | 29 | ## Building 30 | 31 | To create a production version of your app: 32 | 33 | ```bash 34 | npm run build 35 | ``` 36 | 37 | You can preview the production build with `npm run preview`. 38 | 39 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. 40 | -------------------------------------------------------------------------------- /src/lib/Message.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
25 | Avatar 26 |
27 |
28 | {name} 29 | {handle} 30 | · {time} 31 |
32 |

{content}

33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /src/lib/OnlineBanner.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | {#if !isOnline} 24 |
25 |
26 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | You are currently offline 46 |
47 |
48 | {/if} 49 | 50 | 75 | -------------------------------------------------------------------------------- /src/lib/StackedMessages.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 |
32 | {#each topComponents as layer, index (components.length - index - 1)} 33 |
39 | 40 | 44 |
45 | {/each} 46 |
47 | 48 | 78 | -------------------------------------------------------------------------------- /src/service-worker.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | const sw = self as unknown as ServiceWorkerGlobalScope; 7 | 8 | import { build, files, version } from '$service-worker'; 9 | 10 | // Create a unique cache name for this deployment 11 | const CACHE = `cache-${version}`; 12 | 13 | const ASSETS = [ 14 | ...build, // the app itself 15 | ...files // everything in `static` 16 | ]; 17 | 18 | sw.addEventListener('install', (event) => { 19 | // Create a new cache and add all files to it 20 | async function addFilesToCache() { 21 | const cache = await caches.open(CACHE); 22 | await cache.addAll(ASSETS); 23 | } 24 | 25 | event.waitUntil(addFilesToCache()); 26 | }); 27 | 28 | sw.addEventListener('activate', (event) => { 29 | // Remove previous cached data from disk 30 | async function deleteOldCaches() { 31 | for (const key of await caches.keys()) { 32 | if (key !== CACHE) await caches.delete(key); 33 | } 34 | } 35 | 36 | event.waitUntil(deleteOldCaches()); 37 | }); 38 | 39 | sw.addEventListener('fetch', (event) => { 40 | // ignore POST requests etc 41 | if (event.request.method !== 'GET') return; 42 | 43 | async function respond() { 44 | const url = new URL(event.request.url); 45 | const cache = await caches.open(CACHE); 46 | 47 | // `build`/`files` can always be served from the cache 48 | if (ASSETS.includes(url.pathname)) { 49 | const response = await cache.match(url.pathname); 50 | 51 | if (response) { 52 | return response; 53 | } 54 | } 55 | 56 | // for everything else, try the network first, but 57 | // fall back to the cache if we're offline 58 | try { 59 | const response = await fetch(event.request); 60 | 61 | // if we're offline, fetch can return a value that is not a Response 62 | // instead of throwing - and we can't pass this non-Response to respondWith 63 | if (!(response instanceof Response)) { 64 | throw new Error('invalid response from fetch'); 65 | } 66 | 67 | if (response.status === 200) { 68 | cache.put(event.request, response.clone()); 69 | } 70 | 71 | return response; 72 | } catch (err) { 73 | const response = await cache.match(event.request); 74 | 75 | if (response) { 76 | return response; 77 | } 78 | 79 | // if there's no cache, then just error out 80 | // as there is nothing we can do to respond to this request 81 | throw err; 82 | } 83 | } 84 | 85 | event.respondWith(respond()); 86 | }); -------------------------------------------------------------------------------- /src/lib/MessageWithComments.svelte: -------------------------------------------------------------------------------- 1 | 60 | 61 |
62 |
63 | Avatar 64 |
65 |
66 | {tweet.name} 67 | {tweet.handle} 68 | · {tweet.time} 69 |
70 |

{tweet.content}

71 |
72 |
73 | 74 |
75 | 76 | 77 | {#each comments as comment} 78 |
handleCommentClick(comment)}> 79 | Avatar 80 |
81 |
82 | {comment.name} 83 | {comment.handle} 84 | · {comment.time} 85 |
86 |

{comment.content}

87 |
88 |
89 | {/each} 90 |
91 |
92 | 93 | 144 | -------------------------------------------------------------------------------- /static/avatars/7.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/14.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/18.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/2.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/17.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 100 | 101 |
102 |
0}> 103 |
104 | {#each Array(3) as _, i} 105 |
106 | 107 | {#each Array(10) as _, x} 108 | 109 | 110 |
112 | handleMessageClick({ 113 | name: 'User', 114 | handle: '@user', 115 | time: '1h', 116 | content: `Message ${x + 1} in tab ${i + 1}`, 117 | avatarSeed: `${i}-${x}` 118 | })} 119 | > 120 | 121 |
122 | {/each} 123 |
124 |
125 | {/each} 126 |
127 |
128 | 129 |
130 | 131 | 143 | -------------------------------------------------------------------------------- /static/avatars/1.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/15.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/8.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/4.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/12.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /src/routes/settings/+page.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 |
33 |

Settings

34 | 35 |
36 | 37 | 38 |
39 | 40 |
41 | 42 | 43 |
44 | 45 |
46 | 50 |
51 | 52 |
53 |

Notifications

54 | 58 | 62 |
63 | 64 |
65 | 66 | 72 |
73 | 74 |
75 | 76 | 81 |
82 | 83 |
84 |

Really long text so you can test scroll restoration!

85 |

86 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam lectus felis, finibus a lectus ac, tempus pellentesque justo. Proin sed commodo mauris. Aliquam quis nisi tristique ipsum porta ultrices vitae sit amet velit. Fusce sapien nisi, laoreet ac varius id, commodo vel enim. Aenean porta eget nisl quis condimentum. Proin congue sit amet nulla ut tincidunt. Ut nunc nibh, dapibus id sodales eu, iaculis quis felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc porta, diam et tincidunt molestie, nulla lacus luctus purus, nec convallis justo lacus at nibh. Suspendisse dignissim tortor ut odio consectetur semper. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. 87 |

88 |
89 | 90 | 91 |
92 | 93 | -------------------------------------------------------------------------------- /static/avatars/5.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/3.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/19.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/9.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/6.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/11.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/10.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/13.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/16.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /static/avatars/20.svg: -------------------------------------------------------------------------------- 1 | Avatar Illustration SystemMicah Lanierhttps://www.figma.com/community/file/829741575478342595https://creativecommons.org/licenses/by/4.0/Remix of „Avatar Illustration System” (https://www.figma.com/community/file/829741575478342595) by „Micah Lanier”, licensed under „CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 |
60 |
61 |
62 | Profile 63 |
64 | 67 | 68 |
69 | 70 |
71 | 72 | {#if $page.url.pathname === '/'} 73 |
91 | 92 | 265 | --------------------------------------------------------------------------------