PortalLayout, never StarlightPageEvery tool page in this folder MUST use PortalLayout. See
/CLAUDE.md (“Portal pages vs. Starlight”) for the full rule and the
reasoning.
Copy this when starting a new tool page (e.g. /tools/my-tool.astro):
---
import PortalLayout from "../../layouts/PortalLayout.astro";
import MyTool from "../../components/tools/MyTool";
import { getUserAskTier } from "../../lib/access";
import { normalizeTierSlug } from "../../lib/tiers";
const auth = Astro.locals.auth?.();
if (!auth?.userId) {
return Astro.redirect("/sign-in?redirect_url=/tools/my-tool");
}
const tier = await getUserAskTier(auth);
const normalizedTier = normalizeTierSlug(tier);
if (!["principal", "team"].includes(normalizedTier)) {
return Astro.redirect("/pricing?upgrade=principal");
}
const user = await Astro.locals.currentUser?.();
const firstName =
user?.firstName ??
user?.emailAddresses?.[0]?.emailAddress?.split("@")[0] ??
null;
---
<PortalLayout
title="My Tool"
isSignedIn={true}
normalizedTier={normalizedTier}
firstName={firstName ?? undefined}
>
<div class="tool-shell">
<div class="tool-header">
<a class="tool-back" href="/tools">← Tools</a>
{/* No body <h1>: the page title is rendered in the top bar via the
PortalLayout `title` prop, so a heading here is a redundant 2nd h1. */}
<p class="tool-desc">Short description of what this tool does.</p>
</div>
<MyTool client:load />
</div>
</PortalLayout>
<style>
.tool-shell {
max-width: 80rem;
padding: 0.5rem 0 3rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.tool-header { display: flex; flex-direction: column; gap: 0.5rem; }
.tool-back {
font-family: var(--lp-font-mono);
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--lp-steel);
text-decoration: none;
width: max-content;
}
.tool-back:hover { color: var(--lp-text); }
.tool-desc {
margin: 0;
font-size: 0.9rem;
color: var(--lp-steel);
line-height: 1.5;
max-width: 56rem;
}
</style>
Add a corresponding entry to src/pages/tools.astro under the
appropriate category group so the tool is discoverable.
Tool React components should use the portal CSS tokens
(var(--lp-text), var(--lp-steel), var(--lp-border),
var(--lp-indigo), etc.) so they feel native to the portal.