The Angular Migration

Angular 16 → Angular 21. Archived to production-ready.

The official Kubernetes Dashboard was archived at Angular 16. This is the documented story of upgrading it — five Angular versions, 44 tracked issues resolved, fourteen new features integrated, three visual polish rounds, and one AI pair programmer.

44 Issues Tracked
6 Migration Steps
5 Angular Versions
14 Features Added
3 Polish Rounds
1 AI Pair
Scroll

The Starting Point

The upstream kubernetes/dashboard repository was archived in January 2026 with the Angular 16 frontend unmaintained. The Go API backend — proven, well-designed, and still used by other projects — was worth preserving. The Angular frontend needed five major version upgrades and a complete Material theming rewrite before it could be used in production.

Fragile Toolchain

Custom Makefile with brittle perl regexes, undocumented build steps, locale output path that silently changed between versions.

Material Design Debt

The SCSS theming used Material v2 legacy APIs that were deprecated and removed in the MDC (Material Design Components) migration. Hundreds of component overrides needed rewriting.

Deprecated Dependencies

@angular/flex-layout was archived and unmaintained. ngx-charts was pinned to a version incompatible with any Angular >16. Peer dependency conflicts blocked every upgrade attempt.

Before touching a line of code, every known issue was catalogued into a 44-item migration reference document — the single source of truth for the entire upgrade.

The Approach

Three methodological decisions made the migration tractable.

Gated Steps

Each Angular version upgrade is a discrete step. No proceeding until yarn build:prod and yarn test both pass. Every step has its own guide document. The migration reference overrides all other sources when conflicts arise.

Issue Registry

44 issues catalogued before any code was changed. Each issue has a B-xx identifier, root cause, affected components, and fix. Issues are referenced in commit messages and step guides.

AI Pair Programming

Claude Code (Claude Sonnet) was the pair programmer throughout. Not code generation — pattern recognition across 200+ files, catching breaking API changes, and debugging MDC migration edge cases that esbuild and the TypeScript compiler don't surface.

The Migration Steps

Six gated steps, each with its own guide document and gate check. No step begins until the previous step's build and tests pass.

Step 0

Step 0 — Material Foundation

The longest step. Rewrote the entire SCSS theming layer from Material v2 legacy APIs to MDC. Replaced hardcoded pixel values with the baseline grid. Defined $kd-blue, $kd-dark, and $kd-light palettes. Rewrote _dark.scss, _light.scss, and _theming.scss from scratch. Resolved 12 issues with component overrides, action column sticky behaviour (overflow: clip, not overflow: hidden), and chip pill shapes.

Step 1

Step 1 — Layout Migration

Replaced @angular/flex-layout (archived, no Angular 17+ support) with ngx-layout — a community fork that preserves the fxFlex/fxLayout directive API surface. Updated shared.module.ts, verified 847+ template directive references still rendered correctly. Discovered ngx-layout applies flex styles asynchronously (second change detection pass) — later the root cause of a blank-logs rendering issue.

Step 2

Step 2 — Angular 17

The builder migration (Webpack → esbuild via the Angular application builder) and the most breaking step. Removed enableProdMode() (automatic in v17+). Replaced HttpClientModule with provideHttpClient(). Migrated .eslintrc.yaml to ESLint flat config (eslint.config.js). Added window.global polyfill. Fixed B-31: locale output path changed from dist/public/browser/ to dist/public/ — a silent failure that produces correct HTML but 404s in production.

Step 3

Step 3 — Angular 18

Addressed signal-based component warnings, updated RxJS operator imports, resolved inject() context issues. Fixed B-44 first occurrence: ngx-charts must be v23.1.0 for Angular 18+ — wrong version compiles cleanly but renders blank chart containers at runtime.

Step 4

Step 4 — Angular 19 / 20

Router guard API modernisation (CanActivateFn function form). Peer dependency resolution for Angular Material v17→v18. Verified B-44 ngx-charts override still applies with correct version pin in package.json overrides.

Step 5

Step 5 — Angular 21

Final version bump. Updated Angular Material v18 CSS custom property references. Verified all 9 locale builds produce correct output structure. Gate check passed: yarn build:prod succeeds, yarn test passes, locale dirs present in .dist/public/.

Pair programming with Claude Code

Claude Code — Anthropic's AI coding assistant (Claude Sonnet 4.6) — served as the pair programmer for the entire migration. Every session loaded three documents as context: the current step guide, the 44-issue migration reference, and a running project log that tracked step status, blockers, and gate check results.

The AI's primary contribution was pattern recognition: finding all 847 fxFlex usages across 200+ files, spotting that a specific ngx-charts version would compile without errors but silently render blank, and diagnosing MDC button state layer conflicts that CSS linters don't catch.

What the AI does not replace: architectural decisions (which patterns to keep vs refactor), real-cluster smoke testing, and design judgment. Every migration step was gated by human-verified builds. Claude Code accelerated each step; the gate checks enforced correctness.

Multi-session context management: as the project grew beyond what fits in a single context window, a shared project log served as the handoff document — readable by both human and AI at the start of each new session. The current step, known blockers, and gate check status were all tracked there, keeping continuity across sessions without repeating already-established context.

Claude Code
  • Pattern matching across 200+ template files
  • Breaking change analysis for each Angular version
  • MDC migration edge case diagnostics
  • Real-time debugging during build failures
  • Root cause documentation and session continuity
Human
  • Architecture and feature design decisions
  • Real K3s cluster smoke testing
  • RBAC and security policy review
  • Visual design and UX judgment
  • Go backend and API design

A technical paper — Migrating a Retired Kubernetes Dashboard from Angular 16 to 21 using Claude Code — is in preparation.

Feature Integration

After the migration gates passed, fourteen new routes and features were added to the stable Angular 21 base.

Route Feature Nav Section
/insights Insights overview (cert + audit + events + RBAC pies) Insights
/timeline Event Timeline Insights
/rbac RBAC Viewer Insights
/certs Certificate Tracker Insights
/audit Policy Audit Insights
/operations Operations overview (registry + efficiency pies) Operations
/registries Registry Manager Operations
/efficiency Resource Efficiency Operations
/extensions Extensions overview (tile grid) Extensions
/gateway Gateway API (auto-detected) Extensions
/certmanager Certificate Manager (auto-detected) Extensions
/metallb MetalLB (auto-detected) Extensions
/security Kubescape CVE scanner (auto-detected) Extensions
/about About page Settings

Auto-detected extensions appear in the nav only when the corresponding operator is running in the cluster. Detection is CRD-based — no feature flags, no configuration.

The Go Backend

The original Go API backend was worth keeping. Proven, well-designed, and still maintained by its authors, it handles Kubernetes authentication, RBAC, resource CRUD, and WebSocket proxying. The decision was to preserve it intact and extend it — not rewrite it.

All fourteen new features required new API endpoints. Rather than building a separate service, new routes were added directly to modules/api/ — the existing Go module that already had k8s.io/client-go, authentication middleware, RBAC, and CSRF protection wired up. Each new feature is a self-contained package under resource/.

The Go workspace (go.work) covers all modules. Native endpoints live alongside the original API surface — same binary, same deployment, same bearer token authentication.

Endpoint Package Notes
POST /api/v1/ai/chatresource/aiSSE proxy to Anthropic API — streams tokens back to the Angular client
GET /api/v1/ai/statusresource/aiReturns {"configured": bool} — gates the AI button in the toolbar
GET /api/v1/auditresource/audit14 checks against live pod specs — no CRD required
GET /api/v1/efficiencyresource/efficiencyPod specs + metrics.k8s.io; Goldilocks-style verdict chips per container
GET /api/v1/rbacresource/rbacviewJoins ClusterRoles/Roles/Bindings; flags wildcard permissions
GET /api/v1/certsresource/certtrackerParses kubernetes.io/tls secrets with stdlib crypto/x509
GET /api/v1/metrics/pod/…/cpuresource/vmmetricsVictoriaMetrics time series — enables historical sparklines on pod detail
GET /api/v1/metrics/capabilitiesresource/vmmetricsVM reachable? Gates historical metrics UI components
GET /api/v1/notifications/configresource/notifyGraph API credential status + digest schedule
POST /api/v1/notifications/testresource/notifySends test email via Microsoft Graph API
GET /api/v1/daemonset/…/historyresource/daemonsetControllerRevision-based rollout history
PUT /api/v1/daemonset/…/rollbackresource/daemonsetRollback to target revision
GET /api/v1/access/{namespace}resource/accessSelfSubjectAccessReview — RBAC-aware UI rendering
GET /api/v1/summaryresource/summaryMachine-readable cluster health snapshot for ISMS Core polling

No New Service

All native endpoints live in the existing modules/api/ binary. No sidecar, no additional deployment, no new image to maintain. The Angular frontend talks to one API, same as before.

Auth Already Wired

Bearer token authentication, CSRF middleware, and RBAC are inherited from the original API. New endpoints get security for free — no duplicate auth logic.

Detection-Gated Features

Optional integrations (VictoriaMetrics, Kubescape, Gateway API, MetalLB) are detected at runtime via status endpoints. The Angular frontend polls every five minutes and shows or hides nav items accordingly — no configuration required.

Six things worth knowing before you do this.

The hard lessons, documented so you don't have to rediscover them.

B-44

ngx-charts version is a silent runtime killer

Angular 17 requires @swimlane/ngx-charts@22.0.0. Angular 18-21 requires v23.1.0. Wrong version compiles without errors, passes lint, and passes tests — then renders blank chart containers at runtime. No console error. Add a peer override in package.json and pin it explicitly.

B-31

Locale output path changes silently

Angular 17 changed the localized build output from dist/browser/<locale>/ to dist/<locale>/. If ls .dist/public/ shows a browser/ subdirectory, your nginx paths will 404 in production. Must be verified by inspecting the directory tree after every major version bump, not just checking that the build succeeds.

MDC-01

MDC button state layers override background-color

Angular Material v15+ wraps mat-button in MDC's state layer system. A background-color set on the host element is silently overridden at paint time. The fix: background-color: ... !important targeting the host, or use CSS custom properties (--mat-protected-button-container-color). We hit this on the nav active-item highlight.

NGL-01

ngx-layout is asynchronous

fxFlex and fxLayout directives apply their styles during Angular's second change detection pass. On first render, the host element has no flex layout. Visible as blank components that appear correctly after a click event triggers another CD pass. Fix: replace with static CSS classes for any performance-critical or initially-invisible wrapper elements.

KD-01

kd-card [expandable]="false" silently drops slots

The card component wraps the [actions] content slot in *ngIf="expanded && expandable". With expandable=false, the actions projection point is never created — no error, no warning, nothing in the DOM. This caused three overview pages to appear blank on load. The fix: use [graphMode]="true" which bypasses the slot guard.

CSS-01

overflow: hidden breaks sticky columns

overflow: hidden creates a new stacking context. When applied to a card or container, it prevents position: sticky on descendant table columns from working — the sticky column scrolls with the rest of the table. Fix: overflow: clip (Chrome 90+, Safari 16+) — clips content without creating a stacking context.

The result.

A production deployment on a real K3s cluster. All gates passed.

16 → 21 Five major versions upgraded
44 Issues resolved
14 Native features integrated
9 Locales (i18n preserved)
3 Visual polish rounds
0 External service dependencies added

The original team maintained the Kubernetes Dashboard for almost a decade. Building for that many cluster configurations, Kubernetes versions, and browser environments is no small thing — we found that out ourselves the moment we started the upgrade. The 44 tracked issues we worked through existed because the codebase was serving a genuinely difficult problem. Thank you for keeping it alive as long as you did.

Read the full paper (coming soon)

A detailed technical paper — Migrating a Retired Kubernetes Dashboard from Angular 16 to 21 using Claude Code — documenting the methodology, toolchain, AI workflow, and per-step decisions is in preparation.