diff --git a/mdsvex.config.js b/mdsvex.config.js index 4964c03..c5051ff 100644 --- a/mdsvex.config.js +++ b/mdsvex.config.js @@ -12,6 +12,7 @@ import rehypeExternalLinks from 'rehype-external-links'; import { createHighlighter } from '@svelte-dev/pretty-code'; import { remarkReadingTime } from './src/lib/remarkReadingTime.js'; import { rehypeStripCodeTabindex } from './src/lib/rehypeStripCodeTabindex.js'; +import { rehypeLazyImages } from './src/lib/rehypeLazyImages.js'; const config = { extensions: ['.md'], @@ -47,7 +48,8 @@ const config = { rehypeFigure, [rehypeExternalLinks, { target: ['_blank'], rel: ['noopener noreferrer'] }], [rehypeAutolinkHeadings, { behavior: 'append', content: { type: 'text', value: '#' } }], - rehypeStripCodeTabindex + rehypeStripCodeTabindex, + rehypeLazyImages ] }; diff --git a/src/lib/rehypeLazyImages.js b/src/lib/rehypeLazyImages.js new file mode 100644 index 0000000..2bdbb1f --- /dev/null +++ b/src/lib/rehypeLazyImages.js @@ -0,0 +1,14 @@ +import { visit } from 'unist-util-visit'; + +/** Add loading="lazy" and decoding="async" to all markdown images. */ +export function rehypeLazyImages() { + return (tree) => { + visit(tree, 'element', (node) => { + if (node.tagName === 'img') { + node.properties = node.properties ?? {}; + node.properties.loading = node.properties.loading ?? 'lazy'; + node.properties.decoding = node.properties.decoding ?? 'async'; + } + }); + }; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index bb0386d..d092bf4 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -76,6 +76,7 @@ +
diff --git a/src/routes/blog/[slug]/+page.ts b/src/routes/blog/[slug]/+page.ts index 5838632..e670857 100644 --- a/src/routes/blog/[slug]/+page.ts +++ b/src/routes/blog/[slug]/+page.ts @@ -1,6 +1,7 @@ -// Server-rendered (not prerendered) so the cookie/?lang= preferred language -// is applied on the server — the first byte is the right language, no flash. -export const prerender = false; +// Prerendered so pages are served from CDN edge. +// Language preference is restored client-side via localStorage/cookie in onMount; +// the mounted guard suppresses animations during that initial switch. +export const prerender = true; import { error } from '@sveltejs/kit'; diff --git a/src/routes/projects/[slug]/+page.ts b/src/routes/projects/[slug]/+page.ts index 3de6e4f..dedc72e 100644 --- a/src/routes/projects/[slug]/+page.ts +++ b/src/routes/projects/[slug]/+page.ts @@ -1,6 +1,6 @@ -// Server-rendered (not prerendered) so the cookie/?lang= preferred language -// is applied on the server — the first byte is the right language, no flash. -export const prerender = false; +// Prerendered so pages are served from CDN edge. +// Language preference is restored client-side via localStorage/cookie in onMount. +export const prerender = true; import { error } from '@sveltejs/kit'; diff --git a/vercel.json b/vercel.json index 052e66d..f28fcc2 100644 --- a/vercel.json +++ b/vercel.json @@ -16,6 +16,15 @@ "value": "*" } ] + }, + { + "source": "/images/(.*)", + "headers": [ + { + "key": "Cache-Control", + "value": "public, max-age=604800, s-maxage=2592000, stale-while-revalidate=86400" + } + ] } ], "rewrites": [