Viking-UI — The Zero-Dependency UI Kit
Chapter 32: Viking-UI — The Zero-Dependency UI Kit
The frontend design language of the platform is delivered by the published package @dataengineeringformachinelearning/viking-ui, with packages/viking-ui/ as the single source of truth for every design-system layer: token SCSS, static CSS bundles, framework-neutral Web Components, shared utility exports, and Angular wrapper components. The historical split that placed library ownership under frontend-specific paths has been unused; apps now consume the package the way they would consume an external-style library, even inside the monorepo. The package ships native Angular standalone components with zero third-party UI runtime dependencies, plus browser-ready bundles for Astro, Django, Swagger, and unmanaged HTML. Icons use an internal inline-SVG registry, charts render as native SVG paths, modals use the platform <dialog> element, and every color resolves through THEME.md semantic tokens — light/dark modes, the 8px primary spacing grid, 16px main content typography, and 14px UI chrome are enforced by construction rather than convention. Intrinsic viking-grid columns="auto" and viking-switcher contracts form tracks from available content space rather than device names, preserving readable minimums, equal-height cards, aligned action rows, and natural row-to-column flow from 320px and 400% zoom through wide operational canvases. The system covers the full DEML component surface, from viking-button and viking-badge through viking-command, viking-editor, viking-kanban, viking-tabs, viking-table, and viking-toast.
Information-dense metric groups follow a wide-card density contract: one column when space is constrained and no more than two equal columns on larger canvases, equivalent to 6/12 per card. Status metrics, KPI tiles, CES gauges, and explore-card metrics stretch to the tallest peer in their row so repeated surfaces retain a stable silhouette. Labels, supporting captions, and values remain on one line when presented inside these compact operational tiles; when a narrow viewport cannot preserve that line, the component clips with an ellipsis while retaining the complete accessible name instead of making one card taller than its neighbors. Four-across 3/12 metric layouts are prohibited for dense components because they compress content, force unpredictable wrapping, and slow scanning. Simple navigation and non-content collections are not metric grids and may still use their documented column counts.
Design philosophy (THEME.md)
Viking-UI expresses precision engineering and high-end industrial tech — see the canonical token matrix in THEME.md:
- Dark-first engineering aesthetic — deep charcoals, machined metallic edges, no decorative noise.
- Luxurious minimalism — every pixel earns its place; data and metrics dominate ornament.
- Tactile surfaces — subtle top-edge highlights, restrained elevation, crisp borders.
- Refined accent discipline — deep teal for primary action, rich crimson for secondary emphasis and danger.
- WCAG 2.1 AA — contrast, focus rings, 44px mobile touch targets, keyboard navigation.
Every surface — dataengineeringformachinelearning.com, deml.app, ui.dataengineeringformachinelearning.com, Django templates, and Swagger UI — loads the same compiled viking-ui.css bundle built from packages/viking-ui/src/styles/ and synced via scripts/sync_design_system.py; external sites can load the same style bundle from jsDelivr. The package also publishes web-components.js, viking-ui-elements.js, widget.js, tokens.json, manifest, icons, and site-drakkar subpaths so static-site consumers can use framework-neutral pieces without importing Angular.
Unified design governance
DEML unifies UI through a layered rule stack so agents, contributors, and surfaces never drift:
| Layer | Document | Purpose |
|---|---|---|
| Cursor / IDE | .cursorrules | Mandatory viking-* imports, composable field stacks, zero hardcoded styles |
| Tokens | THEME.md | Canonical --viking-* palette, component standards, maintenance |
| Platform | AGENTS.md | Viking-UI Uniformity Law, architecture and security invariants |
| Narrative | This chapter | Consumption patterns, build, publish, accessibility contracts |
Composable structure, Viking palette: Viking-UI adopts composable primitive ergonomics — viking-field wrapping controls, variant-driven viking-button, dark-first viking-card surfaces — with deep charcoals, machined metallic borders, and restrained teal/crimson. The result is premium restrained luxury: every pixel earns its place, metrics dominate ornament, and tactile surfaces use subtle top-edge highlights instead of glass blur or neon glow.
Brand artwork contract: the DEML mark has two immutable portable-asset colors: brand navy #070C20 (--viking-brand-navy) and brand blue #0078FF (--viking-brand-blue). Logos, favicons, application icons, and social-preview images may embed those exact values so they render correctly without a stylesheet; application components must still use semantic Viking-UI variables rather than copying the literals.
Component law for all Angular work: import components from @dataengineeringformachinelearning/viking-ui or the compatibility @dataengineeringformachinelearning/viking-ui/angular entrypoint only. Do not add Material, Bootstrap, or parallel button/input/chart implementations. When a primitive is missing, extend packages/viking-ui/ first, export it from src/public-api.ts, then consume it from deml.app. Non-Angular pages use the single synced viking-ui.css bundle with semantic var(--viking-*) aliases, Web Components from web-components.js, and framework-neutral utility subpaths such as @dataengineeringformachinelearning/viking-ui/icons — never inline hex palettes or reach into package source paths.
Changes to design governance must update .cursorrules, THEME.md, AGENTS.md, and README.md together, then propagate via scripts/sync_content.py and scripts/sync_design_system.py.
deml.app ships a marketing-parity landing page at /home (hero, capability bands, pricing, Polars-style whitepaper CTA) so the authenticated app feels cohesive with dataengineeringformachinelearning.com. Unauthenticated visitors hitting / are routed to /home; signed-in users go to /dashboard.
Consumption follows the published path first: Angular app shells install and load from npm (npm install @dataengineeringformachinelearning/viking-ui) and consume components + CSS from that package. The package root packages/viking-ui/ is both the framework-neutral source of truth and the Angular wrapper layer: src/styles/ owns tokens and static CSS, src/web/ owns Web Components, src/core/ owns shared registries and utilities, src/lib/ owns Angular standalone wrappers, and src/public-api.ts is the public Angular barrel. Production pages consume components directly — buttons, fields, charts, modals, toasts, and selects — rather than embedding a library-specific gallery inside deml.app. Angular Material, ng-apexcharts, and @angular/cdk have been removed from the frontend; telemetry charts use native SVG viking-chart (line, bar, donut). The project is registered as an ng-packagr library in angular.json, and tests validate manifest parity during release. Component documentation and visual regression live in the standalone docs site (viking-ui-docs/, deployed to ui.dataengineeringformachinelearning.com) — build with npm run build:viking-ui-docs from the repo root. Components follow modern Angular idioms end-to-end: signal-based input()/model() APIs, OnPush change detection, constructor parameter injection (never field-level inject() in library code — avoids NG0203 in cross-package bundles), and ControlValueAccessor implementations on every form control so both template-driven (ngModel) and reactive forms work out of the box.
Non-Angular surfaces (marketing Astro pages, Django templates) load the single static CSS bundle synced from the design system:
<link rel="stylesheet" href="/assets/viking-ui.css" />
External HTML hosts can also use the jsDelivr CDN:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@dataengineeringformachinelearning/[email protected]/dist/viking-ui.css"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@dataengineeringformachinelearning/[email protected]/dist/web-components.js"
></script>
Framework-neutral utility imports stay Angular-free for Astro, static-site generation, and build scripts:
import { resolveVikingIcon } from "@dataengineeringformachinelearning/viking-ui/icons";
import { SITE_NAV_LINKS } from "@dataengineeringformachinelearning/viking-ui/site-drakkar";
import tokens from "@dataengineeringformachinelearning/viking-ui/tokens.json";
import {
VikingButton,
VikingCard,
VikingToastService,
} from "@dataengineeringformachinelearning/viking-ui";
@Component({
imports: [VikingButton, VikingCard],
template: `
<viking-card>
<viking-button variant="primary" icon="check" (pressed)="save()"
>Save</viking-button
>
</viking-card>
`,
})
export class Example {
private readonly toasts = inject(VikingToastService);
save = (): void => void this.toasts.show({ text: "Saved.", tone: "success" });
}
Build
Canonical SCSS lives in packages/viking-ui/src/styles/ (tokens, components, navbar, page shell, and surface-specific static styles). Static CSS for marketing, backend, docs, Swagger, and widgets is compiled by packages/viking-ui/scripts/build-css.mjs. Framework-neutral JavaScript bundles (web-components.js, viking-ui-elements.js, icons.js, site-drakkar.js, and widget.js) are emitted by packages/viking-ui/scripts/build-elements.mjs.
From repo root:
npm run test:viking-ui --prefix frontend # Vitest component tests
npm run build:viking-ui --prefix frontend # ng-packagr → dist/viking-ui
npm run build:viking-ui:package # viking-ui.css, token JSON, Web Components
python3 scripts/sync_design_system.py # fan-out to marketing, backend, frontend /assets/
npm run build:viking-ui-docs # docs site + prebuild static CSS
The frontend build:viking-ui-css script delegates to packages/viking-ui for backward compatibility. Railway/Docker frontend builds consume the built package CSS rather than owning a separate style tree, and no new documentation or code should reference unused frontend-local library paths.
The main Angular app rebuilds the library automatically via prebuild / prestart / pretest hooks.
Version bump
The published version lives in packages/viking-ui/package.json. Bump before every npm publish — npm rejects re-publishing an existing version.
Follow Semantic Versioning:
| Bump | When |
|---|---|
| patch | Bug fixes, token/CSS tweaks, a11y fixes |
| minor | New viking-* components, additive APIs |
| major | Breaking changes to inputs, outputs, or removed exports |
cd packages/viking-ui
# Choose one:
npm version patch --no-git-tag-version # 1.0.0 → 1.0.1
npm version minor --no-git-tag-version # 1.0.0 → 1.1.0
npm version major --no-git-tag-version # 1.0.0 → 2.0.0
--no-git-tag-version updates only package.json (and package-lock.json if present in that folder); commit the bump with your release changes. After bumping, rebuild so dist/viking-ui carries the new version.
Publish to npm
Published scope: @dataengineeringformachinelearning (npm org dataengineeringformachinelearning). You must be a member of that org and use a 2FA one-time password when publishing.
cd packages/viking-ui
npm run test
npm run build
npm pack
npm publish --access public --otp=YOUR_CODE
Install in downstream Angular apps:
npm install @dataengineeringformachinelearning/viking-ui
Peer dependencies: @angular/core, @angular/common, and @angular/forms ^22.
Quality & accessibility
The library carries a machine-readable manifest (viking.manifest.json) that maps each component to its Angular exports and the date of the last audit. The npm run check:viking-upstream script diffs manifest coverage and exits non-zero when the catalog drifts — surfacing gaps in CI the same way a failing contract test would. Accessibility is treated as a WCAG 2.1 AA / Section 508 contract: visible :focus-visible rings on every interactive element, full keyboard navigation for listboxes, menus and the command palette, role/aria-* semantics throughout, and prefers-reduced-motion handling on animated pieces such as skeletons and progress bars. The suite is verified by dedicated Vitest component tests (npm run test:viking-ui) that compile real templates through the AnalogJS Angular plugin, plus ng lint accessibility rules over the library's inline templates.