diff --git a/.claude/skills/physlib-design-language/SKILL.md b/.claude/skills/physlib-design-language/SKILL.md new file mode 100644 index 0000000..16f4820 --- /dev/null +++ b/.claude/skills/physlib-design-language/SKILL.md @@ -0,0 +1,153 @@ +--- +name: physlib-design-language +description: Design language, layout, and data-generation conventions for the Physlib website (web2 Next.js app). Load before adding or modifying any page, component, or data-generation script in web2, so new UI matches the rest of the site instead of inventing a one-off layout. +--- + +# Physlib website design language + +This site (`web2/`) has established, repeated conventions. New pages should +reuse them rather than build ad-hoc layouts — a page that looks or is wired +differently from its siblings is a design bug, not a stylistic choice. + +## Page shell + +Every content page wraps its content the same way: + +```tsx +
+

Page Title

+

Intro copy.

+ {/* page content */} +
+``` + +`max-w-5xl` is the default. Don't widen it to fit extra UI (e.g. a sidebar) — +see below, sidebars are not page content. + +## Root layout & the left sidebar slot + +`src/app/layout.tsx` renders a persistent flex row: + +``` + +
+ +
{children}
+
+``` + +`ConditionalSidebar` (`src/components/conditional-sidebar.tsx`) picks a +sidebar by route: + +```ts +if (pathname === "/") return null; +if (pathname.startsWith("/monthly-updates")) return ; +if (pathname.startsWith("/api-tracker")) return ; +return ; // default: auto-built from h2/h3 on the page +``` + +**If a new page needs left-rail navigation (a list of items, a tree, a table +of contents), add a branch here and a dedicated sidebar component.** Do not +build the sidebar inline inside the page/client component with a local flex +layout — that duplicates the shell, breaks the shared sticky/scroll behavior, +and is the mistake this skill exists to prevent (it's what the API Tracker +page did on the first pass, before being corrected to match this pattern). + +### Sidebar visual pattern + +Every left sidebar (`TableOfContents`, `MonthSidebar`, `ApiTrackerSidebar`) +uses the same markup: + +```tsx + +``` + +- Hidden below `lg`; sticky at `top-16`, independently scrollable + (`max-h-[calc(100vh-4rem)] overflow-y-auto`). +- Active item = `text-accent font-medium` + a small accent dot prefix. Inactive + = `text-muted hover:text-foreground`. +- Nested/indented levels use extra left padding (either a Tailwind step like + `pl-6` for a fixed two-level case, or `style={{ paddingLeft: 8 + depth * 16 }}` + for an arbitrary-depth tree) — never a second nested `