Architecture

Tech Stack

Layer

Technology

Framework

Next.js 15 (App Router)

UI Library

React 19

Language

TypeScript 5.7

Design System

IBM Carbon Design System (@carbon/react)

Styling

SCSS with Carbon tokens

Markdown

react-markdown + remark-gfm + remark-math

Math Rendering

KaTeX (rehype-katex)

Diagrams

Mermaid.js

Testing

Vitest + React Testing Library + jsdom

CMS

Strapi v5 (headless)

Project Structure

www/
├── public/                    Static assets (fonts, images)
│   ├── fonts/                 Custom typeface (SyriusSans)
│   └── images/                Homepage assets, blog placeholder
├── scripts/                   Seed and mock scripts
│   ├── mock-strapi.mjs        Mock Strapi v5 server
│   ├── seed-blog-articles.mjs Seed 9 articles to real Strapi
│   ├── seed-blog-content.mjs  Article content definitions
│   └── seed-blog-edge-cases.mjs  11 edge-case test articles
├── src/
│   ├── app/                   Next.js App Router pages
│   │   ├── [locale]/          Locale-scoped routes
│   │   │   ├── blog/          Blog listing, detail, filters
│   │   │   ├── gallery/       Gallery listing, detail
│   │   │   └── [slug]/        CMS-driven dynamic pages
│   │   ├── robots.ts          SEO robots.txt
│   │   └── sitemap.ts         Dynamic sitemap generator
│   ├── components/            React components
│   │   ├── homepage/          Static homepage fallback
│   │   └── sections/          CMS section renderers
│   ├── lib/                   Core utilities
│   │   ├── strapi.ts          Server-side Strapi API client
│   │   ├── types.ts           TypeScript interfaces
│   │   ├── i18n.ts            Locale config + translations
│   │   └── consoleUrl.ts      Runtime console/stargate URL derivation
│   ├── styles/                SCSS stylesheets
│   │   ├── _tokens.scss       Carbon Design imports
│   │   ├── globals.scss       Root styles and themes
│   │   ├── layout.scss        Layout components
│   │   ├── homepage.scss      Homepage styles
│   │   └── sections.scss      Page section styles
│   ├── middleware.ts           Locale routing middleware
│   └── __tests__/             Vitest unit tests
└── docs/                      This documentation (Sphinx)

Routing

All routes are locale-prefixed. The middleware in src/middleware.ts enforces this:

  1. Requests to /_next, /api, /fonts, or static files pass through

  2. If the first path segment is a valid locale (en, zh-CN, ja), the request passes through

  3. Otherwise, the preferred locale is detected from the Accept-Language header and the user is redirected to /{locale}{path}

Route Map

Route

Description

/[locale]

Homepage (CMS or static fallback)

/[locale]/blog

Blog listing (paginated, 5/page)

/[locale]/blog/[slug]

Article detail page

/[locale]/blog/categories/[slug]

Category view (all articles in category)

/[locale]/blog/tags/[slug]

Tag filter (paginated, 5/page)

/[locale]/gallery

Gallery listing (paginated, 12/page)

/[locale]/gallery/[slug]

Gallery collection detail

/[locale]/[slug]

CMS-driven dynamic page

Components

Layout Components

  • Navbar — Header with logo, nav links, theme toggle, locale switcher, StarGate admin sign-in link, login button, mobile hamburger menu. The admin link (adminUrl) points to stargate.{domain} and is derived at runtime from the current hostname (see consoleUrl.ts below), so it automatically resolves to the correct partition (.ai or .com).

  • Footer — 3 accordion sections (Platform, Services, Company), legal links, copyright. The “Services” section includes a “My Account” link (to StartPoint) and an “Admin” link (to StarGate), both using runtime-derived URLs.

  • ThemeProvider — Dark/light mode context (white / g100 Carbon themes). Persists the user’s choice to a cross-subdomain cookie (fg-theme, scoped to .flexgalaxy.ai or .flexgalaxy.com) so the preference carries over to console apps. Falls back to localStorage and then prefers-color-scheme media query.

  • LocaleSwitcher — Language dropdown preserving current path. On mount and on every language switch it writes a cross-subdomain cookie (fg-lang) so other apps on the same base domain can pick up the preferred locale.

  • CookieConsent — GDPR banner with Essential/Performance/Advertising toggles

Content Components

  • ArticleCard — Blog preview card with cover image, category link, title, summary, tag pills. Full-card clickability via ::after overlay

  • RichMarkdown — Markdown renderer with GFM, KaTeX math, raw HTML, and Mermaid diagram support

  • MermaidBlock — Lazy-loads Mermaid library, renders SVG with Carbon blue theme, fallback to raw code on error

  • CategoryArticleViewer — Category page layout with sidebar article list, article content area, and TOC sidebar

  • Pagination — Previous/Next with page indicator

  • ErrorState — Maps HTTP status codes to localized error pages

CMS Section Components

The SectionRenderer dispatches Strapi dynamic zone components:

__component value

Component

page-sections.hero

HeroSection

page-sections.feature-grid

FeatureGridSection

page-sections.rich-text

RichTextSection

page-sections.call-to-action

CallToActionSection

page-sections.image-gallery

ImageGallerySection

Styling

All styles use SCSS with IBM Carbon Design tokens imported via _tokens.scss. Four responsive breakpoints are defined:

Token

Width

$bp-sm

320px

$bp-md

672px

$bp-lg

1056px

$bp-xlg

1584px

Theme switching is CSS-driven via data-carbon-theme attribute on the <html> element, toggling between white (light) and g100 (dark) Carbon themes. All colors reference CSS custom properties (e.g., var(--cds-text-primary)).

Runtime URL Derivation

External URLs (Console, StarGate) are derived at runtime from the browser’s current hostname, not from build-time environment variables. This is implemented in src/lib/consoleUrl.ts via the useConsoleUrls() React hook.

The hook inspects window.location.hostname to determine:

  1. Base domain — the last two segments of the hostname (e.g., flexgalaxy.ai or flexgalaxy.com)

  2. Environment prefixdev- if the hostname starts with dev-, empty otherwise

It then constructs:

  • startUrl = https://{prefix}console.{baseDomain}/start/

  • signupUrl = https://{prefix}console.{baseDomain}/start/?register=true

  • adminUrl = https://{prefix}stargate.{baseDomain}

This means the same build artifact works on both .ai (international) and .com (China) deployments without rebuild. The NEXT_PUBLIC_START_URL and NEXT_PUBLIC_SIGNUP_URL environment variables serve only as SSR fallbacks (used during server-side rendering before hydration).