Colour palette

Override the dark or light palette via themeColor / themeColorLight:

blogTheme({
  
themeColor: {
    primary: string;
    secondary: string;
    bg: string;
    surface: string;
}
themeColor
: {
primary: stringprimary: '#fb923c', secondary: stringsecondary: '#dc2626', bg: stringbg: '#1a0a00', surface: stringsurface: '#2d1200', },
themeColorLight: {
    primary: string;
    bg: string;
    surface: string;
}
themeColorLight
: {
primary: stringprimary: '#c2410c', bg: stringbg: '#fef9f0', surface: stringsurface: '#fde8c8', }, })
ts

Any key you omit keeps the theme default. At runtime these are injected as CSS custom properties scoped to [data-theme="dark"] .sp-blog-root and [data-theme="light"] .sp-blog-root.

CSS custom properties

All components read these variables. Override them in your own styles for fine-grained control:

VariablePurpose
--sp-blog-bgPage background
--sp-blog-surfaceCard/sidebar background
--sp-blog-borderSeparators, card borders
--sp-blog-textPrimary text (titles)
--sp-blog-contentBody text
--sp-blog-mutedMeta text (dates, counts)
--sp-blog-primaryAccent (links, tags, buttons)
--sp-blog-secondarySecondary accent

Swapping components

The scaffolded files under src/routes/ import components from @sveltepress/theme-blog/components/*. To replace one — say the post cards — edit src/routes/+page.svelte and substitute your own component:

src/routes/+page.svelte
<script lang="ts">
  import 
type MyGrid = SvelteComponent<Record<string, any>, any, any>
const MyGrid: LegacyComponentType
MyGrid
from '$lib/MyGrid.svelte'
const { const data: anydata } =
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
$
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();
props
()
</script> <const MyGrid: LegacyComponentTypeMyGrid posts: anyposts={const data: anydata.posts} />
svelte

The theme re-exports these building blocks:

  • @sveltepress/theme-blog/GlobalLayout.svelte
  • @sveltepress/theme-blog/PageLayout.svelte
  • @sveltepress/theme-blog/PostLayout.svelte
  • @sveltepress/theme-blog/components/MasonryGrid.svelte
  • @sveltepress/theme-blog/components/Timeline.svelte
  • @sveltepress/theme-blog/components/Pagination.svelte
  • @sveltepress/theme-blog/components/Sidebar.svelte
  • @sveltepress/theme-blog/components/SearchModal.svelte
  • @sveltepress/theme-blog/components/ReadingProgress.svelte
  • @sveltepress/theme-blog/components/GiscusComments.svelte
  • @sveltepress/theme-blog/components/RelatedPosts.svelte
  • @sveltepress/theme-blog/components/TaxonomyHeader.svelte
  • @sveltepress/theme-blog/components/AuthorCard.svelte
  • @sveltepress/theme-blog/components/AuthorProfile.svelte

Data modules

The package ships TypeScript declarations for the virtual modules used by scaffolded routes. The main customization points are:

ModuleExport
virtual:sveltepress/blog-posts-metaposts: BlogPostMeta[]
virtual:sveltepress/blog-tags-indextags: Array<{ name, count }>
virtual:sveltepress/blog-categories-indexcategories: Array<{ name, count }>
virtual:sveltepress/blog-configblogConfig: BlogThemeOptions

Per-post, per-tag, and per-category records are also exposed through literal module IDs such as virtual:sveltepress/blog-post/<slug>. virtual:sveltepress/blog-runtime contains absolute cache paths for server loads and must not be imported into client components.

Deploying under a subpath

Read BASE_PATH in svelte.config.js and SITE_URL in vite.config.ts:

svelte.config.js
export default {
  kit: {
    paths: {
      base: process.env.BASE_PATH ?? '',
      relative: false,
    },
  },
}
js
vite.config.ts
blogTheme({
  base: stringbase: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

{
  TERM: 'xterm-256color',
  SHELL: '/usr/local/bin/bash',
  USER: 'maciej',
  PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/Users/maciej',
  EDITOR: 'vim',
  SHLVL: '1',
  HOME: '/Users/maciej',
  LOGNAME: 'maciej',
  _: '/usr/local/bin/node'
}

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo

While the following will:

import { env } from 'node:process';

env.foo = 'bar';
console.log(env.foo);

Assigning a property on process.env will implicitly convert the value to a string. This behavior is deprecated. Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

import { env } from 'node:process';

env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'

Use delete to delete a property from process.env.

import { env } from 'node:process';

env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined

On Windows operating systems, environment variables are case-insensitive.

import { env } from 'node:process';

env.TEST = 1;
console.log(env.test);
// => 1

Unless explicitly specified when creating a Worker instance, each Worker thread has its own copy of process.env, based on its parent thread's process.env, or whatever was specified as the env option to the Worker constructor. Changes to process.env will not be visible across Worker threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner unlike the main thread.

@sincev0.1.27
env
.string | undefinedSITE_URL ?? 'http://localhost:4173',
// ... })
ts

Then build with both set, e.g. for GitHub Pages project site at user.github.io/repo/blog:

BASE_PATH=/repo/blog SITE_URL=https://user.github.io/repo/blog \
  pnpm build
bash

All internal links in the theme components use SvelteKit's $app/paths base, so they resolve correctly under the subpath. OG image URLs and RSS item URLs use SITE_URL so social crawlers get fully-qualified URLs.

Adding pages

Anything under src/routes/ that isn't reserved by the scaffolder is yours. Wrap custom pages in GlobalLayout automatically by leaving src/routes/+layout.svelte untouched — every route renders inside the sidebar + main grid.