Skip to main content
Skip to content

Last updated on 4 June 2026

Customisation Guide — UI03 ShieldOps Cybersecurity

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 )   →   ( --critical, --brand-   →   ( --background, --card,
                             accent, --safe, … )         --foreground, --border, … )

Primitives are the only place raw OKLCH values live. Change a primitive and it cascades through the semantic aliases to every component token and every Tailwind utility.

Where to edit

You want to change…EditNotes
The severity palette (critical/high/warning/info/safe)globals.css SECTION AChange the --p-red-500 / --p-orange-500 / --p-amber-400 / --p-cyan-500 / --p-green-500 primitives. Every badge, chart, alert, and border updates in one pass.
Light-mode background / text / bordersglobals.css SECTION BAdjust the --p-slate-* primitives. Component tokens (--background, --card, --border, …) derive from these.
Dark-mode background / text / bordersglobals.css SECTION CAdjust the --p-ink-* primitives. Same cascade, dark-side.
The brand accent (acid green)globals.css --p-accent-400/500/600/700Update the four lightness levels and the glow primitives beneath them. --brand-accent points at --p-accent-400 to match the allocation HTML's lead swatch.
Border radiusglobals.css SECTION D--radius: 0.375rem — change once, every corner updates.
Motion timing / easingglobals.css --duration-* / --ease-*--duration-normal, --ease-default (cubic-bezier(0.22, 1, 0.36, 1) / outExpo), etc.
Fontssrc/app/layout.tsx (import) + globals.css @theme inline (binding)See "Typography" below.
Brand name / emails / social / legal entitysrc/config/site.tsSITE_CONFIG.name, SITE_CONFIG.email.*, SITE_CONFIG.legal.*. Legal pages consume these.

Finding the sections in globals.css

bash
grep -n "SECTION A:" src/app/globals.css   # severity tokens
grep -n "SECTION B:" src/app/globals.css   # light mode
grep -n "SECTION C:" src/app/globals.css   # dark mode
grep -n "SECTION D:" src/app/globals.css   # border radius

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 critical colour, update these too:

FileWhy it's a raw literal
src/app/global-error.tsxRuns when the root layout crashes — globals.css has not loaded, so var(--p-accent-500) cannot resolve.
src/app/icon.svgStandalone SVG served as the favicon — no CSS context.
src/components/sections/hero.tsx (HERO_STAR_OKLCH)StarBorder component paints the glow in a shader/canvas context that cannot read CSS vars at paint time.
src/components/sections/features-grid.tsx (SEV_OKLCH)Same reason — card glow literals pinned to severity primitives.
public/og-image.pngStatic image baked at design time — regenerate with your brand colour if rebranding.

src/components/ui/radar-bg.tsx was previously in this list; it now uses SVG currentColor and cascades from --brand-accent automatically. Pass a color prop if you want to override it on a per-instance basis.


Threat severity palette

The severity system is driven by 5 OKLCH tokens in src/app/globals.css. Change these to retheme every badge, chart, alert, and border in one place:

css
/* src/app/globals.css — :root — SECTION A */
--critical: var(--p-red-500);    /* #c5211f ≈ oklch(0.55 0.24 27) — critical threats (darkened for 4.5:1 white-text contrast) */
--high:     var(--p-orange-500); /* #f97316 ≈ — high severity */
--warning:  var(--p-amber-400);  /* #f59e0b ≈ — medium/warning */
--info:     var(--p-cyan-500);   /* #06b6d4 ≈ — informational */
--safe:     var(--p-green-500);  /* #22c55e ≈ — healthy/resolved */

To change the critical colour to hot magenta:

css
--p-red-500: oklch(0.60 0.28 340);  /* overrides everywhere --critical is used */

Use oklch.com to pick values.


Light / dark mode

Light (:root, SECTION B)

--p-slate-* primitives drive the entire light palette. Modify a primitive to shift every component token that derives from it:

css
/* Lighten the page background slightly */
--p-slate-100: oklch(0.98 0.002 248);  /* was 0.96 */

/* Darken the border to sharpen card edges */
--p-slate-300: oklch(0.68 0.010 248); /* was 0.72 — keep ≥3:1 on --p-slate-100 for WCAG 1.4.11 */

Dark (.dark, SECTION C)

Dark primitives live under .dark { --p-ink-*: … }:

css
.dark {
  --p-ink-1000: oklch(0.06 0.018 260);  /* slightly deeper navy */
}

Contrast guardrails

Light-mode --p-slate-300 (the token that feeds --border) must stay at oklch(≤0.72 …) to maintain the 3:1 non-text contrast ratio WCAG 2.2 SC 1.4.11 requires. Light-mode --p-slate-500 (the token that feeds --muted-foreground) must stay at oklch(≤0.52 …) for 4.5:1 small-text contrast.


Typography

Three fonts are loaded via next/font/google in src/app/layout.tsx and bound to CSS variables in globals.css:

tsx
const geistSans   = Geist({        variable: "--font-geist-sans", subsets: ["latin"], display: "swap", adjustFontFallback: true });
const geistMono   = Geist_Mono({   variable: "--font-geist-mono", subsets: ["latin"], display: "swap", adjustFontFallback: true });
const chakraPetch = Chakra_Petch({ variable: "--font-chakra-petch", subsets: ["latin"], weight: ["400","600","700"], display: "swap", preload: false });
RoleFontCSS variable (layout)@theme inline binding (globals.css)
Body textGeist Sans--font-geist-sans--font-sans
Code / monoGeist Mono--font-geist-mono--font-mono
Display headingsChakra Petch--font-chakra-petch--font-display

To swap a font:

  1. Replace the import + loader call in src/app/layout.tsx.
  2. Keep the variable: name the same (or update the @theme inline binding in globals.css to match).
  3. That's it — every .font-display, font-mono, and the default font-sans already reads from the CSS variables.

The display font keeps preload: false because it's only used on headlines — no need to block render on pages that don't use it.


Animation tuning

Lenis scroll speed

Edit src/components/providers/lenis-provider.tsx:

tsx
const lenis = new Lenis({
  duration: 1.2,   // lower = faster (0.8 for snappier feel)
});

Motion timing tokens

css
--duration-normal: 300ms;
--ease-default:    cubic-bezier(0.22, 1, 0.36, 1);  /* outExpo — matches globals.css */

Use these in Tailwind utilities or inline styles for consistent timing.


Branding strings

The central config file is src/config/site.ts.

Change SITE_CONFIG.name, SITE_CONFIG.url, SITE_CONFIG.email.*, SITE_CONFIG.social.*, and SITE_CONFIG.legal.* first. This covers metadata, legal entity references, OG tags, and the privacy / terms / GDPR intro strings (they read SITE_CONFIG.legal.companyFull etc.).

Some pages carry the brand name inline in marketing copy, blog content, or page-level metadata titles. After editing site.ts, do a project-wide find-and-replace for "ShieldOps" across:

AreaFiles to edit
Header & Footersrc/components/layout/site-header.tsx, src/components/layout/site-footer.tsx
Dashboard sidebarsrc/components/dashboard/dashboard-shell.tsx
Hero & marketingsrc/components/sections/hero.tsx, src/components/sections/features-grid.tsx, src/components/sections/cta-section.tsx, src/components/sections/threat-preview.tsx
Product pagessrc/components/product/product-page.tsx, src/app/product/*/page.tsx (5 files)
Blogsrc/app/blog/page.tsx, src/app/blog/[slug]/page.tsx, src/components/sections/blog-article-data.ts
Auth pagessrc/app/login/page.tsx, src/app/register/page.tsx, src/app/forgot-password/page.tsx
Legal pagessrc/app/privacy/page.tsx, src/app/terms/page.tsx, src/app/gdpr/page.tsx, src/app/security/page.tsx
Contact & Careerssrc/app/contact/layout.tsx, src/app/contact/page.tsx, src/app/careers/layout.tsx, src/app/careers/page.tsx
Docs & Aboutsrc/app/docs/page.tsx, src/app/about/page.tsx
Dashboardsrc/app/dashboard/empty/page.tsx
Brand assetssrc/app/icon.svg (favicon — see exceptions table above)

Also replace shieldops.io with your domain in legal pages and config.


Adding / removing pages

Pages live under src/app/. Create a folder with a page.tsx to add a route. Delete the folder to remove it from the build. The static export only includes routes that exist.

Replacing the OG image

Replace public/og-image.png (1200×630) with your own branded social share image. Used for Open Graph / Twitter card previews.