diff --git a/src/app/index.client.tsx b/src/app/index.client.tsx
index 92feebdf..52bc900f 100644
--- a/src/app/index.client.tsx
+++ b/src/app/index.client.tsx
@@ -1,12 +1,12 @@
import * as ReactDOM from 'react-dom/client'
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
-import { routes } from './routes.js'
+import { routesObjects } from './routes.js'
import { hydrateLazyRoutes } from './utils.js'
hydrate()
async function hydrate() {
- await hydrateLazyRoutes(routes)
- const router = createBrowserRouter(routes)
+ await hydrateLazyRoutes(routesObjects)
+ const router = createBrowserRouter(routesObjects)
ReactDOM.hydrateRoot(document.getElementById('app')!, )
}
diff --git a/src/app/index.server.tsx b/src/app/index.server.tsx
index a2a8c05e..ca085757 100644
--- a/src/app/index.server.tsx
+++ b/src/app/index.server.tsx
@@ -1,7 +1,7 @@
import type { Request } from '@tinyhttp/app'
import * as ReactDOMServer from 'react-dom/server'
import { Helmet } from 'react-helmet'
-import { Route, Routes } from 'react-router-dom'
+import { Routes } from 'react-router-dom'
import {
type StaticHandlerContext,
StaticRouter,
@@ -10,27 +10,13 @@ import {
createStaticRouter,
} from 'react-router-dom/server.js'
-import { routes } from './routes.js'
+import { routesElements, routesObjects } from './routes.js'
import { createFetchRequest } from './utils.js'
export async function prerender(location: string) {
- const unwrappedRoutes = await Promise.all(
- routes.map(async (route) => {
- const lazyRoute = await route.lazy()
- return {
- path: route.path,
- element: lazyRoute.element,
- }
- }),
- )
-
const body = ReactDOMServer.renderToString(
-
- {unwrappedRoutes.map((route) => (
-
- ))}
-
+ {routesElements}
,
)
@@ -38,7 +24,7 @@ export async function prerender(location: string) {
}
export async function render(req: Request) {
- const { query, dataRoutes } = createStaticHandler(routes)
+ const { query, dataRoutes } = createStaticHandler(routesObjects)
const fetchRequest = createFetchRequest(req)
const context = (await query(fetchRequest)) as StaticHandlerContext
@@ -55,16 +41,11 @@ export async function render(req: Request) {
function head() {
const helmet = Helmet.renderStatic()
-
- const themeKey = 'vocs.theme'
- const themeScript = ``
-
return `
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
${helmet.style.toString()}
${helmet.script.toString()}
- ${themeScript}
`
}
diff --git a/src/app/main.tsx b/src/app/main.tsx
deleted file mode 100644
index 019fd10f..00000000
--- a/src/app/main.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { type ReactNode } from 'react'
-import { useApplyCssTransition } from './hooks/useApplyCssTransition.js'
-
-export function Main({ children }: { children: ReactNode }) {
- useApplyCssTransition()
- return (
-
- )
-}
diff --git a/src/app/root.tsx b/src/app/root.tsx
new file mode 100644
index 00000000..8edc2f29
--- /dev/null
+++ b/src/app/root.tsx
@@ -0,0 +1,7 @@
+import { type ReactNode } from 'react'
+import { useApplyCssTransition } from './hooks/useApplyCssTransition.js'
+
+export function Root({ children }: { children: ReactNode }) {
+ useApplyCssTransition()
+ return {children}
+}
diff --git a/src/app/routes.tsx b/src/app/routes.tsx
index c027e9c0..a9e494d0 100644
--- a/src/app/routes.tsx
+++ b/src/app/routes.tsx
@@ -1,12 +1,12 @@
import type { MDXComponents } from 'mdx/types.js'
import { Helmet } from 'react-helmet'
-import { type RouteObject } from 'react-router-dom'
+import { Outlet, Route, type RouteObject, createRoutesFromElements } from 'react-router-dom'
import { routes as routes_virtual } from 'virtual:routes'
import { A } from './components/A.js'
import { CodeGroup } from './components/CodeGroup.js'
import { FrontmatterHead } from './components/FrontmatterHead.js'
-import { Main } from './main.js'
+import { Root } from './root.js'
const components: MDXComponents = {
a: A,
@@ -16,21 +16,37 @@ const components: MDXComponents = {
},
}
-export const routes = routes_virtual.map((route_virtual) => ({
- path: route_virtual.path,
- lazy: async () => {
- const { frontmatter, head, ...route } = await route_virtual.lazy()
- return {
- ...route,
- element: (
- <>
- {head && {head}}
- {frontmatter && }
-
-
-
- >
- ),
- } satisfies RouteObject
- },
-}))
+export const routesElements = (
+
+
+
+ }
+ >
+ {routes_virtual.map((route_virtual) => (
+ {
+ const { frontmatter, head, ...route } = await route_virtual.lazy()
+ return {
+ ...route,
+ element: (
+ <>
+ {head && {head}}
+ {frontmatter && }
+
+
+
+ >
+ ),
+ } satisfies RouteObject
+ }}
+ />
+ ))}
+
+)
+
+export const routesObjects = createRoutesFromElements(routesElements)
diff --git a/src/app/utils/initialize-theme.ts b/src/app/utils/initialize-theme.ts
new file mode 100644
index 00000000..f0ee2fbe
--- /dev/null
+++ b/src/app/utils/initialize-theme.ts
@@ -0,0 +1,13 @@
+const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+
+const storedTheme = localStorage.getItem('vocs.theme')
+const theme = storedTheme || 'dark'
+
+if (theme === 'dark') document.documentElement.classList.add('dark')
+
+if (!storedTheme)
+ // Update the theme if the user changes their OS preference
+ darkModeMediaQuery.addEventListener('change', ({ matches: isDark }) => {
+ if (isDark) document.documentElement.classList.add('dark')
+ else document.documentElement.classList.remove('dark')
+ })
diff --git a/src/build.ts b/src/build.ts
index a86802de..647240b5 100644
--- a/src/build.ts
+++ b/src/build.ts
@@ -30,4 +30,19 @@ export async function build({ outDir = 'dist', ssr = false }: BuildParameters =
},
root: __dirname,
})
+
+ // initialize theme script
+ await vite.build({
+ build: {
+ lib: {
+ formats: ['iife'],
+ name: 'theme',
+ entry: [resolve(__dirname, './app/utils/initialize-theme.ts')],
+ },
+ minify: true,
+ outDir: resolve(outDir, ssr ? 'client' : ''),
+ emptyOutDir: false,
+ },
+ configFile: undefined,
+ })
}
diff --git a/src/index.html b/src/index.html
index 5fbd50d8..4e0ed0b8 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,6 +3,7 @@
+
diff --git a/src/prerender.ts b/src/prerender.ts
index 279882b9..7917b4c1 100644
--- a/src/prerender.ts
+++ b/src/prerender.ts
@@ -21,7 +21,10 @@ export async function prerender(args: PrerenderParameters = {}) {
// Prerender each route.
for (const route of routes) {
const { head, body } = await mod.prerender(route)
- const html = template.replace('', body).replace('', head)
+ const html = template
+ .replace('', body)
+ .replace('', head)
+ .replace('/app/utils/initialize-theme.ts', '/initialize-theme.iife.js')
const filePath = `${route.endsWith('/') ? `${route}index` : route}.html`.replace(/^\//, '')
const path = resolve(outDir_resolved, filePath)
const pathDir = dirname(path)