├── .env.example ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── posts ├── animated-code-blocks-using-shiki │ └── animated-code-blocks-using-shiki.md ├── animation-with-svelte │ ├── animation-with-svelte.md │ └── images │ │ ├── animate-flip.mp4 │ │ ├── crossfade.webp │ │ ├── flip-gsap.mp4 │ │ ├── flip-svelte.mp4 │ │ ├── flip.webp │ │ ├── motion-spring.mp4 │ │ ├── motion-tweened.mp4 │ │ ├── transitions-custom-css.mp4 │ │ ├── transitions-custom-js.mp4 │ │ ├── transitions-deferred.mp4 │ │ ├── transitions-examples.mp4 │ │ ├── transitions-fly.mp4 │ │ ├── transitions-key.mp4 │ │ ├── transitions-local.mp4 │ │ └── tweet.webp ├── avoid-async-effects-in-svelte │ └── avoid-async-effects-in-svelte.md ├── avoid-flashing-iframe │ ├── avoid-flashing-iframe.md │ └── images │ │ ├── iframe-flashing.mp4 │ │ └── iframe-without-flashing.mp4 ├── avoid-sharing-server-and-client-state-in-sveltekit │ └── avoid-sharing-server-and-client-state-in-sveltekit.md ├── beautiful-presentations-with-svelte │ └── beautiful-presentations-with-svelte.md ├── blazing-fast-sveltekit-search │ └── blazing-fast-sveltekit-search.md ├── catch-errors-during-rendering-with-svelte-error-boundaries │ └── catch-errors-during-rendering-with-svelte-error-boundaries.md ├── clean-git-history │ ├── clean-git-history.md │ └── images │ │ ├── feature-behind-rebase.png │ │ ├── feature-behind-upstream.png │ │ ├── first-commits.png │ │ ├── first-merge.png │ │ ├── rebase-checkout.png │ │ ├── rebase-commit-change.png │ │ ├── rebase-commit.png │ │ ├── rebase-complete.png │ │ ├── rebase-open.png │ │ └── vs-code.png ├── create-a-coding-sandbox │ ├── create-a-coding-sandbox.md │ └── images │ │ ├── codepen.webp │ │ ├── diagram.webp │ │ ├── error.webp │ │ ├── iframe.webp │ │ └── source.webp ├── create-a-markdown-blog │ ├── create-a-markdown-blog.md │ └── images │ │ ├── layout.webp │ │ ├── mdx-component.webp │ │ ├── next-page-render.webp │ │ ├── site-diagram.webp │ │ ├── sorted-posts.webp │ │ └── vercel.webp ├── creating-content │ ├── creating-content.md │ └── images │ │ ├── audio-filters.webp │ │ ├── editing.mp4 │ │ ├── interface.webp │ │ ├── macro.mp4 │ │ ├── microphone.webp │ │ ├── obs-1.webp │ │ ├── obs-2.webp │ │ ├── obs-3.webp │ │ ├── obs-4.webp │ │ ├── shure.webp │ │ └── video.mp4 ├── css-focus-outline │ ├── css-focus-outline.md │ └── images │ │ ├── border.gif │ │ ├── box-shadow.gif │ │ ├── default.gif │ │ ├── no-outline.gif │ │ └── outline.gif ├── dark-mode-favicon │ ├── dark-mode-favicon.md │ └── images │ │ ├── comparison.webp │ │ ├── link-media.webp │ │ ├── prefers-color-scheme.webp │ │ ├── safari.webp │ │ └── svg-favicons.webp ├── design-for-developers │ ├── design-for-developers.md │ └── images │ │ ├── alignment-bad.webp │ │ ├── alignment-good.webp │ │ ├── alignment-grid.webp │ │ ├── alignment-spacing.webp │ │ ├── apple-grid.webp │ │ ├── color-contrast-bad.webp │ │ ├── color-contrast-good.webp │ │ ├── color-palette.webp │ │ ├── color-picker.webp │ │ ├── color-rule.webp │ │ ├── color-wheel.webp │ │ ├── contrast-checker.webp │ │ ├── proximity-outline.webp │ │ ├── proximity.webp │ │ ├── repetition-scrolling-area.webp │ │ ├── repetition-scrolling-heading.webp │ │ ├── repetition.webp │ │ ├── scroll-more-idiot.webp │ │ ├── text-contrast.webp │ │ ├── typefaces.webp │ │ ├── typography.webp │ │ ├── wireframe-landing-page.webp │ │ └── wireframe.webp ├── dynamic-social-share-images │ ├── dynamic-social-share-images.md │ └── images │ │ ├── social-card.webp │ │ ├── social-share-image.png │ │ └── social-share.webp ├── global-styles-in-sveltekit │ └── global-styles-in-sveltekit.md ├── great-developers-steal │ ├── great-developers-steal.md │ └── images │ │ ├── search-results.webp │ │ └── spec.webp ├── how-to-optimize-images │ ├── how-to-optimize-images.md │ └── images │ │ └── squoosh.webp ├── how-to-publish-a-javascript-library │ └── how-to-publish-a-javascript-library.md ├── how-to-share-state-in-svelte-5 │ └── how-to-share-state-in-svelte-5.md ├── how-to-think-like-a-developer │ └── how-to-think-like-a-developer.md ├── impossible-layout-animations-with-svelte │ ├── diagram │ │ └── flip.excalidraw │ ├── images │ │ ├── circle-flip.mp4 │ │ ├── circle-teleport.mp4 │ │ ├── flip-circles.mp4 │ │ ├── flip-grid.mp4 │ │ ├── flip-movies.mp4 │ │ ├── flip.webp │ │ └── how-flip-works.mp4 │ └── impossible-layout-animations-with-svelte.md ├── intro-to-react │ ├── images │ │ ├── js-update.gif │ │ ├── paint-flashing.webp │ │ └── react-update.gif │ └── intro-to-react.md ├── introduction-to-3d-with-svelte │ ├── images │ │ ├── controls.webp │ │ ├── models.mp4 │ │ ├── scene.webp │ │ ├── sphere.webp │ │ └── spooky.webp │ └── introduction-to-3d-with-svelte.md ├── introduction-to-tailwind-css │ ├── images │ │ └── banding.webp │ └── introduction-to-tailwind-css.md ├── learn-how-sveltekit-works │ ├── images │ │ ├── csr.webp │ │ ├── debugger.webp │ │ ├── hydration.webp │ │ ├── kit.webp │ │ ├── navigation.webp │ │ ├── ssr.webp │ │ ├── sveltekit.webp │ │ └── twitter.webp │ └── learn-how-sveltekit-works.md ├── learn-problem-solving │ ├── images │ │ ├── player.webp │ │ └── todos.webp │ └── learn-problem-solving.md ├── make-a-svelte-component-library │ └── make-a-svelte-component-library.md ├── master-the-svelte-context-api │ ├── images │ │ └── context-api.webp │ └── master-the-svelte-context-api.md ├── next-bundle-size │ ├── images │ │ ├── preact.webp │ │ └── react.webp │ └── next-bundle-size.md ├── react-project-structure │ └── react-project-structure.md ├── responsive-css-grid-layout │ └── responsive-css-grid-layout.md ├── rethink-how-you-write-git-commits │ ├── images │ │ ├── commit.png │ │ ├── confused.webp │ │ ├── git.png │ │ ├── gitmoji.png │ │ └── options.png │ └── rethink-how-you-write-git-commits.md ├── screenshot-page-and-dom-elements │ ├── images │ │ ├── brave-screenshot.webp │ │ └── firefox-screenshot.webp │ └── screenshot-page-and-dom-elements.md ├── simple-css-debug-trick │ ├── images │ │ ├── bookmarklet.webp │ │ └── css-debug.webp │ └── simple-css-debug-trick.md ├── simpler-color-syntax │ └── simpler-color-syntax.md ├── space-elements-with-flexbox │ └── space-elements-with-flexbox.md ├── stacking-items-with-css-grid │ └── stacking-items-with-css-grid.md ├── static-pages-without-javascript │ ├── images │ │ ├── experimental-feature.webp │ │ ├── inspector-with-javascript.webp │ │ ├── inspector-without-javascript.webp │ │ ├── network-with-javascript.webp │ │ └── network-without-javascript.webp │ └── static-pages-without-javascript.md ├── svelte-actions-guide │ └── svelte-actions-guide.md ├── svelte-attachments-explained │ └── svelte-attachments-explained.md ├── svelte-context-with-stores │ └── svelte-context-with-stores.md ├── svelte-for-beginners │ └── svelte-for-beginners.md ├── svelte-for-react-developers │ └── svelte-for-react-developers.md ├── svelte-gui │ └── svelte-gui.md ├── svelte-headless-ui │ ├── images │ │ ├── listbox.webp │ │ └── select.webp │ └── svelte-headless-ui.md ├── svelte-inspector │ ├── images │ │ └── inspector.mp4 │ └── svelte-inspector.md ├── svelte-markdoc-renderer │ └── svelte-markdoc-renderer.md ├── svelte-matching-game │ └── svelte-matching-game.md ├── svelte-preprocessors │ └── svelte-preprocessors.md ├── svelte-state-management │ ├── images │ │ ├── bindings.webp │ │ ├── context.webp │ │ ├── events.webp │ │ ├── guide.webp │ │ ├── module.webp │ │ ├── props.webp │ │ ├── stores.webp │ │ └── url.webp │ └── svelte-state-management.md ├── svelte-stores-guide │ ├── images │ │ ├── observer.webp │ │ └── store.webp │ └── svelte-stores-guide.md ├── svelte-todo-app │ ├── images │ │ ├── filtering-todos-problem.mp4 │ │ ├── filtering-todos-solution.mp4 │ │ ├── todomvc-editing.webp │ │ ├── todomvc-without-styles.webp │ │ └── todos-list.webp │ └── svelte-todo-app.md ├── svelte-typing-game │ ├── images │ │ ├── game.webp │ │ ├── offset.webp │ │ ├── rect.webp │ │ └── state.webp │ └── svelte-typing-game.md ├── svelte-ui-libraries │ └── svelte-ui-libraries.md ├── sveltekit-advanced-layouts │ ├── images │ │ ├── admin-group-layout.webp │ │ ├── admin-layout.webp │ │ ├── layout-reset.webp │ │ ├── page-reset.webp │ │ ├── plume.webp │ │ ├── tags-group-layout.webp │ │ └── tags-layout.webp │ └── sveltekit-advanced-layouts.md ├── sveltekit-authentication-using-cookies │ └── sveltekit-authentication-using-cookies.md ├── sveltekit-data-flow │ ├── cheatsheet │ │ ├── sveltekit-data-flow-dark.svg │ │ └── sveltekit-data-flow-light.svg │ ├── images │ │ └── sveltekit-data-flow.png │ └── sveltekit-data-flow.md ├── sveltekit-deployment │ ├── images │ │ ├── blog.webp │ │ ├── cdn.webp │ │ ├── databases.webp │ │ ├── edge.webp │ │ ├── full-stack.webp │ │ ├── generalist.webp │ │ ├── serverless.webp │ │ └── specialized.webp │ └── sveltekit-deployment.md ├── sveltekit-endpoints │ ├── images │ │ ├── flicker.mp4 │ │ ├── prefetch.webp │ │ ├── sveltekit-endpoints.webp │ │ └── without-flicker.mp4 │ └── sveltekit-endpoints.md ├── sveltekit-environment-variables │ └── sveltekit-environment-variables.md ├── sveltekit-for-beginners │ ├── images │ │ ├── custom-error.webp │ │ ├── elements-tab.webp │ │ ├── home-page.webp │ │ ├── hot-take.webp │ │ ├── javascript-disabled.webp │ │ ├── landing-page.webp │ │ ├── nested-error.webp │ │ ├── network-request.webp │ │ ├── network-tab.webp │ │ ├── settings.webp │ │ ├── social-image.webp │ │ ├── ssr-hydrate.webp │ │ ├── sveltekit.webp │ │ ├── tweets.webp │ │ ├── twittr.mp4 │ │ └── user-profile.webp │ └── sveltekit-for-beginners.md ├── sveltekit-google-analytics │ ├── images │ │ ├── account-setup.webp │ │ ├── data-streams.webp │ │ ├── realtime.webp │ │ ├── set-up-data-stream.webp │ │ ├── tagging-instructions.webp │ │ └── web-stream-details.webp │ └── sveltekit-google-analytics.md ├── sveltekit-hooks │ ├── images │ │ └── hooks.webp │ └── sveltekit-hooks.md ├── sveltekit-loading-data │ ├── images │ │ ├── cached.webp │ │ └── headers.webp │ └── sveltekit-loading-data.md ├── sveltekit-markdown-blog │ ├── images │ │ ├── endpoint.webp │ │ ├── post.webp │ │ ├── posts.webp │ │ ├── setup.webp │ │ └── shiki.webp │ └── sveltekit-markdown-blog.md ├── sveltekit-page-transitions │ └── sveltekit-page-transitions.md ├── sveltekit-progressive-enhancement │ └── sveltekit-progressive-enhancement.md ├── sveltekit-project-structure │ └── sveltekit-project-structure.md ├── sveltekit-routing │ ├── images │ │ ├── error.webp │ │ ├── layout.webp │ │ ├── posts.webp │ │ ├── routing.webp │ │ ├── slot.webp │ │ └── slug.webp │ └── sveltekit-routing.md ├── sveltekit-view-transitions │ └── sveltekit-view-transitions.md ├── sveltekit-web-scraping │ └── sveltekit-web-scraping.md ├── sveltekit-window-is-not-defined │ └── sveltekit-window-is-not-defined.md ├── sveltekit-with-tailwind-css │ └── sveltekit-with-tailwind-css.md ├── sveltify-any-javascript-library │ └── sveltify-any-javascript-library.md ├── svg-animation-library │ └── svg-animation-library.md ├── template-strings-syntax-highlight │ ├── images │ │ └── extension.mp4 │ └── template-strings-syntax-highlight.md ├── test-your-site-in-every-browser │ └── test-your-site-in-every-browser.md ├── testing-for-beginners │ └── testing-for-beginners.md ├── threlte-github-skyline │ └── threlte-github-skyline.md ├── typescript-fundamentals │ ├── images │ │ ├── array-constructor.webp │ │ ├── array-interface-es5.webp │ │ ├── array-interface.webp │ │ ├── built-in-extensions.webp │ │ ├── checking.webp │ │ ├── code-completion.webp │ │ ├── date-constructor.webp │ │ ├── date-string.webp │ │ ├── first-overload-signature.webp │ │ ├── functions-are-objects.webp │ │ ├── hover-type.webp │ │ ├── intellisense.webp │ │ ├── jsdoc.webp │ │ ├── no-checking.webp │ │ ├── pokemon-api.webp │ │ ├── primitives.webp │ │ ├── problems-tab.webp │ │ ├── quicktype.webp │ │ ├── require-to-import.webp │ │ ├── second-overload-signature.webp │ │ ├── signature-call.webp │ │ ├── ts-error.webp │ │ ├── ts-node.webp │ │ ├── ts-server.webp │ │ ├── tsconfig.webp │ │ ├── type-declaration-search.webp │ │ ├── type-definitions.webp │ │ ├── type-search.webp │ │ ├── typescript-compiler.webp │ │ ├── typescript-output.webp │ │ ├── typescript-playground.webp │ │ ├── typescript-setup.webp │ │ └── typescript-watch.webp │ └── typescript-fundamentals.md ├── using-fonts-on-the-web │ ├── images │ │ ├── basic-latin.webp │ │ ├── block.gif │ │ ├── cdn.webp │ │ ├── custom-axis.webp │ │ ├── fallback.gif │ │ ├── google-variable-fonts.webp │ │ ├── inter-variable-subset.webp │ │ ├── inter-variable.webp │ │ ├── optional.gif │ │ ├── preconnect.webp │ │ ├── shared-cache-a.webp │ │ ├── shared-cache-b.webp │ │ ├── similar-font.gif │ │ ├── single-cache-a.webp │ │ ├── single-cache-b.webp │ │ ├── size.webp │ │ ├── swap.gif │ │ ├── unicode.webp │ │ └── variable-fonts-axis.gif │ └── using-fonts-on-the-web.md ├── using-functions-from-another-svelte-component │ └── using-functions-from-another-svelte-component.md ├── using-future-css-in-svelte │ └── using-future-css-in-svelte.md ├── using-iconify-with-svelte │ └── using-iconify-with-svelte.md ├── using-javascript-libraries-in-svelte │ └── using-javascript-libraries-in-svelte.md ├── using-pico-css-with-svelte │ └── using-pico-css-with-svelte.md ├── using-react-libraries-in-svelte │ └── using-react-libraries-in-svelte.md ├── using-sveltekit-endpoints │ ├── images │ │ ├── csr.webp │ │ ├── dynamic.webp │ │ ├── ssr.webp │ │ └── standalone.webp │ └── using-sveltekit-endpoints.md ├── using-tailwind-with-vite │ └── using-tailwind-with-vite.md ├── using-websockets-with-sveltekit │ └── using-websockets-with-sveltekit.md ├── web-development-podcasts │ ├── images │ │ ├── career-chats.webp │ │ ├── chats-with-kent.webp │ │ ├── codepen-radio.webp │ │ ├── compressed.webp │ │ ├── frontend-greatness.webp │ │ ├── full-stack-radio.webp │ │ ├── future-of-coding.webp │ │ ├── html-all-the-things.webp │ │ ├── jamstack-radio.webp │ │ ├── js-party.webp │ │ ├── ladybug-podcast.webp │ │ ├── modern-web.webp │ │ ├── react-podcast.webp │ │ ├── remotely-interesting.webp │ │ ├── shoptalk.webp │ │ ├── syntax.webp │ │ ├── the-changelog.webp │ │ ├── the-css-podcast.webp │ │ ├── the-undefined-podcast.webp │ │ └── toolsday.webp │ └── web-development-podcasts.md ├── what-is-sveltekit │ ├── images │ │ ├── spa.webp │ │ └── ssr.webp │ └── what-is-sveltekit.md └── working-with-forms-in-sveltekit │ └── working-with-forms-in-sveltekit.md ├── src ├── app.d.ts ├── app.html ├── lib │ ├── analytics │ │ └── index.ts │ ├── embed │ │ └── youtube.svelte │ ├── icons │ │ ├── arrow-right.svelte │ │ ├── chevron-double-left.svelte │ │ ├── chevron-double-right.svelte │ │ ├── cog.svelte │ │ ├── envelope.svelte │ │ ├── eye.svelte │ │ ├── github.svelte │ │ ├── heart.svelte │ │ ├── index.ts │ │ ├── mail.svelte │ │ ├── menu.svelte │ │ ├── pencil-square.svelte │ │ ├── rss.svelte │ │ ├── x.svelte │ │ └── youtube.svelte │ ├── markdown │ │ ├── index.js │ │ └── plugins.js │ ├── sfx │ │ └── index.ts │ ├── site │ │ ├── config.ts │ │ ├── plugins.ts │ │ └── posts.ts │ ├── types │ │ └── index.ts │ ├── ui │ │ ├── footer.svelte │ │ ├── header │ │ │ ├── header.svelte │ │ │ ├── logo.svelte │ │ │ ├── menu.svelte │ │ │ ├── preferences │ │ │ │ ├── dyslexic.svelte │ │ │ │ ├── index.svelte │ │ │ │ ├── preferences.svelte.ts │ │ │ │ ├── reading.svelte │ │ │ │ ├── reset.svelte │ │ │ │ └── themes.svelte │ │ │ ├── search │ │ │ │ ├── index.ts │ │ │ │ ├── search-icon.svelte │ │ │ │ ├── search-worker.ts │ │ │ │ ├── search.svelte │ │ │ │ └── search.ts │ │ │ └── socials.svelte │ │ ├── heading.svelte │ │ ├── newsletter.svelte │ │ └── posts.svelte │ └── utils │ │ └── index.ts ├── routes │ ├── +error.svelte │ ├── +layout.server.ts │ ├── +layout.svelte │ ├── +layout.ts │ ├── +page.server.ts │ ├── +page.svelte │ ├── [slug] │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── card.svelte │ │ ├── clipboard.svelte │ │ ├── overlay.svelte │ │ ├── toc.svelte │ │ └── warning.svelte │ ├── about │ │ └── +page.svelte │ ├── api │ │ ├── posts │ │ │ └── +server.ts │ │ ├── search │ │ │ └── +server.ts │ │ └── subscribe │ │ │ └── +server.ts │ ├── archive │ │ ├── +page.server.ts │ │ └── +page.svelte │ ├── categories │ │ └── [category] │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ ├── drafts │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ └── [slug] │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ ├── newsletter │ │ └── +page.svelte │ ├── rss.xml │ │ └── +server.ts │ └── sitemap.xml │ │ └── +server.ts └── styles │ ├── code.css │ ├── fonts.css │ ├── global.css │ ├── post.css │ ├── reset.css │ ├── styles.css │ └── themes.css ├── static ├── assets │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.svg │ ├── icon-192x192.png │ └── icon-256x256.png ├── fonts │ ├── atkinson-hyperlegible │ │ ├── atkinson-hyperlegible-400.woff2 │ │ ├── atkinson-hyperlegible-400i.woff2 │ │ ├── atkinson-hyperlegible-700.woff2 │ │ └── atkinson-hyperlegible-700i.woff2 │ ├── dm-serif │ │ └── dm-serif-400.woff2 │ ├── monaspace-neon │ │ ├── monaspace-neon-400.woff2 │ │ └── monaspace-neon-700.woff2 │ └── openDyslexic │ │ ├── openDyslexic-400.woff2 │ │ ├── openDyslexic-700.woff2 │ │ └── openDyslexicMono-400.woff2 ├── manifest.json ├── robots.txt ├── sfx │ └── click.mp3 └── social.png ├── svelte.config.js ├── tsconfig.json ├── vercel.json └── vite.config.js /.env.example: -------------------------------------------------------------------------------- 1 | # Private 2 | BUTTONDOWN_API_KEY=API_KEY 3 | 4 | # Public -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | /data 10 | /posts 11 | 12 | # Ignore files for PNPM, NPM and YARN 13 | pnpm-lock.yaml 14 | package-lock.json 15 | yarn.lock -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier', 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'], 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true, 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser', 27 | }, 28 | }, 29 | ], 30 | rules: { 31 | '@typescript-eslint/no-explicit-any': 'off', 32 | '@typescript-eslint/ban-ts-comment': 'off', 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | .vercel 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "useTabs": true, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "printWidth": 80, 7 | "plugins": ["prettier-plugin-svelte"], 8 | "pluginSearchDirs": ["."], 9 | "overrides": [ 10 | { 11 | "files": "*.svelte", 12 | "options": { "parser": "svelte" } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to Contribute 2 | 3 | ![Bob Ross mixing colors together using his palette](https://i.giphy.com/media/d31vTpVi1LAcDvdm/giphy.webp) 4 | 5 | If you're contributing for the first time, welcome! 😄 6 | 7 | - You can raise an issue if something isn't working so I can update it 8 | - At the end of each post, there's a direct link to the Markdown file you can edit inside GitHub 9 | - When you're done, submit the pull request 🥳 10 | - Please use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format for your commit messages (e.g. "chore: Fix typo") 11 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: joyofcode 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Joy of Code 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | Bob Ross 4 |
5 |
6 | 7 | ## Joy of Code 8 | 9 | Web development and design-focused content. 10 | 11 | ## ▶️ YouTube ![YouTube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC6wpjLSLn2dhlaDjn6_V0rw) 12 | 13 | You can find my videos on [YouTube](https://www.youtube.com/@joyofcodedev). 14 | 15 | ## 🙏 Support 16 | 17 | You can support my work by [becoming a patron](https://www.patreon.com/joyofcode) starting low as **$1/month**. 18 | 19 | ## 🤗 Contributing 20 | 21 | At the end of each post there's a direct link to the **Markdown** file of the post you can edit within GitHub. Read the [contributing guidelines](CONTRIBUTING.md) to learn more. 22 | 23 | ## 🧭 Uses 24 | 25 | - ⚡️ [SvelteKit](https://kit.svelte.dev/) for the framework and prerendering pages ahead of time making it blazingly fast 🔥 26 | 27 | - The project is hosted on [Vercel](https://vercel.com/) 28 | 29 | - I use [Buttondown](https://buttondown.email/) for the newsletter when I remember to send one 🤭 30 | 31 | ## 📜 Setup 32 | 33 | These instructions are mostly if you want to learn how the code works but in general **I don't accept pull requests that aren't related to posts** but you can always raise an issue. 34 | 35 | The project uses 📦️ [pnpm](https://pnpm.io/) but any package manager should work. 36 | 37 | ### 👬 Clone the project 38 | 39 | ```sh 40 | git clone https://github.com/mattcroat/joy-of-code.git 41 | ``` 42 | 43 | ### ⚙️ Rename `.env.example` to `.env` 44 | 45 | ```text 46 | # Private 47 | BUTTONDOWN_API_KEY=API_KEY 48 | ``` 49 | 50 | ### 📦️ Install the dependencies 51 | 52 | ```sh 53 | pnpm i 54 | ``` 55 | 56 | ### 💿️ Run the development server 57 | 58 | ```sh 59 | pnpm run dev 60 | ``` 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joy-of-code", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "test": "playwright test", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "test:unit": "vitest", 12 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 13 | "format": "prettier --plugin-search-dir . --write ." 14 | }, 15 | "devDependencies": { 16 | "@melt-ui/pp": "^0.3.0", 17 | "@playwright/test": "^1.41.2", 18 | "@shikijs/rehype": "^1.9.0", 19 | "@shikijs/transformers": "^1.9.0", 20 | "@sveltejs/adapter-vercel": "^5.1.0", 21 | "@sveltejs/kit": "^2.5.27", 22 | "@sveltejs/vite-plugin-svelte": "^4.0.0", 23 | "@types/node": "^20.11.17", 24 | "@typescript-eslint/eslint-plugin": "^6.21.0", 25 | "@typescript-eslint/parser": "^6.21.0", 26 | "eslint": "^8.56.0", 27 | "eslint-config-prettier": "^9.1.0", 28 | "eslint-plugin-svelte3": "^4.0.0", 29 | "prettier": "^3.2.5", 30 | "prettier-plugin-svelte": "^3.2.6", 31 | "sass": "^1.70.0", 32 | "svelte": "^5.0.0", 33 | "svelte-check": "^4.0.0", 34 | "svelte-preprocess": "^6.0.0", 35 | "tslib": "^2.6.2", 36 | "typescript": "^5.5.0", 37 | "vite": "^5.4.4" 38 | }, 39 | "dependencies": { 40 | "@melt-ui/svelte": "^0.73.0", 41 | "flexsearch": "^0.7.43", 42 | "gray-matter": "^4.0.3", 43 | "lite-youtube-embed": "^0.3.0", 44 | "posthog-js": "^1.242.2", 45 | "rehype-autolink-headings": "^7.1.0", 46 | "rehype-code-titles": "^1.2.0", 47 | "rehype-slug": "^6.0.0", 48 | "rehype-stringify": "^10.0.0", 49 | "remark-gfm": "^4.0.0", 50 | "remark-parse": "^11.0.0", 51 | "remark-rehype": "^11.1.0", 52 | "remark-smartypants": "^2.1.0", 53 | "remark-toc": "^9.0.0", 54 | "unified": "^11.0.4", 55 | "unist-util-visit": "^5.0.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test' 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: 'pnpm run build && pnpm run preview', 6 | port: 3000, 7 | }, 8 | } 9 | 10 | export default config 11 | -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/animate-flip.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/animate-flip.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/crossfade.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/crossfade.webp -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/flip-gsap.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/flip-gsap.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/flip-svelte.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/flip-svelte.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/flip.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/flip.webp -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/motion-spring.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/motion-spring.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/motion-tweened.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/motion-tweened.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-custom-css.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-custom-css.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-custom-js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-custom-js.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-deferred.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-deferred.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-examples.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-examples.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-fly.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-fly.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-key.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-key.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/transitions-local.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/transitions-local.mp4 -------------------------------------------------------------------------------- /posts/animation-with-svelte/images/tweet.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/animation-with-svelte/images/tweet.webp -------------------------------------------------------------------------------- /posts/avoid-flashing-iframe/images/iframe-flashing.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/avoid-flashing-iframe/images/iframe-flashing.mp4 -------------------------------------------------------------------------------- /posts/avoid-flashing-iframe/images/iframe-without-flashing.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/avoid-flashing-iframe/images/iframe-without-flashing.mp4 -------------------------------------------------------------------------------- /posts/clean-git-history/images/feature-behind-rebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/feature-behind-rebase.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/feature-behind-upstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/feature-behind-upstream.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/first-commits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/first-commits.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/first-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/first-merge.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/rebase-checkout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/rebase-checkout.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/rebase-commit-change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/rebase-commit-change.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/rebase-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/rebase-commit.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/rebase-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/rebase-complete.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/rebase-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/rebase-open.png -------------------------------------------------------------------------------- /posts/clean-git-history/images/vs-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/clean-git-history/images/vs-code.png -------------------------------------------------------------------------------- /posts/create-a-coding-sandbox/images/codepen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-coding-sandbox/images/codepen.webp -------------------------------------------------------------------------------- /posts/create-a-coding-sandbox/images/diagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-coding-sandbox/images/diagram.webp -------------------------------------------------------------------------------- /posts/create-a-coding-sandbox/images/error.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-coding-sandbox/images/error.webp -------------------------------------------------------------------------------- /posts/create-a-coding-sandbox/images/iframe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-coding-sandbox/images/iframe.webp -------------------------------------------------------------------------------- /posts/create-a-coding-sandbox/images/source.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-coding-sandbox/images/source.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/layout.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/layout.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/mdx-component.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/mdx-component.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/next-page-render.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/next-page-render.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/site-diagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/site-diagram.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/sorted-posts.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/sorted-posts.webp -------------------------------------------------------------------------------- /posts/create-a-markdown-blog/images/vercel.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/create-a-markdown-blog/images/vercel.webp -------------------------------------------------------------------------------- /posts/creating-content/images/audio-filters.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/audio-filters.webp -------------------------------------------------------------------------------- /posts/creating-content/images/editing.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/editing.mp4 -------------------------------------------------------------------------------- /posts/creating-content/images/interface.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/interface.webp -------------------------------------------------------------------------------- /posts/creating-content/images/macro.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/macro.mp4 -------------------------------------------------------------------------------- /posts/creating-content/images/microphone.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/microphone.webp -------------------------------------------------------------------------------- /posts/creating-content/images/obs-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/obs-1.webp -------------------------------------------------------------------------------- /posts/creating-content/images/obs-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/obs-2.webp -------------------------------------------------------------------------------- /posts/creating-content/images/obs-3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/obs-3.webp -------------------------------------------------------------------------------- /posts/creating-content/images/obs-4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/obs-4.webp -------------------------------------------------------------------------------- /posts/creating-content/images/shure.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/shure.webp -------------------------------------------------------------------------------- /posts/creating-content/images/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/creating-content/images/video.mp4 -------------------------------------------------------------------------------- /posts/css-focus-outline/images/border.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/css-focus-outline/images/border.gif -------------------------------------------------------------------------------- /posts/css-focus-outline/images/box-shadow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/css-focus-outline/images/box-shadow.gif -------------------------------------------------------------------------------- /posts/css-focus-outline/images/default.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/css-focus-outline/images/default.gif -------------------------------------------------------------------------------- /posts/css-focus-outline/images/no-outline.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/css-focus-outline/images/no-outline.gif -------------------------------------------------------------------------------- /posts/css-focus-outline/images/outline.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/css-focus-outline/images/outline.gif -------------------------------------------------------------------------------- /posts/dark-mode-favicon/images/comparison.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dark-mode-favicon/images/comparison.webp -------------------------------------------------------------------------------- /posts/dark-mode-favicon/images/link-media.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dark-mode-favicon/images/link-media.webp -------------------------------------------------------------------------------- /posts/dark-mode-favicon/images/prefers-color-scheme.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dark-mode-favicon/images/prefers-color-scheme.webp -------------------------------------------------------------------------------- /posts/dark-mode-favicon/images/safari.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dark-mode-favicon/images/safari.webp -------------------------------------------------------------------------------- /posts/dark-mode-favicon/images/svg-favicons.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dark-mode-favicon/images/svg-favicons.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/alignment-bad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/alignment-bad.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/alignment-good.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/alignment-good.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/alignment-grid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/alignment-grid.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/alignment-spacing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/alignment-spacing.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/apple-grid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/apple-grid.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-contrast-bad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-contrast-bad.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-contrast-good.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-contrast-good.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-palette.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-palette.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-picker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-picker.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-rule.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-rule.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/color-wheel.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/color-wheel.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/contrast-checker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/contrast-checker.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/proximity-outline.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/proximity-outline.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/proximity.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/proximity.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/repetition-scrolling-area.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/repetition-scrolling-area.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/repetition-scrolling-heading.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/repetition-scrolling-heading.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/repetition.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/repetition.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/scroll-more-idiot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/scroll-more-idiot.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/text-contrast.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/text-contrast.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/typefaces.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/typefaces.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/typography.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/typography.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/wireframe-landing-page.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/wireframe-landing-page.webp -------------------------------------------------------------------------------- /posts/design-for-developers/images/wireframe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/design-for-developers/images/wireframe.webp -------------------------------------------------------------------------------- /posts/dynamic-social-share-images/images/social-card.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dynamic-social-share-images/images/social-card.webp -------------------------------------------------------------------------------- /posts/dynamic-social-share-images/images/social-share-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dynamic-social-share-images/images/social-share-image.png -------------------------------------------------------------------------------- /posts/dynamic-social-share-images/images/social-share.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/dynamic-social-share-images/images/social-share.webp -------------------------------------------------------------------------------- /posts/global-styles-in-sveltekit/global-styles-in-sveltekit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Use Global Styles In SvelteKit 3 | description: How to use global styles in SvelteKit. 4 | slug: global-styles-in-sveltekit 5 | published: '2022-11-24' 6 | category: sveltekit 7 | --- 8 | 9 | {% youtube id="jHSwChkx3TQ" title="Global Styles In SvelteKit" %} 10 | 11 | ## Table of Contents 12 | 13 | ## app.html 14 | 15 | You can include your global styles inside `app.html` in SvelteKit but then you can't take advantage of [HMR (hot module replacement)](https://vitejs.dev/guide/features.html#hot-module-replacement) since SvelteKit has no idea the file got updated. 16 | 17 | ```svelte:src/app.html showLineNumbers 18 | 19 | 20 | 25 | 26 | 27 | ``` 28 | 29 | ## +layout.svelte 30 | 31 | Instead use a `+layout.svelte` file and import your styles. 32 | 33 | ```css:src/app.css showLineNumbers 34 | h1 { 35 | color: aqua; 36 | } 37 | ``` 38 | 39 | ```svelte:routes/+layout.svelte showLineNumbers 40 | 43 | 44 | 45 | ``` 46 | 47 | The base layout wraps the entire SvelteKit app. 48 | 49 | ## :global 50 | 51 | Svelte ignores styles that aren't part of your template which sucks when you don't have control over the markup from a CMS (content management system), or the markup is inside another component. 52 | 53 | In that case you can use `:global`. 54 | 55 | ```svelte:+page.svelte showLineNumbers 56 | 59 | 60 |
61 | {@html data.content} 62 |
63 | 64 | 69 | ``` 70 | 71 | You can scope `.prose` to the component if you want. 72 | 73 | ```svelte:+page.svelte showLineNumbers 74 | 75 | 76 | 81 | ``` 82 | 83 | SvelteKit by default comes with the [svelte-preprocess](https://github.com/sveltejs/svelte-preprocess) preprocessor where you can use the `global` attribute on the ` 93 | ``` 94 | -------------------------------------------------------------------------------- /posts/great-developers-steal/images/search-results.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/great-developers-steal/images/search-results.webp -------------------------------------------------------------------------------- /posts/great-developers-steal/images/spec.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/great-developers-steal/images/spec.webp -------------------------------------------------------------------------------- /posts/how-to-optimize-images/images/squoosh.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/how-to-optimize-images/images/squoosh.webp -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/circle-flip.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/circle-flip.mp4 -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/circle-teleport.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/circle-teleport.mp4 -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/flip-circles.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/flip-circles.mp4 -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/flip-grid.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/flip-grid.mp4 -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/flip-movies.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/flip-movies.mp4 -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/flip.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/flip.webp -------------------------------------------------------------------------------- /posts/impossible-layout-animations-with-svelte/images/how-flip-works.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/impossible-layout-animations-with-svelte/images/how-flip-works.mp4 -------------------------------------------------------------------------------- /posts/intro-to-react/images/js-update.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/intro-to-react/images/js-update.gif -------------------------------------------------------------------------------- /posts/intro-to-react/images/paint-flashing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/intro-to-react/images/paint-flashing.webp -------------------------------------------------------------------------------- /posts/intro-to-react/images/react-update.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/intro-to-react/images/react-update.gif -------------------------------------------------------------------------------- /posts/introduction-to-3d-with-svelte/images/controls.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-3d-with-svelte/images/controls.webp -------------------------------------------------------------------------------- /posts/introduction-to-3d-with-svelte/images/models.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-3d-with-svelte/images/models.mp4 -------------------------------------------------------------------------------- /posts/introduction-to-3d-with-svelte/images/scene.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-3d-with-svelte/images/scene.webp -------------------------------------------------------------------------------- /posts/introduction-to-3d-with-svelte/images/sphere.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-3d-with-svelte/images/sphere.webp -------------------------------------------------------------------------------- /posts/introduction-to-3d-with-svelte/images/spooky.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-3d-with-svelte/images/spooky.webp -------------------------------------------------------------------------------- /posts/introduction-to-tailwind-css/images/banding.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/introduction-to-tailwind-css/images/banding.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/csr.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/csr.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/debugger.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/debugger.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/hydration.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/hydration.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/kit.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/kit.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/navigation.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/navigation.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/ssr.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/ssr.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/sveltekit.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/sveltekit.webp -------------------------------------------------------------------------------- /posts/learn-how-sveltekit-works/images/twitter.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-how-sveltekit-works/images/twitter.webp -------------------------------------------------------------------------------- /posts/learn-problem-solving/images/player.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-problem-solving/images/player.webp -------------------------------------------------------------------------------- /posts/learn-problem-solving/images/todos.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/learn-problem-solving/images/todos.webp -------------------------------------------------------------------------------- /posts/master-the-svelte-context-api/images/context-api.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/master-the-svelte-context-api/images/context-api.webp -------------------------------------------------------------------------------- /posts/next-bundle-size/images/preact.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/next-bundle-size/images/preact.webp -------------------------------------------------------------------------------- /posts/next-bundle-size/images/react.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/next-bundle-size/images/react.webp -------------------------------------------------------------------------------- /posts/next-bundle-size/next-bundle-size.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reduce Next.js Bundle Size by Half 3 | description: Learn how using Preact instead of React can reduce Next.js bundle size by half. 4 | slug: next-bundle-size 5 | published: '2021-9-1' 6 | category: next 7 | --- 8 | 9 | [Next.js](https://nextjs.org/) is a **meta-framework** built on top of [React](https://reactjs.org/) that solves the problem of **hybrid static and server rendering** for **React**. 10 | 11 | That being said the [react](https://bundlephobia.com/package/react@17.0.2) package itself is a tiny library, but combined with [react-dom](https://bundlephobia.com/package/react-dom@17.0.2) it's **42 kB** of **runtime** we ship to the client. 12 | 13 | {% img src="react.webp" alt="Next.js bundle size using React" %} 14 | 15 | The final **Next.js** bundle size is **79.46 kB**. 16 | 17 | [Preact](https://preactjs.com/) is a **React** alternative with the same API, but it's only [3kb in size](https://bundlephobia.com/package/preact@10.5.14). This is possible because it doesn't have extra overhead from using [synthetic events](https://reactjs.org/docs/events.html) like **React**, but uses the **browser's native API**. 18 | 19 | If you want to be more informed about the differences, **Preact** has an entire page dedicated explaining the [differences to React](https://preactjs.com/guide/v8/differences-to-react/) (you could say it's _svelte_ 🥁). 20 | 21 | This might sound like a lot of work, but it only requires two steps changing the **Webpack** config inside **Next.js**. Credits go to [Lee Robinson](https://leerob.io/). 22 | 23 | The first step is to install **Preact**. 24 | 25 | ```shell:terminal 26 | npm i preact 27 | ``` 28 | 29 | The second step is replacing **React** with **Preact** in production by changing the `next.config.js`. 30 | 31 | ```js:next.config.js showLineNumbers 32 | module.exports = { 33 | webpack: (config, { dev, isServer }) => { 34 | if (!dev && !isServer) { 35 | Object.assign(config.resolve.alias, { 36 | react: 'preact/compat', 37 | 'react-dom/test-utils': 'preact/test-utils', 38 | 'react-dom': 'preact/compat', 39 | }) 40 | } 41 | return config 42 | }, 43 | } 44 | ``` 45 | 46 | That's it! 🥳 Next time you run `npm run build`, and `npm start` there's a **48%** reduction in bundle size at **45.28 Kb**. 47 | 48 | {% img src="preact.webp" alt="Next.js bundle size using Preact" %} 49 | 50 | ## Conclusion 51 | 52 | This is a great method to save your users from having to download the entire **React** runtime, but make sure you're informed about the differences between **React** and **Preact** if you need some specific **React** features. 53 | 54 | Thanks for reading! 🏄‍♀️ 55 | -------------------------------------------------------------------------------- /posts/responsive-css-grid-layout/responsive-css-grid-layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Use This Simple Line Of CSS Grid To Make Your Layout Responsive 3 | description: Avoid writing a bunch of media queries using a single line of CSS Grid. 4 | slug: responsive-css-grid-layout 5 | published: '2024-03-28' 6 | category: css 7 | --- 8 | 9 | {% embed src="https://svelte.dev/repl/2a2644dd768c43f6a5e16ef94fd145ca?version=4.2.12" title="Responsive CSS Grid Layout" %} 10 | 11 | If you're using [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout) for your layout, you can avoid writing a bunch of media queries with this simple line of CSS. 12 | 13 | ```css:css {3} showLineNumbers 14 | .grid { 15 | display: grid; 16 | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 17 | } 18 | ``` 19 | 20 | Let's break it down: 21 | 22 | - `repeat` uses `auto-fill` as the repeat count to repeat the grid item as many times as needed 23 | - `minmax` specifies a minimum size of `200px`, and maximum size of `1fr` for the grid item 24 | 25 | You could also use `auto-fit` instead of `auto-fill`, but what is the difference? 26 | 27 | {% embed src="https://svelte.dev/repl/6b20131ea56a480b83af648d461bc1b4?version=4.2.12" title="CSS Grid Auto-Fill Versus Auto-Fit" %} 28 | 29 | If you want to stretch the grid items to take the entire available space use `auto-fit`, otherwise use `auto-fill`. 30 | -------------------------------------------------------------------------------- /posts/rethink-how-you-write-git-commits/images/commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/rethink-how-you-write-git-commits/images/commit.png -------------------------------------------------------------------------------- /posts/rethink-how-you-write-git-commits/images/confused.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/rethink-how-you-write-git-commits/images/confused.webp -------------------------------------------------------------------------------- /posts/rethink-how-you-write-git-commits/images/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/rethink-how-you-write-git-commits/images/git.png -------------------------------------------------------------------------------- /posts/rethink-how-you-write-git-commits/images/gitmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/rethink-how-you-write-git-commits/images/gitmoji.png -------------------------------------------------------------------------------- /posts/rethink-how-you-write-git-commits/images/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/rethink-how-you-write-git-commits/images/options.png -------------------------------------------------------------------------------- /posts/screenshot-page-and-dom-elements/images/brave-screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/screenshot-page-and-dom-elements/images/brave-screenshot.webp -------------------------------------------------------------------------------- /posts/screenshot-page-and-dom-elements/images/firefox-screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/screenshot-page-and-dom-elements/images/firefox-screenshot.webp -------------------------------------------------------------------------------- /posts/screenshot-page-and-dom-elements/screenshot-page-and-dom-elements.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Screenshot The Entire Page 3 | description: You don't need extensions to screenshot the page, sections and elements. 4 | slug: screenshot-page-and-dom-elements 5 | published: '2021-10-4' 6 | category: general 7 | --- 8 | 9 | ## Firefox 10 | 11 | One of the [Firefox](https://www.mozilla.org/en-US/firefox/new/) features I love is the [Take a Screenshot](https://support.mozilla.org/en-US/kb/take-screenshots-firefox) tool that let's you capture **visible parts** of the page, the **full page**, or **DOM** (Document Object Model) elements. 12 | 13 | You can do so using the **dropdown option**, or the Ctrl + Shift + S **shortcut**. 14 | 15 | {% img src="firefox-screenshot.webp" alt="Taking screenshot of elements in Firefox" %} 16 | 17 | ## Chrome 18 | 19 | This is the one thing I miss when using [Brave](https://brave.com/) but there's a solution that works for any [Chromium]() based browser. 20 | 21 | Open your developer tools with Ctrl + Shift + I, or use the **options menu** from your browser. 22 | 23 | Inspired by the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) from [Visual Studio Code](https://code.visualstudio.com/) you can open the [Command Menu](https://developer.chrome.com/docs/devtools/command-menu/) with Ctrl + Shift + P and typing **screenshot** you get these options. 24 | 25 | {% img src="brave-screenshot.webp" alt="Taking screenshot of elements in Brave" %} 26 | 27 | If you want to capture a **DOM** element it has to be **selected** in **elements**. 28 | 29 | ## Conclusion 30 | 31 | While it's not as convenient as **Firefox** at least you don't require an extension but if you're looking for one that lets you also record video you can use something like [Nimbus](https://chrome.google.com/webstore/detail/nimbus-screenshot-screen/bpconcjcammlapcogcnnelfmaeghhagj?hl=en). 32 | 33 | Thanks for reading! 🏄‍♀️ 34 | -------------------------------------------------------------------------------- /posts/simple-css-debug-trick/images/bookmarklet.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/simple-css-debug-trick/images/bookmarklet.webp -------------------------------------------------------------------------------- /posts/simple-css-debug-trick/images/css-debug.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/posts/simple-css-debug-trick/images/css-debug.webp -------------------------------------------------------------------------------- /posts/simple-css-debug-trick/simple-css-debug-trick.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Simple Trick To Debug Your CSS 3 | description: Learn how to debug CSS using this simple trick. 4 | slug: simple-css-debug-trick 5 | published: '2021-10-2' 6 | category: css 7 | --- 8 | 9 | {% youtube id="5nrMpCWkncc" title="Debug Your CSS" %} 10 | 11 | ## Table of Contents 12 | 13 | ## The Outline Trick 14 | 15 | The fastest way to find misbehaving elements on the page is using the `outline` trick in CSS. This helps you find issues such as overflowing elements quickly, but more importantly it gives you a visual of the entire page. 16 | 17 | {% img src="css-debug.webp" alt="Debugging CSS" %} 18 | 19 | You can use `outline` to set an outline on every element, which isn't going to affect the layout compared to using `border` — unless you specify `box-sizing: border-box`. 20 | 21 | ```css:css showLineNumbers 22 | * { 23 | outline: 1px solid tomato; 24 | } 25 | ``` 26 | 27 | You can also add a `background` property, or use different `outline` colors for nested elements, if you want to make things more readable: 28 | 29 | ```css:css showLineNumbers 30 | * { 31 | outline: 1px solid tomato; 32 | } 33 | 34 | * * { 35 | outline: 1px solid black; 36 | } 37 | 38 | * * * { 39 | outline: 1px solid white; 40 | } 41 | ``` 42 | 43 | ## Creating A Bookmarklet 44 | 45 | You can turn this helper into a JavaScript bookmarklet, and use it when you need it. The only thing you have to do is create a regular bookmark in your browser, and include this code. 46 | 47 | {% img src="bookmarklet.webp" alt="Bookmarklet" %} 48 | 49 | ```js:bookmarklet showLineNumbers 50 | javascript: (function () { 51 | const headElement = document.head; 52 | const styleElement = document.createElement('style'); 53 | styleElement.setAttribute('debug-css', ''); 54 | styleElement.innerText = '* { outline: 1px solid tomato; }'; 55 | 56 | const debugElement = headElement.querySelector('[debug-css]'); 57 | if (debugElement) return debugElement.remove(); 58 | 59 | headElement.append(styleElement); 60 | })(); 61 | ``` 62 | 63 | The code uses an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) to create a ` 104 | -------------------------------------------------------------------------------- /src/lib/ui/header/header.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 |
16 |
17 | 21 | 22 | 23 | 24 | 29 |
30 |
31 | 32 | 83 | -------------------------------------------------------------------------------- /src/lib/ui/header/logo.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/lib/ui/header/menu.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 18 | {#if open} 19 | 30 | {/if} 31 | 32 | 81 | -------------------------------------------------------------------------------- /src/lib/ui/header/preferences/dyslexic.svelte: -------------------------------------------------------------------------------- 1 | 38 | 39 |
40 |
41 | 42 | 43 | 51 | 52 | 53 |
54 |
55 | 56 | 95 | -------------------------------------------------------------------------------- /src/lib/ui/header/preferences/index.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | 26 | {#if open} 27 | 39 | {/if} 40 | 41 | 105 | -------------------------------------------------------------------------------- /src/lib/ui/header/preferences/preferences.svelte.ts: -------------------------------------------------------------------------------- 1 | import { browser } from '$app/environment' 2 | 3 | class Preferences { 4 | #textSize = $state(20) 5 | #textLength = $state(60) 6 | #textHeight = $state(32) 7 | #htmlEl: HTMLElement | undefined 8 | resetTheme = $state(false) 9 | 10 | get textSize() { 11 | return this.#textSize 12 | } 13 | 14 | set textSize(value) { 15 | this.#textSize = value 16 | localStorage.textSize = `${value}px` 17 | this.#htmlEl?.style.setProperty('--post-txt-size', `${value}px`) 18 | } 19 | 20 | get textLength() { 21 | return this.#textLength 22 | } 23 | 24 | set textLength(value) { 25 | this.#textLength = value 26 | localStorage.textLength = `${value}ch` 27 | this.#htmlEl?.style.setProperty('--post-txt-length', `${value}ch`) 28 | } 29 | 30 | get textHeight() { 31 | return this.#textHeight 32 | } 33 | 34 | set textHeight(value) { 35 | this.#textHeight = value 36 | localStorage.textHeight = `${value}px` 37 | this.#htmlEl?.style.setProperty('--post-txt-height', `${value}px`) 38 | } 39 | 40 | constructor() { 41 | if (!browser) return 42 | 43 | const { textSize, textLength, textHeight } = localStorage 44 | 45 | if (textSize) this.textSize = +textSize.replace('px', '') 46 | if (textLength) this.textLength = +textLength.replace('ch', '') 47 | if (textHeight) this.textHeight = +textHeight.replace('px', '') 48 | 49 | this.#htmlEl = document.documentElement 50 | } 51 | 52 | reset() { 53 | this.textSize = 20 54 | this.textLength = 60 55 | this.textHeight = 32 56 | 57 | this.#htmlEl!.dataset.theme = '🌛 Night' 58 | delete this.#htmlEl!.dataset.font 59 | 60 | localStorage.theme = '🌛 Night' 61 | localStorage.removeItem('font') 62 | 63 | this.resetTheme = !this.resetTheme 64 | } 65 | } 66 | 67 | export const preferences = new Preferences() 68 | -------------------------------------------------------------------------------- /src/lib/ui/header/preferences/reading.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 9 |
10 | {preferences.textSize}px 11 | 20 |
21 |
22 | 23 |
24 | 27 |
28 | {preferences.textLength}ch 29 | 38 |
39 |
40 | 41 |
42 | 45 |
46 | {preferences.textHeight}px 47 | 56 |
57 |
58 | 59 | 93 | -------------------------------------------------------------------------------- /src/lib/ui/header/preferences/reset.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | Use default settings 7 | 8 |
9 | 10 | 20 | -------------------------------------------------------------------------------- /src/lib/ui/header/search/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './search.svelte' 2 | -------------------------------------------------------------------------------- /src/lib/ui/header/search/search-icon.svelte: -------------------------------------------------------------------------------- 1 | 12 | Search 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/ui/header/search/search-worker.ts: -------------------------------------------------------------------------------- 1 | import { createPostsIndex, searchPostsIndex } from './search' 2 | 3 | addEventListener('message', async (e) => { 4 | const { type, payload } = e.data 5 | 6 | if (type === 'load') { 7 | const posts = await fetch('/api/search').then((res) => res.json()) 8 | createPostsIndex(posts) 9 | postMessage({ type: 'ready' }) 10 | } 11 | 12 | if (type === 'search') { 13 | const searchTerm = payload.searchTerm 14 | const results = searchPostsIndex(searchTerm) 15 | postMessage({ type: 'results', payload: { results, searchTerm } }) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /src/lib/ui/header/search/search.ts: -------------------------------------------------------------------------------- 1 | import FlexSearch from 'flexsearch' 2 | 3 | export type Post = { 4 | content: string 5 | slug: string 6 | title: string 7 | } 8 | 9 | export type Result = { 10 | content: string[] 11 | slug: string 12 | title: string 13 | } 14 | 15 | let postsIndex: FlexSearch.Index 16 | let posts: Post[] 17 | 18 | export function createPostsIndex(data: Post[]) { 19 | postsIndex = new FlexSearch.Index({ tokenize: 'forward' }) 20 | 21 | data.forEach((post, i) => { 22 | const item = `${post.title} ${post.content}` 23 | postsIndex.add(i, item) 24 | }) 25 | 26 | posts = data 27 | } 28 | 29 | export function searchPostsIndex(searchTerm: string) { 30 | const match = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 31 | const results = postsIndex.search(match) 32 | return results 33 | .map((index) => posts[index as number]) 34 | .map(({ slug, title, content }) => { 35 | return { 36 | slug, 37 | title: replaceTextWithMarker(title, match), 38 | content: getMatches(content, match), 39 | } 40 | }) 41 | } 42 | 43 | function replaceTextWithMarker(text: string, match: string) { 44 | const regex = new RegExp(match, 'gi') 45 | return text.replaceAll(regex, (match) => `${match}`) 46 | } 47 | 48 | function getMatches(text: string, searchTerm: string, limit = 1) { 49 | const regex = new RegExp(searchTerm, 'gi') 50 | const indexes = [] 51 | let matches = 0 52 | let match 53 | 54 | while ((match = regex.exec(text)) !== null && matches < limit) { 55 | indexes.push(match.index) 56 | matches++ 57 | } 58 | 59 | return indexes.map((index) => { 60 | const start = index - 20 61 | const end = index + 80 62 | const excerpt = text.substring(start, end).trim() 63 | return `...${replaceTextWithMarker(excerpt, searchTerm)}...` 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /src/lib/ui/header/socials.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | 18 | 29 | -------------------------------------------------------------------------------- /src/lib/ui/heading.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

6 | {@render props.children?.()} 7 |

8 | 9 | 22 | -------------------------------------------------------------------------------- /src/lib/ui/newsletter.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 |
32 | 33 | 41 | 45 |
46 | 47 |
48 | {#if error} 49 | {error} 50 | {/if} 51 | 52 | {#if success} 53 | {success} 54 | {/if} 55 |
56 | 57 | 120 | -------------------------------------------------------------------------------- /src/lib/ui/posts.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | {@render title?.()} 19 | 20 |
21 | {#each posts as post, i} 22 |
28 | 37 |
38 | {/each} 39 |
40 | 41 | {@render more?.()} 42 |
43 | 44 | 95 | -------------------------------------------------------------------------------- /src/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { onNavigate } from '$app/navigation' 2 | import type { DateStyle, Fetch } from '$lib/types' 3 | 4 | export async function fetchJSON( 5 | url: string, 6 | fetchFn: Fetch = fetch 7 | ): Promise { 8 | const response = await fetchFn(url) 9 | if (!response.ok) throw new Error(`Error fetching JSON from ${response.url}`) 10 | return await response.json() 11 | } 12 | 13 | export function formatDate( 14 | date: string, 15 | dateStyle: DateStyle = 'medium', 16 | locales = 'en' 17 | ) { 18 | // safari is mad about dashes in the date 19 | const dateToFormat = new Date(date.replaceAll('-', '/')) 20 | const dateFormatter = new Intl.DateTimeFormat(locales, { dateStyle }) 21 | return dateFormatter.format(dateToFormat) 22 | } 23 | 24 | export function formatNumber(number: number, locales = 'en') { 25 | return new Intl.NumberFormat(locales).format(number) 26 | } 27 | 28 | export async function setupViewTransition() { 29 | onNavigate((navigation) => { 30 | if (!document.startViewTransition) return 31 | 32 | return new Promise((resolve) => { 33 | document.startViewTransition(async () => { 34 | resolve() 35 | await navigation.complete 36 | }) 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |

{$page.status}: {$page?.error?.message}

7 |
8 | 9 | 24 | -------------------------------------------------------------------------------- /src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true 2 | 3 | export async function load({ url }) { 4 | return { 5 | url: url.pathname, 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 |
21 |
22 | 23 |
24 | {#key data.url} 25 |
26 | {@render children?.()} 27 |
28 | {/key} 29 | 30 |
31 |
32 |
33 | 34 | 57 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import { initAnalytics } from '$lib/analytics' 2 | 3 | export async function load() { 4 | initAnalytics() 5 | } 6 | -------------------------------------------------------------------------------- /src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { fetchJSON } from '$lib/utils' 2 | import { error } from '@sveltejs/kit' 3 | import type { Post } from '$lib/types' 4 | 5 | export async function load({ fetch }) { 6 | try { 7 | const posts = await fetchJSON('/api/posts', fetch) 8 | const publishedPosts = posts.filter(({ draft }) => !draft) 9 | return { posts: publishedPosts.slice(0, 10) } 10 | } catch (e) { 11 | error(404, (e as Error).message) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/[slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | {data.frontmatter.title} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 |
43 |
44 |

45 | {data.frontmatter.title} 46 |

47 |

48 | Published {formatDate(data.frontmatter.published)} 49 |

50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 | 58 |
59 |
60 | 61 | 79 | -------------------------------------------------------------------------------- /src/routes/[slug]/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | 3 | export async function load({ params: { slug } }) { 4 | try { 5 | const module = await import(`../../../posts/${slug}/${slug}.md`) 6 | return { component: module.default, frontmatter: module.metadata } 7 | } catch (e) { 8 | error(404, `Post does not exist`) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/routes/[slug]/card.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if preset === 'support'} 13 |
14 |
15 | 16 |
17 | Support 18 |

19 | You can 22 | subscribe on YouTube 23 | , or consider becoming a patron if you want to support my work. 24 |

25 | 31 | Patreon 32 | 33 | 34 |
35 | {/if} 36 | 37 | {#if preset === 'edit'} 38 |
39 |
40 | 41 |
42 | Found a mistake? 43 |

44 | Every post is a Markdown file so contributing is simple as following the 45 | link below and pressing the pencil icon inside GitHub to edit it. 46 |

47 | 48 | Edit on GitHub 49 | 50 | 51 |
52 | {/if} 53 | 54 | 106 | -------------------------------------------------------------------------------- /src/routes/[slug]/clipboard.svelte: -------------------------------------------------------------------------------- 1 | 52 | -------------------------------------------------------------------------------- /src/routes/[slug]/overlay.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | {#if overlay} 26 |
27 | {/if} 28 | 29 | 38 | -------------------------------------------------------------------------------- /src/routes/[slug]/warning.svelte: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /src/routes/about/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | About 7 | 8 | 9 | About 10 | 11 |
12 |

Hey! 👋

13 |

14 | I'm Matija from 🇭🇷 Croatia and I'm infinitely curious at how 15 | things work but I'm mostly passionate about ☕ 16 | JavaScript and 17 | 🎨 18 | UI/UX design. 19 |

20 |

21 | I created Joy of Code because I think a lot of tutorials don't help you 22 | learn how to solve problems but just read the documentation to you and go from 23 | point A to point B. 24 |

25 |

26 | I want to show you steps between A and B by sharing what I learn 27 | and how I think about solving problems. 28 |

29 |

30 | I'm trying to create something I wish existed — it's more like having a 31 | friend help you out instead of someone talking over your head. 32 |

33 |
34 | 35 | 44 | -------------------------------------------------------------------------------- /src/routes/api/posts/+server.ts: -------------------------------------------------------------------------------- 1 | import { error, json } from '@sveltejs/kit' 2 | import { getPosts } from '$lib/site/posts' 3 | 4 | export async function GET() { 5 | try { 6 | const posts = await getPosts() 7 | return json(posts) 8 | } catch (e) { 9 | error(404, 'Failed to load posts') 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/routes/api/search/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit' 2 | import matter from 'gray-matter' 3 | 4 | export const prerender = true 5 | 6 | const patterns: Record = { 7 | frontmatter: /---.*?---/gs, 8 | code: /```.*?\n|```/gs, 9 | inline: /`([^`]*)`/g, 10 | heading: /^#{1,6}\s.*$/gm, 11 | link: /\[([^\]]+)\]\(([^)]+)\)/g, 12 | image: /\!\[.*?\]\(.*?\)/g, 13 | blockquote: /> /gm, 14 | bold: /\*\*/g, 15 | italic: /\b_([^_]+)_(?!\w)/g, 16 | special: /{%.*?%}/g, 17 | tags: /[<>]/g, 18 | } 19 | 20 | const htmlEntities: Record = { 21 | '<': '<', 22 | '>': '>', 23 | } 24 | 25 | function stripMarkdown(markdown: string) { 26 | for (const pattern in patterns) { 27 | switch (pattern) { 28 | case 'inline': 29 | markdown = markdown.replace(patterns[pattern], '$1') 30 | break 31 | case 'tags': 32 | markdown = markdown.replace( 33 | patterns[pattern], 34 | (match) => htmlEntities[match] 35 | ) 36 | break 37 | case 'link': 38 | markdown = markdown.replace(patterns[pattern], '$2') 39 | break 40 | case 'italic': 41 | markdown = markdown.replace(patterns[pattern], '$1') 42 | break 43 | default: 44 | markdown = markdown.replace(patterns[pattern], '') 45 | } 46 | } 47 | 48 | return markdown 49 | } 50 | 51 | export async function GET() { 52 | const paths = import.meta.glob('/posts/**/*.md', { as: 'raw', eager: true }) 53 | const posts = Object.entries(paths) 54 | .map(([_, content]) => { 55 | const frontmatter = matter(content) 56 | 57 | if (frontmatter.data.draft) { 58 | return null 59 | } 60 | 61 | return { 62 | title: frontmatter.data.title, 63 | slug: frontmatter.data.slug, 64 | content: stripMarkdown(content), 65 | } 66 | }) 67 | .filter(Boolean) 68 | 69 | return json(posts) 70 | } 71 | -------------------------------------------------------------------------------- /src/routes/api/subscribe/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit' 2 | import { BUTTONDOWN_API_KEY } from '$env/static/private' 3 | 4 | export async function POST({ request }) { 5 | const API_URL = 'https://api.buttondown.email/v1/subscribers' 6 | const API_KEY = BUTTONDOWN_API_KEY 7 | 8 | const email = await request.json() 9 | 10 | if (!email) { 11 | return json( 12 | { error: 'You forget the email. 😊' }, 13 | { 14 | status: 400, 15 | } 16 | ) 17 | } 18 | 19 | try { 20 | const response = await fetch(API_URL, { 21 | method: 'post', 22 | body: JSON.stringify({ email }), 23 | headers: { 24 | Authorization: `Token ${API_KEY}`, 25 | 'Content-Type': 'application/json', 26 | }, 27 | }) 28 | 29 | if (!response.ok) { 30 | const text = await response.text() 31 | 32 | if (text.includes('already subscribed')) { 33 | return json( 34 | { error: `You're already subscribed. 😊` }, 35 | { 36 | status: 400, 37 | } 38 | ) 39 | } 40 | 41 | return json({ error: text }, { status: 400 }) 42 | } 43 | 44 | return json( 45 | { success: 'Thank you for subscribing! 🥳' }, 46 | { 47 | status: 201, 48 | headers: { location: '/' }, 49 | } 50 | ) 51 | } catch (error) { 52 | if (error instanceof Error) { 53 | return json({ error: error.message }) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/routes/archive/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | import { fetchJSON } from '$lib/utils' 3 | import type { Post } from '$lib/types' 4 | 5 | export async function load({ fetch }) { 6 | try { 7 | const posts = await fetchJSON('/api/posts', fetch) 8 | const publishedPosts = posts.filter(({ draft }) => !draft) 9 | return { posts: publishedPosts } 10 | } catch (e) { 11 | error(404, (e as Error).message) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/archive/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Archive 10 | 11 | 12 | 13 | Archive 14 | 15 |
16 |
17 |

Posts

18 |
19 | {data.posts.length} results 20 |
21 |
22 | 23 |
24 | {#each data.posts as post, i} 25 | 41 | {/each} 42 |
43 |
44 | 45 | 89 | -------------------------------------------------------------------------------- /src/routes/categories/[category]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | import { getPostsByCategory } from '$lib/site/posts' 3 | import * as config from '$lib/site/config' 4 | 5 | export async function load({ params }) { 6 | const category = params.category 7 | 8 | if (!config.categories[category]) { 9 | error(404, 'Category does not exist') 10 | } 11 | 12 | try { 13 | return { 14 | posts: await getPostsByCategory(category), 15 | } 16 | } catch (e) { 17 | error(404, `Failed to load posts`) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/routes/categories/[category]/+page.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | {config.categories[category]} 16 | 17 | 18 | 19 | {config.categories[category]} 20 | 21 | 22 | {#snippet title()} 23 |
24 |
25 | {category} 26 |
27 |
28 | {posts.length} results 29 |
30 |
31 | {/snippet} 32 |
33 | 34 | 52 | -------------------------------------------------------------------------------- /src/routes/drafts/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | import { fetchJSON } from '$lib/utils' 3 | import type { Post } from '$lib/types' 4 | 5 | export async function load({ fetch }) { 6 | try { 7 | const posts = await fetchJSON('/api/posts', fetch) 8 | const draftPosts = posts.filter(({ draft }) => draft) 9 | return { posts: draftPosts } 10 | } catch (e) { 11 | error(404, (e as Error).message) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/drafts/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Drafts 10 | 11 | 12 | 13 | Drafts 14 | 15 |
16 |
17 |

Drafts

18 |
19 | {data.posts.length} results 20 |
21 |
22 | 23 |
24 | {#each data.posts as post, i} 25 | 39 | {/each} 40 |
41 |
42 | 43 | 87 | -------------------------------------------------------------------------------- /src/routes/drafts/[slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | {data.frontmatter.title} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 | 39 | 40 |
41 |
42 |

{data.frontmatter.title}

43 |

44 | Published {formatDate(data.frontmatter.published)} 45 |

46 |
47 | 48 | 49 |
50 | 51 |
52 | 53 | 54 |
55 |
56 | 57 | 75 | -------------------------------------------------------------------------------- /src/routes/drafts/[slug]/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | 3 | export async function load({ params: { slug } }) { 4 | try { 5 | const module = await import(`../../../../posts/${slug}/${slug}.md`) 6 | return { component: module.default, frontmatter: module.metadata } 7 | } catch (e) { 8 | error(404, `Post does not exist`) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/routes/newsletter/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | Newsletter 8 | 12 | 13 | 14 | Newsletter 15 | 16 | 19 | 20 | 32 | -------------------------------------------------------------------------------- /src/routes/rss.xml/+server.ts: -------------------------------------------------------------------------------- 1 | import { getPosts } from '$lib/site/posts.js' 2 | import * as config from '$lib/site/config' 3 | 4 | export const prerender = true 5 | 6 | export async function GET() { 7 | const posts = await getPosts() 8 | 9 | const headers = { 'Content-Type': 'application/xml' } 10 | 11 | const xml = ` 12 | 13 | 14 | ${config.siteName} 15 | ${config.siteDescription} 16 | ${config.siteUrl} 17 | 20 | ${posts 21 | .reverse() 22 | .map( 23 | (post) => ` 24 | 25 | ${post.title} 26 | ${post.description} 27 | ${config.siteUrl}${post.slug} 28 | ${config.siteUrl}${post.slug} 29 | ${new Date(post.published).toUTCString()} 30 | 31 | ` 32 | ) 33 | .join('')} 34 | 35 | 36 | `.trim() 37 | 38 | return new Response(xml, { headers }) 39 | } 40 | -------------------------------------------------------------------------------- /src/routes/sitemap.xml/+server.ts: -------------------------------------------------------------------------------- 1 | import * as config from '$lib/site/config' 2 | import { getPosts } from '$lib/site/posts' 3 | 4 | export const prerender = true 5 | 6 | export async function GET() { 7 | const posts = await getPosts() 8 | 9 | const headers = { 'Content-Type': 'application/xml' } 10 | 11 | const pages = [ 12 | 'archive', 13 | 'series', 14 | 'newsletter', 15 | 'uses', 16 | 'about', 17 | ...Object.keys(config.categories).map( 18 | (category) => `categories/${category}` 19 | ), 20 | ...posts.map((post) => post.slug), 21 | ] 22 | 23 | const sitemap = ` 24 | 25 | 33 | ${pages 34 | .map((page) => { 35 | return ` 36 | 37 | ${config.siteUrl}${page} 38 | ${new Date().toISOString()} 39 | 40 | ` 41 | }) 42 | .join('')} 43 | 44 | `.trim() 45 | 46 | return new Response(sitemap, { headers }) 47 | } 48 | -------------------------------------------------------------------------------- /src/styles/code.css: -------------------------------------------------------------------------------- 1 | .prose { 2 | code[class*='shiki'], 3 | pre[class*='shiki'] { 4 | font-size: var(--font-18); 5 | tab-size: 2; 6 | } 7 | 8 | pre { 9 | padding-block: var(--spacing-16); 10 | line-height: var(--post-txt-height); 11 | background-image: var(--clr-code-bg); 12 | border-left: 1px solid var(--clr-code-border); 13 | border-top: none; 14 | border-bottom-left-radius: var(--rounded-20); 15 | border-bottom-right-radius: var(--rounded-20); 16 | box-shadow: var(--shadow-md); 17 | overflow-x: auto; 18 | } 19 | 20 | code { 21 | &:not(pre > code) { 22 | padding: var(--spacing-4) var(--spacing-8); 23 | font-size: var(--font-16); 24 | color: var(--clr-code-inline-txt); 25 | background-color: var(--clr-code-inline-bg); 26 | border-radius: var(--rounded-4); 27 | } 28 | 29 | .line:not(:last-of-type) { 30 | display: inline-block; 31 | padding-inline: var(--spacing-24); 32 | } 33 | 34 | .highlighted { 35 | width: 100%; 36 | background-color: var(--clr-code-line-highlight); 37 | } 38 | 39 | span { 40 | font-style: normal !important; 41 | } 42 | } 43 | 44 | kbd, 45 | code { 46 | font-family: var(--font-mono); 47 | } 48 | 49 | html[data-font='dyslexic'] & kbd, 50 | html[data-font='dyslexic'] & code { 51 | font-family: var(--font-dyslexic-mono); 52 | } 53 | 54 | kbd { 55 | padding: var(--spacing-4); 56 | color: var(--clr-code-inline-txt); 57 | background-color: var(--clr-code-inline-bg); 58 | border: 1px solid var(--clr-primary); 59 | border-width: 1px 1px 3px; 60 | border-radius: var(--rounded-4); 61 | } 62 | 63 | .rehype-code-title { 64 | display: flex; 65 | justify-content: space-between; 66 | padding: var(--spacing-16) var(--spacing-24); 67 | font-size: var(--post-txt-size); 68 | color: var(--clr-code-title); 69 | background-color: var(--clr-code-title-bg); 70 | border-top: 1px solid var(--clr-code-border); 71 | border-left: 1px solid var(--clr-code-border); 72 | border-bottom: none; 73 | border-top-left-radius: var(--rounded-20); 74 | border-top-right-radius: var(--rounded-20); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/styles/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Atkinson Hyperlegible'; 3 | font-style: normal; 4 | font-weight: 400; 5 | font-display: swap; 6 | src: url('/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400.woff2') 7 | format('woff2'); 8 | } 9 | 10 | @font-face { 11 | font-family: 'Atkinson Hyperlegible'; 12 | font-style: italic; 13 | font-weight: 400; 14 | font-display: swap; 15 | src: url('/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400i.woff2') 16 | format('woff2'); 17 | } 18 | 19 | @font-face { 20 | font-family: 'Atkinson Hyperlegible'; 21 | font-style: normal; 22 | font-weight: 700; 23 | font-display: swap; 24 | src: url('/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700.woff2') 25 | format('woff2'); 26 | } 27 | 28 | @font-face { 29 | font-family: 'Atkinson Hyperlegible'; 30 | font-style: italic; 31 | font-weight: 700; 32 | font-display: swap; 33 | src: url('/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700i.woff2') 34 | format('woff2'); 35 | } 36 | 37 | @font-face { 38 | font-family: 'DM Serif'; 39 | font-style: normal; 40 | font-weight: 400; 41 | font-display: swap; 42 | src: url('/fonts/dm-serif/dm-serif-400.woff2') format('woff2'); 43 | } 44 | 45 | @font-face { 46 | font-family: 'Monaspace Neon'; 47 | font-style: normal; 48 | font-weight: 400; 49 | font-display: swap; 50 | src: url('/fonts/monaspace-neon/monaspace-neon-400.woff2') format('woff2'); 51 | } 52 | 53 | @font-face { 54 | font-family: 'Monaspace Neon'; 55 | font-style: normal; 56 | font-weight: 700; 57 | font-display: swap; 58 | src: url('/fonts/monaspace-neon/monaspace-neon-700.woff2') format('woff2'); 59 | } 60 | 61 | @font-face { 62 | font-family: 'OpenDyslexic'; 63 | font-style: normal; 64 | font-weight: 400; 65 | font-display: swap; 66 | src: url('/fonts/openDyslexic/openDyslexic-400.woff2') format('woff2'); 67 | } 68 | 69 | @font-face { 70 | font-family: 'OpenDyslexic Mono'; 71 | font-style: normal; 72 | font-weight: 700; 73 | font-display: swap; 74 | src: url('/fonts/openDyslexic/openDyslexic-700.woff2') format('woff2'); 75 | } 76 | 77 | @font-face { 78 | font-family: 'OpenDyslexic'; 79 | font-style: normal; 80 | font-weight: 400; 81 | font-display: swap; 82 | src: url('/fonts/openDyslexic/openDyslexicMono-400.woff2') format('woff2'); 83 | } 84 | -------------------------------------------------------------------------------- /src/styles/reset.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | img, 10 | picture, 11 | video, 12 | canvas, 13 | svg { 14 | display: block; 15 | max-width: 100%; 16 | } 17 | 18 | iframe, 19 | video { 20 | width: 100%; 21 | border: none; 22 | aspect-ratio: 16 / 9; 23 | } 24 | 25 | input, 26 | button, 27 | textarea, 28 | select { 29 | font: inherit; 30 | } 31 | 32 | p, 33 | h1, 34 | h2, 35 | h3, 36 | h4, 37 | h5, 38 | h6 { 39 | overflow-wrap: break-word; 40 | } 41 | 42 | button, 43 | input { 44 | font: inherit; 45 | color: inherit; 46 | border: none; 47 | } 48 | 49 | button { 50 | font: inherit; 51 | background: none; 52 | cursor: pointer; 53 | } 54 | 55 | ul, 56 | ol { 57 | list-style: none; 58 | } 59 | -------------------------------------------------------------------------------- /src/styles/styles.css: -------------------------------------------------------------------------------- 1 | @import 'reset.css'; 2 | @import 'global.css'; 3 | @import 'fonts.css'; 4 | @import 'themes.css'; 5 | @import 'post.css'; 6 | @import 'code.css'; 7 | -------------------------------------------------------------------------------- /static/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /static/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/favicon-16x16.png -------------------------------------------------------------------------------- /static/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/favicon-32x32.png -------------------------------------------------------------------------------- /static/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/favicon.ico -------------------------------------------------------------------------------- /static/assets/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/icon-192x192.png -------------------------------------------------------------------------------- /static/assets/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/assets/icon-256x256.png -------------------------------------------------------------------------------- /static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400.woff2 -------------------------------------------------------------------------------- /static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400i.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-400i.woff2 -------------------------------------------------------------------------------- /static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700.woff2 -------------------------------------------------------------------------------- /static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700i.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/atkinson-hyperlegible/atkinson-hyperlegible-700i.woff2 -------------------------------------------------------------------------------- /static/fonts/dm-serif/dm-serif-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/dm-serif/dm-serif-400.woff2 -------------------------------------------------------------------------------- /static/fonts/monaspace-neon/monaspace-neon-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/monaspace-neon/monaspace-neon-400.woff2 -------------------------------------------------------------------------------- /static/fonts/monaspace-neon/monaspace-neon-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/monaspace-neon/monaspace-neon-700.woff2 -------------------------------------------------------------------------------- /static/fonts/openDyslexic/openDyslexic-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/openDyslexic/openDyslexic-400.woff2 -------------------------------------------------------------------------------- /static/fonts/openDyslexic/openDyslexic-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/openDyslexic/openDyslexic-700.woff2 -------------------------------------------------------------------------------- /static/fonts/openDyslexic/openDyslexicMono-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/fonts/openDyslexic/openDyslexicMono-400.woff2 -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Joy of Code", 3 | "short_name": "Joy of Code", 4 | "description": "Digital garden growing curious minds with content related to web development and design.", 5 | "icons": [ 6 | { 7 | "src": "/assets/icon-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png" 10 | }, 11 | { 12 | "src": "/assets/icon-256x256.png", 13 | "sizes": "256x256", 14 | "type": "image/png" 15 | } 16 | ], 17 | "theme_color": "#fbd38d", 18 | "background_color": "#1a202c", 19 | "display": "standalone" 20 | } 21 | -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /drafts/ 3 | Sitemap: https://joyofcode.xyz/sitemap.xml -------------------------------------------------------------------------------- /static/sfx/click.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/sfx/click.mp3 -------------------------------------------------------------------------------- /static/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattcroat/joy-of-code/6f7d47f5e73313b12747aa17c22aaaa07c62c598/static/social.png -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-vercel' 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 3 | import { sequence, preprocessMeltUI } from '@melt-ui/pp' 4 | import markdown from './src/lib/markdown/index.js' 5 | 6 | /** @type {import('@sveltejs/kit').Config} */ 7 | const config = { 8 | extensions: ['.svelte', '.md'], 9 | preprocess: sequence([markdown(), vitePreprocess(), preprocessMeltUI()]), 10 | kit: { 11 | adapter: adapter(), 12 | }, 13 | vitePlugin: { 14 | inspector: { 15 | toggleKeyCombo: 'meta-shift', 16 | showToggleButton: 'always', 17 | toggleButtonPos: 'bottom-right', 18 | }, 19 | }, 20 | } 21 | 22 | export default config 23 | -------------------------------------------------------------------------------- /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 | } 15 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "redirects": [ 3 | { 4 | "source": "/invite", 5 | "destination": "https://discord.gg/k6ZpwAKwwZ", 6 | "permanent": true 7 | }, 8 | { 9 | "source": "/uses", 10 | "destination": "https://github.com/mattcroat/uses", 11 | "permanent": true 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite' 2 | import { defineConfig } from 'vite' 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | server: { 7 | fs: { allow: ['..'] }, 8 | }, 9 | }) 10 | --------------------------------------------------------------------------------