Skip to the content

Capsid menu

app/root.tsx

145 lines and 10 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.

Functions

The functions of root.tsx
FunctionLinesComplexityCoveredCRAPMutation
<anonymous:28>28 to 322No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
loader35 to 391No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
meta45 to 494No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
links51 to 821No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
Layout84 to 1508No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:94>94 to 941No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:97>97 to 972No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
<anonymous:107>107 to 1071No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
App152 to 1541No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
ErrorBoundary156 to 1581No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports app/components/error-page.tsx, app/lib/context.ts, app/lib/enhance-loader.mjs, app/lib/seo.ts, app/lib/theme.ts, app/lib/timing.ts. Imported by app/routes/admin.tsx, app/routes/workspace.tsx.

Source

import {
  isRouteErrorResponse,
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useMatches,
  useRouteLoaderData,
} from "react-router";

import { ErrorPage } from "~/components/error-page";
import { getNonce } from "~/lib/context";
import { ENHANCE_LOADER } from "~/lib/enhance-loader.mjs";
import { SITE, SITE_ORIGIN } from "~/lib/seo";
import { colorSchemeMeta, themeAttribute, themeFromRequest } from "~/lib/theme";
import { timingsContext, wantsTiming, type Timings } from "~/lib/timing";

import type { Route } from "./+types/root";

import interCoreUrl from "./fonts/inter-latin-core-normal.woff2?url";
// `?url`, not a bare import, which would fold the math bytes into the stylesheet every page links.
import katexCssUrl from "./styles/katex.generated.css?url";

// Middleware, not the loader: loaders run alongside each other, so a collector made in one is
// invisible to another.
export const middleware: Route.MiddlewareFunction[] = [
  async ({ request, context }, next) => {
    const timings: Timings | undefined = wantsTiming(new URL(request.url)) ? [] : undefined;
    context.set(timingsContext, { timings });
    return next();
  },
];

export function loader({ request, context }: Route.LoaderArgs) {
  // The nonce is carried through the loader because `Layout` cannot reach the request context.
  // Admin only: undefined on every public page, which is edge-cached and trusted by hash instead.
  return { theme: themeFromRequest(request), nonce: getNonce(context) };
}

/**
 * Only on the error path: an unmatched URL matches no route but this one, so without it the 404 page
 * has no title (WCAG 2.4.2). A matched route's own `meta` wins, so its not-found title stands.
 */
export function meta({ error }: Route.MetaArgs) {
  if (!error) return [];
  const notFound = isRouteErrorResponse(error) && error.status === 404;
  return [{ title: `${notFound ? "Page not found" : "Error"} | ${SITE.name}` }];
}

export const links: Route.LinksFunction = () => [
  // `links` from every matched route are merged and `meta` is NOT, so these ride on every page.
  { rel: "icon", href: "/favicon.ico", sizes: "48x48" },
  { rel: "icon", href: "/dustin-edwards-favicon.svg", type: "image/svg+xml" },
  { rel: "apple-touch-icon", href: "/dustin-edwards-apple-touch-icon.png" },
  { rel: "manifest", href: "/site.webmanifest" },
  // feed+json although served as application/json: readers scan autodiscovery for the feed types.
  {
    tagName: "link",
    rel: "alternate",
    type: "application/rss+xml",
    title: `${SITE.name} blog`,
    href: `${SITE_ORIGIN}/writing/rss.xml`,
  },
  {
    tagName: "link",
    rel: "alternate",
    type: "application/feed+json",
    title: `${SITE.name} blog`,
    href: `${SITE_ORIGIN}/writing/feed.json`,
  },
  // `crossorigin` is mandatory: fonts are fetched in anonymous CORS mode whatever their origin, so a
  // preload without it is a different request and the file is fetched twice. The core, which every
  // page draws; the rest of the latin range loads only where a page needs it (app.css).
  {
    rel: "preload",
    href: interCoreUrl,
    as: "font",
    type: "font/woff2",
    crossOrigin: "anonymous",
  },
];

export function Layout({ children }: { children: React.ReactNode }) {
  // Layout also renders the error boundary, where the root loader may not have run.
  const data = useRouteLoaderData<typeof loader>("root");
  const theme = data ? themeAttribute(data.theme) : undefined;
  // No fallback: a made-up nonce would satisfy the markup while matching nothing in the header.
  const nonce = data?.nonce;

  // Hydration is opt-in by route (`handle = { hydrate: true }`).
  const matches = useMatches();
  const hydrates = matches.some(
    (match) => (match.handle as { hydrate?: boolean } | undefined)?.hydrate === true,
  );
  // The admin and lab planes are Capsomer's shell, which brings its own skip link and none of the public CSS.
  const adminPlane = matches.some((match) => match.id === "routes/admin" || match.id === "routes/workspace");

  // Not React's `precedence` hoisting: it lifts the sheet above the color-scheme meta, which must
  // arrive before the first stylesheet request.
  const postData = useRouteLoaderData("routes/blog.$slug") as { hasMath?: boolean } | undefined;
  const previewData = useRouteLoaderData("routes/preview.$token") as
    | { hasMath?: boolean }
    | undefined;
  // Unconditional in the editor: hasMath describes what was SAVED, not what the author is typing.
  const editorWantsMath = matches.some(
    (match) => (match.handle as { math?: boolean } | undefined)?.math === true,
  );
  const linksMath =
    postData?.hasMath === true || previewData?.hasMath === true || editorWantsMath;

  return (
    <html lang="en" data-theme={theme} data-cap-site="dustinedwards">
      <head>
        <meta charSet="utf-8" />
        {/* Before any stylesheet: data-theme means nothing to the browser until the CSS is parsed. */}
        <meta name="color-scheme" content={colorSchemeMeta(data?.theme ?? "system")} />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        {/* Literal hexes: browser chrome does not resolve a custom property. check:contrast
            asserts they match the palette. */}
        <meta name="theme-color" media="(prefers-color-scheme: light)" content="#f4efe6" />
        <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1c1916" />
        <Meta />
        {/* <Links> takes a nonce from the framework context and has no opt-out. */}
        <Links />
        {/* After <Links /> so a site rule wins over the upstream katex rule. No `precedence`: React
            would lift the element above the color-scheme meta. */}
        {linksMath ? <link rel="stylesheet" href={katexCssUrl} /> : null}
      </head>
      <body>
        {adminPlane ? null : (
          <a className="skip-link" href="#main">
            Skip to content
          </a>
        )}
        {children}
        {/* JSON-LD blocks carry no nonce: `script-src` does not gate application/ld+json. */}
        {hydrates ? (
          <>
            <ScrollRestoration nonce={nonce} />
            <Scripts nonce={nonce} />
          </>
        ) : null}
        {/* Last, so every <Enhance> marker is parsed before it runs. Allowed by its sha256 in the
            policy; raw HTML, because the hash is of these exact bytes. The nonce is admin's. */}
        <script nonce={nonce} dangerouslySetInnerHTML={{ __html: ENHANCE_LOADER }} />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
  return <ErrorPage error={error} />;
}