Skip to main content
Skip to content

Last updated on 4 June 2026

Customisation Guide

Theme structure — the one-file promise

99% of visual customisation happens in one file: src/app/globals.css. The three-tier OKLCH token system below is the single source of truth for every colour, spacing, motion, and font binding in the kit.

Tier 1 — Primitives        Tier 2 — Semantic aliases    Tier 3 — Component tokens
( --p-*, raw OKLCH )   →   ( --brand-accent, --brand-  →   ( --bg-surface, --text-
                             glow, --chart-series-*,       primary, --border-*,
                             --severity-*, --model-* )     --glass-*, --radius-* )

Most brand-critical OKLCH values live in primitives. Semantic aliases derive from primitives where they need to rebrand together; intentionally independent semantic/component tokens (severity, chart series, glass alpha, overlays, shadows, and model comparison colours) keep their own OKLCH values so they remain meaningful across rebrands. Tailwind consumes the final layer through the @theme inline { ... } block.

Tailwind v4 note — this kit uses Tailwind v4's CSS-first configuration. All theme tokens live in src/app/globals.css inside @theme inline { … }. There is no tailwind.config.js / tailwind.config.ts to edit. If you're coming from v3, stop looking for the JS config — it's gone by design.

Where to edit

You want to change…EditNotes
The warm-amber brand accentglobals.css --p-amber-400/500/600/700 in :rootAdjust hue to rebrand the whole kit. Light mode inherits the same primitives; edit html.light only when you intentionally want independent light-mode surfaces.
Light/dark backgrounds + surfacesglobals.css :root and html.lightAll surfaces derive from the primitive --p-* tokens.
Severity palette (success/warning/error/info)globals.css --severity-* tokens (registered as --color-severity-* in @theme)Independent of brand accent — use Tailwind utilities like bg-severity-success, text-severity-error/20.
Chart series colours (Recharts)globals.css --chart-series-* tokensReferenced by SVG <stop> / <rect> elements via var(--chart-series-N).
Model colours (dashboard Model Comparison)globals.css --model-emerald/violet/blue/amberComponents read the CSS variables directly and use color-mix() for translucent states.
Border radiusglobals.css --radius-*Single source for cards, buttons, inputs.
Motion timing + easingglobals.css --duration-* / --ease-*Used by CSS utilities. Framer Motion and GSAP components keep local timing props so interactive effects remain tuned per component.
Fontssrc/app/layout.tsx (import) + globals.css @theme inline (binding)See "Typography" below.
Brand identity stringssrc/lib/site-config.tsName, emails, social, legal entity, API URLs.
Logo marksrc/components/layout/navbar.tsx, src/components/layout/footer.tsx, src/app/icon.svgReplace the default Zap icon in the nav/footer and keep the favicon SVG in the same brand style.
Package identitypackage.json nameOptional during a full rebrand; rename ui01-aria-ai-saas to your own package/app slug if you publish or embed it in a monorepo.
Site language (HTML lang attribute)src/app/layout.tsx <html lang="…"> AND src/app/global-error.tsx <html lang="…">Both files must be kept in sync — global-error.tsx runs when the root layout fails so cannot inherit. Update both atomically.
OAuth / SSO provider buttonssrc/components/auth/login-page-parts.tsx OAuthButtonsGeneric "Continue with SSO" placeholder ships behind NEXT_PUBLIC_TE_DEMO=true. To wire real providers (Google, GitHub, Microsoft, etc.), replace the buttons with your provider's branded button per their brand guidelines, then implement the auth handler. The kit ships frontend-only — bring your own auth provider (NextAuth.js, Clerk, Auth0, Supabase Auth, etc.).

Documented exceptions (raw hex allowed)

A handful of files intentionally hold literal hex values because CSS custom properties are unavailable at that execution point. Each carries a top-of-file comment explaining why. If you retheme the accent or model colours, update these too:

FileWhy it's a raw literal
src/lib/tokens.tsJS mirror of the CSS palette — consumed by WebGL / Canvas / Three.js shaders that cannot read CSS variables. light-droplets-bg.tsx imports from here automatically. Edit globals.css first, then sync the matching hex values here.
src/app/icon.svgStandalone SVG served as the favicon — no CSS context. Pin the background colour to your --p-amber-600 and the glyph colour to your brand-accent foreground mirror.
src/app/global-error.tsxRuns when the root layout crashes — globals.css has not loaded, so CSS vars cannot resolve. Mirror the ERROR_FALLBACK palette from src/lib/tokens.ts here.
<meta name="theme-color"> in layout.tsxBrowser UI chrome (mobile address bar, PWA theming) requires hex — CSS vars aren't resolved in HTTP metadata.

Toggle-knob colour comes from the --toggle-knob CSS variable (defined in globals.css) — it's near-white in dark mode and near-black in light mode so both satisfy WCAG AA contrast against the alpha-on-ink track. Don't replace with a literal bg-white — that fails light-mode contrast and has been deliberately retired from this kit.


Colours

The brand colour system is defined as OKLCH tokens in src/app/globals.css. Editing the four --p-amber-* primitive lines alone cascades through every brand-semantic surface in the kit — buttons, links, badges, section labels, borders, pricing card auras, shadow glows, orb backgrounds, surface tints. 3 additional files require manual edits for the hex/RGBA fallback (used by WebGL), the app icon SVG, and the error fallback page. See Documented exceptions above and Rebrand workflow below.

Important: If you are shipping under a different brand, complete the full manual checklist before considering the rebrand finished.

Change the brand colour

Edit the Tier 1 primitives in :root {}:

css
:root {
  /* Tier 1 — Primitive palette */
  --p-amber-400: oklch(0.85 0.16 75);        /* light amber */
  --p-amber-500: oklch(0.76 0.175 75);       /* mid amber */
  --p-amber-600: oklch(0.740 0.111 74.6);    /* ← main accent (#D4A055) */
  --p-amber-700: oklch(0.52 0.145 75);       /* dark amber for light-mode text */
}

Adjust the hue value (75 = warm amber) to shift the entire brand colour:

  • 75 — warm amber/gold (default)
  • 145 — green
  • 180 — teal
  • 255 — blue
  • 320 — pink/rose

The second OKLCH value controls saturation (chroma). Lightness steps are set for contrast — keep the relative differences when changing.

Accent variants

The commented Teal/Rose snippets in globals.css are reference palettes, not one-step theme switches. For a complete rebrand, re-hue the four --p-amber-* primitives in :root {} and then update the three non-CSS surfaces in the workflow below. That keeps glow, orb, surface-tint, light-mode, and hero-frame colours on the same hue instead of leaving amber-derived tokens behind.

Light mode colours

Overrides are in html.light {} in globals.css. Edit those values to customise the light theme independently.

Known exceptions

These elements use hardcoded colours that won't update from the CSS tokens:

  • Hex/RGBA fallback (src/lib/tokens.ts) — source of truth for WebGL, Canvas, viewport metadata, and root error fallback contexts that cannot read CSS custom properties. light-droplets-bg.tsx imports BRAND.amber.accent; ambient-glow.tsx imports WEBGL.ambientBrand / WEBGL.darkBackground; src/app/global-error.tsx mirrors ERROR_FALLBACK locally.
  • SVG icon (src/app/icon.svg) — replace #D4A055 manually or regenerate in your brand colour, and keep the glyph fill/stroke aligned with your brand-accent foreground mirror.
  • Recharts chart colours — SVG <stop> elements in src/components/dashboard/UsageDailyChart.tsx reference var(--chart-series-2). To change the chart-2 hue, edit the --chart-series-2-base: oklch(0.733 0.156 297.2); token in src/app/globals.css; the exported --chart-series-2 token derives from it and light mode automatically mixes the same base for contrast — no component edit needed.
  • Status/severity colours — operational (green), warning (amber), error (red), info (blue) are driven by four CSS custom properties in globals.css (--severity-success, --severity-warning, --severity-error, --severity-info) that are independent of the brand colour. These are registered in @theme as --color-severity-*, so Tailwind utilities work with the full modifier syntax: bg-severity-success, text-severity-error, border-severity-info/20, hover:bg-severity-warning/10. Edit the four OKLCH values in globals.css to shift the whole status palette.
  • Shadow glowsshadow-[0_0_Xpx_var(--brand-glow)] utilities throughout the kit resolve through a color-mix(in oklch, var(--brand-accent) X%, transparent) cascade, so no manual edit is needed after a primitive hue swap.

Rebrand workflow

Follow these steps in order when changing the brand colour:

Step 1 — CSS tokens (covers most UI)

  • Open src/app/globals.css
  • Change the four --p-amber-400/500/600/700 primitives to your brand hue (adjust lightness, chroma, and hue)
  • That's it for the cascade. --brand-accent, --brand-glow*, --brand-orb-glow, --brand-surface-tint*, .brand-glow-* utilities, and the orb backgrounds all derive from these primitives via var() or color-mix() — no further edit needed.
  • Convert your OKLCH values to hex at oklch.com when you need a literal for the non-CSS surfaces in Step 2.

Step 2 — Non-CSS surfaces (3 files)

#FileWhat to change
1src/lib/tokens.tsSource of truth — update BRAND.amber.accent, .glow, .muted, .rgb fields (search for the BRAND export). If your rebrand changes the page background, browser chrome palette, or root error fallback, update VIEWPORT_THEME.dark, VIEWPORT_THEME.light, and ERROR_FALLBACK in the same file. light-droplets-bg.tsx imports from here automatically.
2src/app/icon.svgReplace #D4A055 with your brand hex and #1f1308 with your brand-accent foreground mirror (cannot import TS — keep in sync with tokens.ts)
3src/app/global-error.tsxMirror ERROR_FALLBACK from src/lib/tokens.ts — this page renders without the root layout, so it intentionally keeps local fallback constants instead of importing CSS vars or modules

Informational — token-driven, no edit needed

FileNotes
src/components/ui/orb-bg.tsxThree radial-gradient orbs reference semantic var(--brand-orb-glow), which derives from --brand-accent. Rebrand via the --p-amber-* primitives in Step 1 and all three orbs update automatically. Listed here only so you know where the orb colours come from.
--brand-glow* shadow tokensNow composed from --brand-accent via color-mix(in oklch, … X%, transparent) for 4 alpha steps (brand-glow, -strong, -subtle, -whisper). Cascades both in :root (dark) and html.light.
--brand-surface-tint / --brand-surface-gradient-tintDerived from --brand-accent + --p-ink-900 / --p-slate-50 via color-mix. Card backgrounds get a subtle warm wash that re-tints with the brand on rebrand.
--similarity-mid / --similarity-lowDerive from --severity-warning + a fixed orange step — deliberately NOT tied to brand so RAG score traffic-lights (red → amber → green) stay semantic across rebrands.

Step 3 — Verify

  • Run pnpm dev and check the landing page in both dark and light mode
  • Toggle the theme — all branded elements should match your new colour
  • Check the dashboard, pricing, and error pages

Step 4 — Catch leftover brand strings The kit ships with pnpm rebrand-check (see scripts/rebrand-check.mjs). It greps the source for hardcoded occurrences of the kit's original brand name (Aria) outside SITE_CONFIG. Run it after Step 3 — any hits are strings the cascade can't reach (model labels, status copy, demo data seeded with the brand name). Replace each with SITE_CONFIG.name or your new brand string.

bash
pnpm rebrand-check

The script exits 0 if every match is in an allowlisted file (e.g. src/lib/site-config.ts, where the brand name is the source of truth).


Typography

Fonts are bundled as .woff2 variable font files in src/app/fonts/ and loaded via next/font/local — no network access is required at build time (deliberate choice for air-gapped CI / offline-build support). Three files ship with the kit:

RoleFileFontVariable (layout)@theme inline binding
Body / sanssrc/app/fonts/geist-sans.woff2Geist Sans (100–900)--font-geist-sans--font-sans
Code / monosrc/app/fonts/geist-mono.woff2Geist Mono (100–900)--font-geist-mono--font-mono
Display headingssrc/app/fonts/space-grotesk.woff2Space Grotesk (300–700)--font-space-grotesk--font-display

The @theme inline { … } block in globals.css maps these variables to the role-neutral names Tailwind consumes. Consumers use font-sans, font-mono, .font-display (or style={{ fontFamily: 'var(--font-display)' }}) — never reference --font-space-grotesk directly outside the binding.

The display font is preloaded because the homepage H1 uses it as an LCP-critical headline font. Keeping it preloaded avoids layout shift from a late headline font swap on wide viewports.

Change the display font

Replace src/app/fonts/space-grotesk.woff2 with any variable .woff2 and update the localFont config in src/app/layout.tsx:

ts
// src/app/layout.tsx
const spaceGrotesk = localFont({
  src: [{ path: './fonts/your-font.woff2', weight: '300 700', style: 'normal' }],
  variable: '--font-space-grotesk',
  display: 'swap',
  preload: true,
})

The --font-space-grotesk CSS variable is referenced throughout the template via the .font-display class.

Change the body font

The body font is Geist Sans. Replace src/app/fonts/geist-sans.woff2 and update the localFont config:

ts
const geistSans = localFont({
  src: './fonts/your-body-font.woff2',
  variable: '--font-geist-sans',
  weight: '100 900',
  display: 'swap',
})

Fluid type scale

Font sizes use clamp() in @theme inline {}:

css
--text-step-3: clamp(1.5rem, 1.2rem + 1.5vi, 2.25rem);

Adjust the min, preferred, and max values to taste. The vi unit scales relative to the viewport inline size.


Content

Site copy

All section content lives in the component files under src/components/sections/. Each section exports a single component; edit the constant arrays and strings directly.

Edit src/components/layout/navbar.tsx — the NAV_LINKS constant.

Shell-level brand identity (name, metadata, contact emails, social links, API URLs) is centralised in src/lib/site-config.ts. Change the SITE_CONFIG object to update these across the site in one place. Demo marketing, legal, blog, and testimonial content is separate and must be replaced manually — see the "Demo content" section below.

The wordmark text comes from SITE_CONFIG.name, but the mark itself is an icon. Replace the default Zap icon imports/rendering in src/components/layout/navbar.tsx and src/components/layout/footer.tsx, then keep src/app/icon.svg in sync so the browser favicon matches the visible logo.

The config object has the following sections:

KeyPurposeUsed by
nameBrand name (e.g. "Aria")Navbar logo, footer, forgot-password page
slugSafe lowercase identifier for API keys, model IDs, URL fragments, and analytics keysDashboard models, scripts, generated IDs
taglineShort brand taglineFooter
urlCanonical site URLOpenGraph, noreply email derivation
legalEntity.companyNameRegistered company name (e.g. "Aria, Inc.")Footer copyright, legal pages
legalEntity.companyNumberRegistered company / business numberFooter registered-entity disclosure
legalEntity.placeOfRegistrationRegistered jurisdiction text such as "Registered in England and Wales"Footer registered-entity disclosure
legalEntity.registeredAddressYour registered business addressTerms of Service, Privacy Policy
legalEntity.jurisdictionState/country of incorporationTerms of Service (governing law)
legalEntity.vatNumberVAT / tax registration number; empty hides the lineFooter registered-entity disclosure
legalEntity.regulator / regulatorNumberRegulated-profession disclosure fields; empty hides the lineFooter registered-entity disclosure
regions.uk / regions.eu / regions.usRegion switches controlling compliance links and disclosuresFooter legal links, compliance pages
compliance.dmca.*Designated Copyright Agent placeholdersDMCA page and conditional footer link
compliance.ccpa.doNotSellControls "Do Not Sell or Share" footer linkFooter legal links
compliance.ccpa.sensitivePIIControls "Limit Use of Sensitive Personal Information" footer linkFooter legal links
compliance.modernSlaveryStatementUrlOptional Modern Slavery Statement URLFooter legal links
metadata.titleDefault <title> tag and OG/Twitter titlesrc/app/layout.tsx metadata export
metadata.descriptionDefault meta description and OG/Twitter descriptionsrc/app/layout.tsx metadata export
api.baseUrlAPI base URL shown in docs and code samplesAPI reference page, playground code snippets
api.docsUrlLink to API documentation siteAvailable for use in custom pages
api.sdkPackageSDK package name shown in install/code snippetsAPI reference page, docs snippets
contact.emailGeneral contact emailContact page
contact.salesSales emailContact page FAQ
contact.privacyPrivacy/cookie enquiriesPrivacy policy, cookie policy pages
contact.legalLegal enquiriesTerms of service page
contact.securitySecurity reportsSecurity page
contact.statusStatus page contactStatus page
contact.careersCareers/jobs emailCareers page
contact.ethicsEthics/responsible-AI contactBlog post body
contact.phonePhone-support placeholderContact page
supportSla.*Public support response-time promisesContact page FAQ
offices[]Office city/address placeholdersContact page
marketing.activeTeamsCount / activeTeamsClaimSubstantiable headline metric; default null renders an obvious N demo placeholder. The hero/statement count-up animation stays dormant until activeTeamsCount is set to a positive number — until then the headline reads "Modern teams" with no animated count.Statement, about, CTA, dashboard scripts
trustBadges.login / trustBadges.securityProduct trust badges; replace with real attestations only when heldLogin, security page
cookieConsent.enabled / versionEnables consent banner and controls re-prompt versioningCookie banner, analytics gating
social.twitterTwitter/X profile URLFooter, community channels
social.twitterHandleTwitter/X @handle (display only)Community page decorative text
social.githubGitHub org URLFooter, community channels
social.youtubeYouTube channel URLCommunity page channels
social.linkedinLinkedIn company URLFooter
social.discordDiscord invite URLFooter

Demo content (replace before production)

site-config.ts covers your brand shell — name, metadata, contact details, and social links. The following pages contain demo content (marketing copy, blog posts, testimonials, legal text) that you should replace with your own before publishing:

AreaFilesWhat to replace
Docs hubsrc/app/docs/page.tsxSDK name, code snippets, guide links
Landing sectionssrc/components/sections/hero.tsx, features.tsx, product-proof.tsx, how-it-works.tsx, api-showcase.tsx, statement.tsx, faq.tsxProduct copy, feature descriptions, brand statement, code examples
About pagesrc/app/about/page.tsxCompany story, mission, timeline
Blog postssrc/app/blog/[slug]/page.tsx, src/lib/blog-data.tsAll article content and metadata
Testimonialssrc/components/sections/testimonials.tsxCustomer quotes, names, companies
Legal pagessrc/app/terms/page.tsx, src/app/privacy/page.tsx, src/app/security/page.tsx, src/app/cookie-policy/page.tsxAll legal/compliance text — consult your legal team
Dashboard demosrc/app/dashboard/page.tsx, src/components/dashboard/Chat messages, sample data, demo users
Brand Voicesrc/app/dashboard/brand-voice/page.tsx, src/components/dashboard/brand-voice-data.tsVOICE_PROFILES (Demo Marketing / Technical Docs / Customer Support Voice), tone slider seed values, training-doc filenames, sample output text — update sample output and profile name together
Supported languagessrc/lib/supported-languages.tsSUPPORTED_LANGUAGES array (currently 12). When buyers change the list, update related marketing copy in features.tsx, statement.tsx, about/page.tsx, testimonials.tsx, and pricing-data.ts so the "Twelve languages, one voice" pitch stays accurate.
Communitysrc/app/community/page.tsxChannel links, project examples

These pages contain realistic placeholder content to demonstrate the design. Do not publish them as-is — replace with your own copy. See DEMO_CONTENT.md for the full checklist.

Edit src/components/layout/footer.tsx — the FOOTER_LINKS object. Social links and the brand name are imported from src/lib/site-config.ts.

Pricing plans

Edit both pricing data sources:

  • src/components/sections/pricing.tsx — homepage pricing cards and landing-page billing toggle.
  • src/components/dashboard/pricing-data.ts — dedicated /pricing route cards and comparison table.

Keep plan names, prices, limits, and feature wording aligned across both files unless you intentionally want the homepage to use shorter marketing copy.

FAQ

Edit src/components/sections/faq.tsx — the FAQ_ITEMS constant array.

Testimonials

Edit src/components/sections/testimonials.tsx — the TESTIMONIALS constant array.


Adding a new page

  1. Create src/app/my-page/page.tsx
  2. Export a default React component
  3. Add a link in src/components/layout/navbar.tsx

Pages use the shared layout from src/app/layout.tsx automatically.


Animations

Disable all animations

Motion is controlled by src/hooks/use-motion-preference.ts. The system respects four signals:

  1. System preference: prefers-reduced-motion: reduce — detected automatically
  2. Viewport: animations are disabled on mobile (< 768px) — static layouts shown instead
  3. Manual toggle: if you add a MotionToggle button (see use-motion-preference.ts for the API)
  4. Global override: CSS variable or HTML data attribute, useful for buyer QA

To disable animations globally regardless of system settings, add this to src/app/globals.css:

css
:root { --force-reduce-motion: true; }

Or set data-reduce-motion="true" on the <html> element.

Adjust animation speed

The GSAP animation easings and durations are configured in src/lib/gsap.ts and individual section components. Lenis smooth scroll speed is in src/components/providers/lenis-provider.tsx:

ts
new Lenis({
  duration: 1.2,      // scroll animation duration in seconds
  touchMultiplier: 2, // mobile scroll speed multiplier
})