Frontend
Design system, component architecture, motion physics, SSR/RSC patterns, forms, and accessibility.
Design System: Glassmorphism 2.0
Surface System
Every elevated surface uses the Crystal Card ā translucent glass with iridescent overlay:
<div className="relative overflow-hidden rounded-3xl border border-white/10 p-6 backdrop-blur-2xl bg-white/5 hover:bg-white/10 transition-all duration-500">
<div className="absolute inset-0 bg-linear-to-br from-white/40 via-transparent to-transparent opacity-20 pointer-events-none" />
<div className="relative z-10">{children}</div>
</div>Glass Tokens
| Layer | Class | Use Case |
|---|---|---|
| Crystal Card | bg-white/5 backdrop-blur-2xl border-white/10 | Primary containers |
| Crystal Hover | hover:bg-white/10 transition-all duration-500 | Interactive cards |
| Depth Layer | bg-white/[0.02] backdrop-blur-xl border-white/[0.06] | Nested panels |
| Elevated | bg-white/10 backdrop-blur-3xl border-white/15 | Modals, dropdowns |
| Iridescent | bg-linear-to-br from-white/40 via-transparent to-transparent opacity-20 | Light refraction |
GlowCard
Animate gradient glow on hover:
import { GlowCard } from "@/components/ui/glow-card";
<GlowCard>
<h3 className="font-heading font-bold">Feature</h3>
<p className="font-body text-muted-foreground">Description</p>
</GlowCard>;Typography
Tri-Font System
| Font | CSS Variable | Class | Usage |
|---|---|---|---|
| Space Grotesk | --font-heading | font-heading | Headlines, CTAs, nav |
| DM Sans | --font-body | font-body | Body, descriptions |
| Fragment Mono | --font-heading | font-heading | Code, data, metrics |
Scale
| Element | Classes |
|---|---|
| h1 | font-heading text-4xl md:text-5xl font-bold tracking-tight |
| h2 | font-heading text-2xl md:text-3xl font-semibold tracking-tight |
| h3 | font-heading text-lg font-semibold |
| Body | font-body text-base leading-relaxed |
| Caption | font-body text-sm text-muted-foreground |
| Metric | font-heading text-xs uppercase tracking-wider |
Color Palette
OKLCH Tokens
Defined in app/globals.css with :root (light) and .dark variants.
- ā
Use:
text-foreground,bg-background,text-muted-foreground,bg-primary - ā Never:
text-white,bg-black,text-gray-500(breaks dark mode)
| Token | Light | Dark |
|---|---|---|
--background | Near-white | Deep blue-black |
--foreground | Near-black | Near-white |
--primary | Purple (oklch(46.5% 0.24 302)) | Bright purple (oklch(56% 0.29 301)) |
--muted | Light gray | Dark gray |
--destructive | Red | Red (dark-adjusted) |
Dark mode via next-themes + ThemeProvider.
Dark Mode Enforcement Policy
Every component that uses hardcoded light-mode Tailwind classes must include a corresponding dark: override. This is a non-negotiable standard enforced through deep audits.
| Light-Mode Class | Required Dark Override | Context |
|---|---|---|
bg-white | dark:bg-slate-950 | Card backgrounds, surfaces |
bg-slate-50 | dark:bg-slate-950 | Muted/alternating surfaces |
bg-white/50 | dark:bg-white/5 | Glass translucent layers |
border-slate-200 | dark:border-white/5 | Container borders |
border-white | dark:border-slate-800 | Decorative borders |
bg-emerald-50 | dark:bg-emerald-950/30 | Accent-colored surfaces |
hover:bg-white/40 | dark:hover:bg-white/5 | Interactive row/card hover |
bg-slate-100/80 | dark:bg-slate-800/80 | Badge/pill backgrounds |
bg-slate-900/80 | dark:bg-white/5 with dark:border-white/10 | Floating navigation pills |
Rule: If a Tailwind class contains a color or opacity value tied to a light background, it needs a
dark:sibling. No exceptions.
Branding & Logos (Dynamic Theme)
Whenever the primary brand logo is displayed (Header, Footer, Sidebar, Auth), you must use dynamic logo states to conditionally render the correct logo variant depending on the active theme:
{
/* Light Mode Logo */
}
<Image
src="/logo.png"
className="dark:hidden"
alt="Logo"
width={120}
height={40}
/>;
{
/* Dark Mode Logo */
}
<Image
src="/logo-light.png"
className="hidden dark:block"
alt="Logo"
width={120}
height={40}
/>;Rule (Non-Logo Assets): Any visual asset (illustration, icon, decorative graphic) that is designed for a light background MUST have a dark-mode counterpart or use CSS
filter/invertā never display a light-only graphic on a dark background unchecked.
Image Compliance (Aspect Ratio Safety)
To prevent Next.js hydration layout shifts and aspect ratio console warnings, every next/image instance for logos and fixed-aspect assets MUST include the style={{ width: "auto", height: "auto" }} attribute.
<Image
src="/logo.png"
alt="Logo"
width={135}
height={32}
className="object-contain shrink-0"
style={{ width: "auto", height: "auto" }} // Required for compliance
/>Rule: If you use CSS (like Flexbox or grid) that might constrain the image layout, you must use
style={{ width: "auto", height: "auto" }}. Ensure thewidthandheightprops precisely match the intrinsic ratio of the source asset (e.g.,1364x256scales to135x25).
Pricing Render Contract
components/marketing/common/pricing-section.tsx is intentionally a Client Component because it owns billing interval controls, authenticated checkout state, and provider-backed plan actions. Its authenticated billing reads are parallelized with Promise.all() and committed through one PricingBillingContext state update. This avoids a profile -> purchase -> subscription waterfall and prevents three independent render commits during pricing-page hydration.
Required invariants:
- Keep profile, lifetime purchase, and active subscription reads independent unless a future data dependency is proven in code.
- Keep Supabase reads projected to the exact fields consumed by the pricing UI.
- Commit derived billing state once per auth refresh.
- Preserve the empty billing context fallback for signed-out and failed-read states.
Layout Synchronization (CSS Variables)
For decoupled components that affect global layout (e.g., the Impersonation Banner), the application uses reactive CSS variables to ensure zero-jitter transitions.
- Variable:
--topbar-height(defaults to0px). - Management: Handled via
useLayoutEffectin the banner component to update the DOM before the paint cycle. - Consumption: Components like
AppSidebarandAdminPageShellusevar(--topbar-height, 0px)for their top offset. - SSR Safety: The banner injects a
<style>tag during server rendering to prevent layout shift on the initial page load.
Theme Toggle & Locale Switcher Integration
The <ThemeToggle /> and <LocaleSwitcher /> components are globally distributed in the application:
- Marketing Header: Theme toggle in the main navigation bar.
- Dashboard Sidebars: Both controls live inside
SidebarHeaderin the sharedAppSidebarcomponent, which automatically propagates to Super-Admin and Super-Admin panels. - Collapsible Behavior: The
<LocaleSwitcher />usesgroup-data-[collapsible=icon]:hiddenon its text label so only the globe icon remains when the sidebar is minimized. - Auth Pages: Both controls are absolutely positioned at the top-right (
absolute top-2 right-2) of each auth form container, visible on desktop, tablet, and mobile. Render order:<LocaleSwitcher />then<ThemeToggle />. The locale switcher is gated behindisI18nEnabled().
The <ThemeToggle /> features premium micro-interactions:
| Property | Value |
|---|---|
| Rotation | Spring: stiffness: 300, damping: 20 |
| Hover Scale | 1.1 |
| Tap Scale | 0.9 |
| Sun Glow | drop-shadow-[0_0_8px_rgba(251,191,36,0.5)] |
| Moon Glow | drop-shadow-[0_0_8px_rgba(129,140,248,0.5)] |
Component Inventory
57 shadcn/ui components ā you own the code in components/ui/:
| Category | Components |
|---|---|
| Layout | card, separator, scroll-area, sheet, sidebar, tabs, accordion, collapsible |
| Forms | button, input, floating-label-input, textarea, checkbox, radio-group, select, switch, slider, calendar, date-picker, form |
| Feedback | alert, alert-dialog, badge, progress, skeleton, sonner, tooltip |
| Navigation | breadcrumb, dropdown-menu, menubar, navigation-menu, pagination, command |
| Data | table, chart (Recharts), avatar, aspect-ratio |
| Overlay | dialog, popover, hover-card, context-menu |
| Custom | glow-card, floating-label-input, spinner |
| Dashboard | ApiKeysCard, MfaSettings, PasswordSettings |
| Super-Admin | ContactDetailSheet (Glass 2.0), ContactsTable, TransactionsTable (Glass 2.0 + CSV Export + Filters) |
XSS Prevention (DOMPurify)
To mitigate Cross-Site Scripting (XSS) risks when rendering user-generated or dynamic HTML content, the frontend incorporates dompurify. Any dynamically injected rich text must be sanitized server-side or client-side using DOMPurify.sanitize(content) before being dangerously set.
Premium Administrative Interfaces (Glassmorphism 2.0)
Administrative detail sheets (e.g., ContactDetailSheet) must implement the "High-Fidelity Detail" pattern:
- Surface:
backdrop-blur-2xl bg-white/5 border-white/10with iridescent overlays. - Motion: Staggered entrance animations for content blocks using
itemVariants. - Typography: Space Grotesk for section headers, Fragment Mono for metadata/timestamps.
- Interactive Actions: Integrated 'Copy' and 'Reply' actions with spring feedback.
Defensive Deletion Workflow
All administrative deletion operations (e.g., in ContactsTable, UserManagement) MUST follow the "Defensive Deletion" pattern:
- Trigger: Secondary-style delete button with
pendingIdstate tracking. - Confirmation: Mandatory
AlertDialog(notconfirm()) with destructive styling. - Execution: Server Action with optimistic UI update or localized toast feedback.
Rules
| Rule | Detail |
|---|---|
| Forms | FloatingLabelInput (not separate Input + Label) |
| Loading | Spinning ā³ (<span className="animate-spin">ā³</span>) |
| Dialogs | Enter key via onKeyDown |
| Actions | CornerDownLeft icon indicator |
| Icon-only | Must have aria-label |
| Classes | Always use cn() from @/lib/utils |
Recharts Hydration Stability
Raw ResponsiveContainer evaluations of parent dimensions cause hydration mismatch warnings (-1 dimensions) and require redundant state tracking.
Required Implementation:
- Mandate the use of the centralized
ChartContainerfor all visualizations. - Remove any deprecated
ResponsiveContainerusages with manualmountedguards. ChartContainerinherently manages responsive scaling and hydration stability without bundle bloat.
"use client";
import { AreaChart } from "recharts";
import { ChartContainer } from "@/components/ui/chart";
export function StableChart({ data }) {
// ? NO MORE manual mounted states or ResponsiveContainer
return (
<ChartContainer config={chartConfig} className="min-h-[300px] w-full">
<AreaChart data={data}>{/* Chart internals */}</AreaChart>
</ChartContainer>
);
}Motion & Animation
Library
motion/react (NOT framer-motion) with LazyMotion (~15KB vs ~100KB):
// ā
Correct
import { m } from "motion/react";
<m.div animate={{ opacity: 1 }} />;
// ā Wrong ā bloats bundle
import { motion } from "framer-motion";Staggered Fade-In
const containerVariants = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { staggerChildren: 0.1 } },
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
show: {
opacity: 1,
y: 0,
transition: { type: "spring", stiffness: 300, damping: 30 },
},
};
<m.div variants={containerVariants} initial="hidden" animate="show">
{items.map((item) => (
<m.div key={item.id} variants={itemVariants}>
{item.content}
</m.div>
))}
</m.div>;Physics
| Transition | Animation |
|---|---|
| Page Entrance | staggerChildren: 0.1 + y: 20 to y: 0 fade-in |
| List Item | whileHover: { scale: 1.01 } + backdrop-blur-lg |
| Status Change | spring with stiffness: 300 for color transition |
ļø Administrative Interface Patterns
High-Fidelity Detail View
The administrative dashboard uses a "Premium Detail" pattern for complex entities (Tickets, Contacts, Users):
- Visual Surface: Uses the Crystal Detail pattern ā
backdrop-blur-3xl bg-white/40 dark:bg-white/5with iridescent overlays. - Identity Awareness: Integrates the Identity Resolution module to display real-time Supabase profile data (avatars, names) for matching email addresses.
- Super-Admin Controls: Floating administrative panels with theme-aware surfaces (e.g.,
bg-white/80 dark:bg-slate-900/80) to ensure legibility of staff-only fields. - Interactive Actions: Integrated clipboard actions and mailto triggers with iridescent hover states.
ļø Component Architecture
| Type | Config |
|---|---|
| Enter | { duration: 0.5, ease: [0.16, 1, 0.3, 1] } |
| Spring | { type: "spring", stiffness: 300, damping: 30 } |
| Stagger | { staggerChildren: 0.1 } |
| Hover | { scale: 1.02, transition: { duration: 0.2 } } |
| Toggle Hover | { scale: 1.1 } with contextual glow shadow |
| Toggle Tap | { scale: 0.9 } with snap-back spring |
| Smart Header | { type: "spring", stiffness: 300, damping: 30 } |
Smart Sticky Header Pattern
The global header implements an intelligent visibility strategy to optimize vertical space on both mobile and desktop.
Key Logic:
- Initial State: Visible and transparent.
- Scroll-Down Hide: When scrolling down past 50px, the header slides up (
y: -100%) immediately to clear the viewport. - Scroll-Up Reveal: To prevent jitter from accidental micro-scrolls, the header only reveals itself after the user has scrolled up by an accumulated 150px. This provides a deliberate "intention-based" reveal.
- Top-of-Page Reveal: The header automatically slides down if the user scrolls back to the top 50px of the page.
- Glass Transformation: When the page is scrolled > 20px, the header background switches from transparent to a
backdrop-blur-xl bg-white/50glass surface.
Implementation Details:
- Uses
useScroll()anduseMotionValueEvent()for low-overhead scroll tracking. - Uses
useRefas a high-performance accumulator for upward scroll distance. - Animations use Spring Physics (
stiffness: 300, damping: 30) for a premium feel.
SSR / RSC Patterns
Server Components (Default)
// app/(protected)/dashboard/page.tsx ā Server Component
export default async function DashboardPage() {
const data = await getDashboardData();
return <DashboardClient data={data} />;
}Client Components (Opt-In)
Only "use client" when needed: useState, useEffect, event handlers, browser APIs, animation.
Client Island Pattern
// Server (zero JS shipped)
export default async function Page() {
const data = await getData();
return <InteractiveWidget data={data} />;
}
// Client (minimal JS)
("use client");
export function InteractiveWidget({ data }: Props) {
const [state, setState] = useState(data);
return <m.div>{/* Interactive UI */}</m.div>;
}Form Patterns
Zod + React Hook Form
"use client";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { FloatingLabelInput } from "@/components/ui/floating-label-input";
const schema = z.object({
name: z.string().min(1, "Required").max(100),
email: z.string().email("Invalid email"),
});
export function MyForm() {
const form = useForm({
resolver: zodResolver(schema),
defaultValues: { name: "", email: "" },
});
async function onSubmit(data: z.infer<typeof schema>) {
const result = await serverAction(data);
if (!result.success) toast.error(result.error);
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FloatingLabelInput label="Name" {...form.register("name")} />
<FloatingLabelInput label="Email" {...form.register("email")} />
<Button type="submit" disabled={form.formState.isSubmitting}>
{form.formState.isSubmitting ? (
<span className="animate-spin">ā³</span>
) : (
"Submit"
)}
</Button>
</form>
</Form>
);
}Bundle Optimization
| Strategy | Implementation |
|---|---|
| LazyMotion | domAnimation loaded on demand |
| Named imports | import { Button } from "@/components/ui/button" |
| No barrel re-exports | Import from specific component files |
| Dynamic imports | next/dynamic for heavy below-fold components |
| Tree-shaking | import { Search } from "lucide-react" (not *) |
| Image optimization | next/image with explicit dimensions and style={{ width: "auto", height: "auto" }} on fixed-aspect assets |
| Font optimization | next/font/google with display: "swap" |
| Stable config | Production config strips unstable Next.js keys after plugin composition |
Accessibility
| Standard | Implementation |
|---|---|
| WCAG AA | Minimum contrast, keyboard nav |
| Enter key | All dialogs + action buttons respond |
| aria-label | Required on icon-only buttons |
| Focus management | autoFocus on primary modal actions |
| Screen reader | Semantic HTML (<main>, <nav>, <section>) |
| Color contrast | OKLCH tokens designed for AA in both modes |
| CornerDownLeft | Visual keyboard shortcut cue |
Layout & Container Padding Standards
All full-width marketing/landing page sections MUST use a unified container pattern to ensure perfectly aligned left and right boundaries across every section:
<section className="py-24 relative overflow-hidden">
<div className="container mx-auto px-4 md:px-8 max-w-7xl">
{/* Section content */}
</div>
</section>| Property | Value | Purpose |
|---|---|---|
container mx-auto | ā | Centers the inner wrapper |
px-4 | 16px | Mobile horizontal gutter |
md:px-8 | 32px | Desktop horizontal gutter (ā„ 768px) |
max-w-7xl | 1280px | Maximum content width cap |
Rule: The
max-w-7xlMUST live on the inner container<div>, NOT on the<section>element. Placing it on the section clips background glows and ambient effects.
Exceptions
- FAQ Section: Uses
max-w-3xlfor narrow, focused reading. - Full-bleed backgrounds (e.g.,
bg-destructive/5): The<section>stays full-width; only the inner<div>gets the max-width constraint.
Mobile Responsiveness & Touch Architecture
Touch Targets & Ergonomics
- Minimum Dimensions: All interactive elements MUST expose at least a 44px hit target. Shared
Buttonvariants usemin-h-11; icon buttons usesize-11; custom controls must usemin-h-[44px] min-w-[44px]or larger. - Input Padding: Forms use enlarged padding (
p-4top-6) for easier tapping. - Floating Labels: The
FloatingLabelInputensures readable tap targets without sacrificing vertical space.
Spacing & Layout Scaling
- Bento Grids: Multi-column grids MUST collapse to a single column (
grid-cols-1) on screens < 768px. - Fluid Spacing: Margin and padding scales gracefully (e.g.,
max-w-7xl px-4 md:px-8). - Dynamic Typography: Headings scale significantly (e.g.,
text-3xl md:text-5xl).
Per-Route-Group Responsive Spacing
Every route group follows mobile-first responsive rules. Desktop values are never changed ā only mobile/tablet values are reduced:
| Route Group | Element | Mobile | Desktop (md: / lg:) |
|---|---|---|---|
| Dashboard | Container | p-3 | md:p-6 |
| Page title | text-2xl | md:text-3xl | |
| Content gap | space-y-4 | md:space-y-6 | |
| Super-Admin | AdminPageShell padding | p-3 | md:p-6 |
AdminPageShell title | text-xl | md:text-2xl | |
StatsCard value | text-xl | md:text-2xl | |
| Raw pages (posts, blog-categories, affiliates) | p-3 | md:p-6 | |
| Post editor pages | p-3 | md:p-10 | |
| Legal editor grid | Single col | md:grid-cols-2 | |
| Onboarding | Logo margin | mt-4 | md:mt-8 |
| Progress bar margin | mb-4 | md:mb-8 | |
| Step titles | text-2xl | md:text-3xl | |
| Card padding | p-4 | md:p-6 | |
| Button height | h-12 | md:h-14 | |
| Celebration title | text-2xl | md:text-4xl | |
| Marketing (Legal) | Container | py-6 | md:py-12 |
| Glass card | p-4 | md:p-12 | |
| Title | text-2xl | md:text-4xl | |
| Title margin | mb-4 | md:mb-8 | |
| Marketing (Contact) | Top padding | pt-20 | md:pt-40 |
| Title | text-3xl sm:text-4xl | lg:text-6xl | |
| Header margin | mb-8 | md:mb-16 | |
| Address grid | grid-cols-1 | md:grid-cols-2 | |
| Marketing (Blog Post) | Card padding | p-4 | md:p-12 |
| Title | text-2xl sm:text-4xl | md:text-5xl | |
| Description | text-base | md:text-xl | |
| Marketing (System) | Container | px-3 py-12 | md:px-6 md:py-24 |
| Title | text-3xl | md:text-5xl lg:text-6xl | |
| Grid gap | gap-4 | md:gap-6 | |
| StatusCard padding | p-4 | md:p-6 | |
| Marketing (Checkout) | Card padding | p-6 | md:p-12 |
| Success icon | w-16 h-16 | md:w-24 md:h-24 |
Rule: Changes are mobile-only. Desktop layouts MUST remain untouched.
Mobile-First Content Alignment
Text-heavy sections (e.g., Problems, Hero, DX) that split into multi-column layouts on desktop MUST center their text content on mobile:
<div className="flex flex-col items-center lg:items-start text-center lg:text-left">
<h2>{title}</h2>
<p>{description}</p>
</div>Rule: Headings and descriptions default to
text-centeron mobile, switching totext-leftatlg:breakpoint when a side-by-side layout is active.
Auth Pages: Mobile & Tablet Responsive Architecture
Auth forms (login, signup, forgot-password, reset-password, waitlist-signup, mfa-challenge) use a dual-layout system: split-screen on desktop (lg:) and a floating glassmorphism card on mobile/tablet:
| Property | Mobile / Tablet | Desktop (lg:) |
|---|---|---|
| Width | w-[calc(100%-2rem)] sm:w-[calc(100%-4rem)] max-w-lg | lg:w-1/2 |
| Background | bg-white/50 dark:bg-white/5 backdrop-blur-2xl | lg:bg-background |
| Corners | rounded-3xl | lg:rounded-none |
| Border | border-white/20 dark:border-white/10 | lg:border-none |
| Shadow | shadow-2xl | lg:shadow-none |
| Padding | px-6 py-10 sm:px-10 sm:py-14 | lg:px-16 xl:px-24 |
Theme Toggle & Locale Switcher:
All auth forms include <ThemeToggle /> and <LocaleSwitcher /> (when isI18nEnabled() is true) in an absolute-positioned container at the top-right corner: absolute top-2 right-2 flex items-center z-50. These are visible across all breakpoints.
Ambient Effects (Mobile Only):
- Grid overlay:
bg-[url('/grid.svg')] opacity-10behind the form. - Gradient orbs: Two
bg-primary/20 blur-[100px]circles at opposing corners create depth.
Centered Logo (Mobile Only):
All auth forms display the brand logo above the heading on < lg screens using the dynamic theme logo pattern (/logo.png + /logo-light.png), hidden on desktop via lg:hidden.
Auth Button Styling:
All auth action buttons (Email & Password, Magic Link, Passkey) use a unified primary style with a hover-to-outline effect: bg-primary text-primary-foreground hover:text-primary hover:bg-primary/5 hover:border hover:border-primary.
OAuth Button Styling:
OAuth buttons use the inverse pattern: bg-background dark:bg-input/30 hover:bg-accent dark:hover:bg-input/50 ā visually subdued by default, with a subtle fill on hover.
iOS Safe Zones & Viewport Handling
- Viewport Height: Use
min-h-screen-dvhto account for dynamic browser chrome (address bars popping up/down). - Safe Area Padding: Apply
pb-safe(orpadding-bottom: env(safe-area-inset-bottom)) to fixed-bottom elements. - Sticky Overlays: Sticky headers or bottom CTA bars must have strict Z-Index hierarchies (
z-50) and backdrop-blur to prevent visual collisions.
Overlays (Modals vs. Sheets)
- Mobile First: Prefer
<Sheet side="bottom">for complex interactions on mobile screens, upgrading to centered<Dialog>on desktop breakpoints. - Mobile Menu Overlays: Navigation overlays (hamburger menus) MUST apply
max-h-[calc(100vh-6rem)]withoverflow-y-autoto prevent content cutoff on short devices. Usecustom-scrollbarfor styled scrollbars.
Collapsible Nested Navigation
Primary navigation items that contain sub-links (e.g., "Home" ā Features, Problems, DX, Comparison, etc.) use a collapsible dropdown pattern on both desktop and mobile:
// Desktop: hover-triggered dropdown with AnimatePresence
<div onMouseEnter={() => setIsDropdownOpen(true)} onMouseLeave={() => setIsDropdownOpen(false)}>
<button className="flex items-center gap-1">
<span>Home</span>
<ChevronDown className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`} />
</button>
<AnimatePresence>{isDropdownOpen && <m.div>{/* sub-links */}</m.div>}</AnimatePresence>
</div>
// Mobile: click-triggered expand/collapse with height animation
<button onClick={() => setIsMobileHomeOpen(!isMobileHomeOpen)}>
<span>Home</span>
<ChevronDown className={`${isMobileHomeOpen ? "rotate-180" : ""}`} />
</button>
<AnimatePresence>
{isMobileHomeOpen && <m.div initial={{ height: 0 }} animate={{ height: "auto" }} exit={{ height: 0 }}>{/* sub-links */}</m.div>}
</AnimatePresence>Rule: The top-right header CTA buttons (e.g., "Sign up", "Go to Dashboard") MUST hide when the mobile menu overlay is open. Use conditional classes:
${isOpen ? "hidden md:flex" : "flex"}.
Footer Layout & Newsletter i18n
- Mobile Footer Centering: The brand column (logo, tagline, social icons) centers horizontally on mobile via
flex flex-col items-center text-center lg:items-start lg:text-left. - Link Columns: Footer navigation link groups use
grid-cols-2on mobile for dual-column layouts, expanding to multi-column atlg:. - Newsletter i18n: Email placeholders and submit button text MUST use
next-intltranslation keys (e.g.,tCommon("emailPlaceholder"),tCommon("subscribe")), never hardcoded strings. - Legal Links: Copyright and legal links (Privacy, Terms, Cookies) wrap into a compact
flex-wraprow on mobile.
Dashboard Sidebar UI Architecture
The dashboard sidebar (components/dashboard/common/dashboard-sidebar.tsx) implements three key patterns:
| Pattern | Implementation | Why |
|---|---|---|
| Hover Isolation | Scoped group/name namespaces (group/text, group/theme) | Prevents parent sidebar hover states from triggering child animations in LocaleSwitcher and ThemeToggle |
| Collapsed Branding | Theme-aware compact icons (sidebar-icon.png / sidebar-icon-light.png) replace full logo in collapsible=icon mode | Professional brand identity at every sidebar density |
| Adaptive Visibility | group-data-[collapsible=icon]:hidden on LocaleSwitcher | Maintains aesthetic density when sidebar is collapsed |
| Icon Centering | flex items-center justify-center w-full on footer controls | Pixel-perfect horizontal centering for ThemeToggle and SidebarTrigger in collapsed state |
Rule: Always use
group/namenamespaces (e.g.,group/text,group/theme) for hover-dependent animations inside the sidebar. Never use baregroupclasses ā they cause cascading hover conflicts.
Error Boundaries & Resilience
Vibe44 uses a bifurcated error boundary strategy to ensure users receive contextually relevant recovery paths:
- Global Boundaries (
app/error.tsx): Captured for public/marketing pages. Uses theGlobalErrortranslation namespace and provides a link back to the homepage. - Dashboard Boundaries (
app/(protected)/error.tsx): Captured for authenticated dashboard pages. Uses theDashboardErrortranslation namespace and provides a primary "Return to Dashboard" action to keep the user within the protected session.
Both boundaries utilize the Glassmorphism 2.0 visual language with mesh gradients and iridescent cards to maintain a premium feel even during failure states.
ļø Route Structure
app/
āāā (marketing)/ # Public ā SSR, SEO
ā āāā page.tsx # Landing
ā āāā pricing/
ā āāā blog/
ā āāā contact/
ā āāā about/
ā āāā docs/ # Fumadocs
ā āāā docs/api/ # Scalar
āāā (auth)/ # Split-screen
ā āāā login/
ā āāā signup/
ā āāā forgot-password/
ā āāā reset-password/
āāā (protected)/ # Sidebar layout
āāā dashboard/
āāā page.tsx # Overview
āāā analytics/
āāā billing/
āāā organizations/
āāā projects/
āāā settings/
āāā super-admin/ # 17 modular super-admin components (Glassmorphism 2.0)SEO
import { generatePageMetadata } from "@/lib/seo";
export const metadata = generatePageMetadata({
title: "Dashboard","
description: "Your performance overview.",
path: "/dashboard",
noIndex: true,
});| Function | Use Case |
|---|---|
generatePageMetadata() | Standard pages |
generateArticleMetadata() | Blog posts |
generateOrganizationSchema() | Landing JSON-LD |
generateProductSchema() | Pricing JSON-LD |
<JsonLd nonce={nonce} /> | CSP-compliant structured data scripts |
Marketing pages read the request nonce from next/headers and pass it into
JsonLd; this keeps Organization, WebSite, Product, and FAQ structured data
compatible with the strict script-src 'nonce-...' 'strict-dynamic' policy.
Dynamic Sitemap Generation
The application implements dynamic sitemap generation via Next.js app/sitemap.ts to ensure search engines can index all dynamically generated routes, including:
- Marketing pages
- Dynamic blog posts
- Legal documents (Privacy Policy, Terms of Service)
Copyright Ā© 2026 Vibe44. Built with ā¤ļø by Scale with Alap.