Component Architecture
Components are organized in four atomic-design layers under components/. Route pages
almost always compose from the top layer (organisms) inside <SiteShell> — there is
rarely a reason to write markup directly in a page.tsx.
ui/ primitives
↓
molecules/ small compositions of ui/
↓
organisms/ full page sections
↓
templates/ the per-page wrapper (SiteShell)ui/ — primitives
Low-level, style-focused building blocks with no page-specific meaning:
button, tag, stat, eyebrow, accordion, animated-link-label,
brand-mark, corner-cta, history-back-button, icon-feature-card,
logo-strip, parallax-cover-image.
molecules/ — small compositions
Built from ui/ primitives, still content-agnostic (no fetching, no page-specific
logic): content-card, pricing-card, quote-card, team-member-card,
stat-highlight-card, avatar-identity, section-heading, split-intro, step-grid,
numbered-step, numbered-list-item, tag-list, toc-card, detail-sidebar,
accordion-list, media-content-card, info-card, field.
organisms/ — full page sections
Everything a route composes to build a page: hero variants (hero-section through
hero-section-7), stats-section, pricing-adjacent sections, article-grid-section
(and -2/-3/-4 variants), carousel-section, cta-banner, cta-cards,
accordion-section, feature-list-section, features-section,
bento-highlight-section, comparison-table-section, gallery,
gradient-image-gallery-section, logo-strip-section, newsletter-section,
people-grid-section, pinned-showcase-section, quote-section-1/2/3,
stat-bar-section, stat-results-section, steps-section/steps-section-2,
ticker-section, category-tabs, fact-grid-section, featured-item-section,
icon-feature-grid-section, media-feature-grid-section, numbered-list-section,
form-panel-section, plus the always-present header and footer, and
article-specific organisms (article-card, article-detail-layout,
author-share-section).
There are multiple numbered variants of some sections (e.g. seven hero sections). Before building a new section from scratch, check whether an existing numbered variant already does what you need — the multiplicity exists specifically so you can pick a layout without writing new code.
Forms live in organisms/forms/.
templates/site-shell.tsx — the per-page wrapper
Every route page renders its content inside <SiteShell>:
export function SiteShell({
children,
footerVariant,
}: {
children: ReactNode;
footerVariant: FooterVariant;
}) {
return (
<SiteMotionProvider>
<Header showcase={footerVariant === "minimal"} />
<CornerCta
href={siteWideContent.buyNow.href}
label={siteWideContent.buyNow.label}
/>
<SiteAnimations>
<div aria-hidden="true" className="h-21 sm:h-23.5" />
<main data-main-container="">
{children}
<Footer variant={footerVariant} />
</main>
</SiteAnimations>
</SiteMotionProvider>
);
}It wires up the GSAP motion context (SiteMotionProvider), the scroll-animation
wrapper (SiteAnimations — see Animation System), the header, the
footer, and the always-present CornerCta (a fixed-position buy-now button, fed the
buyNow link from content/site-wide.tsx; pass position="left" to pin it to the other
corner). A route page’s job is just to pick a footerVariant and list which organisms go
in between.
footerVariant
SiteShell takes footerVariant: "full" | "minimal" (the FooterVariant type in
components/organisms/footer.tsx), which does two things:
- Selects the footer.
"full"renders the four-column marketing footer — nav columns, contact details, and a newsletter signup."minimal"renders a single-row footer — brand lockup, copyright, and thefooter.showcaseLinkslinks — used only by the template’s own demo page. - Toggles header behavior, via
showcase={footerVariant === "minimal"}passed toHeader. Theshowcaseflag swaps which nav link set the header renders (nav.showcasevs.nav.primary, both fromcontent/site-wide.tsx) and enables smooth-anchor scrolling for on-page section links — it’s how the template’s own demo page gets different nav from real site pages.
For real site pages, use "full". Reserve "minimal" for the template’s own showcase
page (app/page.tsx) — using it elsewhere pulls in the showcase nav links and anchor
behavior.
When to add a new component vs. reuse
- Need a page section that’s a close variant of an existing organism (different heading layout, different number of columns)? Check the numbered variants first — there’s a good chance one already fits.
- Need a genuinely new kind of section? Build it in
organisms/, composing frommolecules//ui/rather than writing raw markup — this keeps spacing, typography, and responsive behavior consistent with the rest of the site. - Need a new small reusable piece (a card shape, a labeled stat)? That belongs in
molecules/orui/depending on how composed it is, not inlined into an organism.