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 ( |
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:
Requests to
/_next,/api,/fonts, or static files pass throughIf the first path segment is a valid locale (
en,zh-CN,ja), the request passes throughOtherwise, the preferred locale is detected from the
Accept-Languageheader and the user is redirected to/{locale}{path}
Route Map¶
Route |
Description |
|---|---|
|
Homepage (CMS or static fallback) |
|
Blog listing (paginated, 5/page) |
|
Article detail page |
|
Category view (all articles in category) |
|
Tag filter (paginated, 5/page) |
|
Gallery listing (paginated, 12/page) |
|
Gallery collection detail |
|
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 tostargate.{domain}and is derived at runtime from the current hostname (seeconsoleUrl.tsbelow), so it automatically resolves to the correct partition (.aior.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/g100Carbon themes). Persists the user’s choice to a cross-subdomain cookie (fg-theme, scoped to.flexgalaxy.aior.flexgalaxy.com) so the preference carries over to console apps. Falls back tolocalStorageand thenprefers-color-schememedia 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
::afteroverlayRichMarkdown — 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 |
|---|---|
|
HeroSection |
|
FeatureGridSection |
|
RichTextSection |
|
CallToActionSection |
|
ImageGallerySection |
Styling¶
All styles use SCSS with IBM Carbon Design tokens imported via
_tokens.scss. Four responsive breakpoints are defined:
Token |
Width |
|---|---|
|
320px |
|
672px |
|
1056px |
|
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:
Base domain — the last two segments of the hostname (e.g.,
flexgalaxy.aiorflexgalaxy.com)Environment prefix —
dev-if the hostname starts withdev-, empty otherwise
It then constructs:
startUrl=https://{prefix}console.{baseDomain}/start/signupUrl=https://{prefix}console.{baseDomain}/start/?register=trueadminUrl=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).