Architecture Comparison: Dual vs iris vs iX
Side-by-side comparison of the three CSS architectures. Dual is the POC.
iris is the current production system. iX is Siemens Industrial Experience.
Direction: the Dual column is a proof of concept, not a shipping target. iris-core adopts Dual's concepts additively under the iris namespace (dual-output from one source, SUIT classes, a component-token tier) and draws status colors from iX — no separate dual package ships. See research/README.md.
§1 — Size & Scope
| Metric | Dual POC | iris-core | iX |
| Components | 6 | 15 | 116 |
| CSS bundle size | 51.8 KB (full preset) · 35.1 KB variables-only · +23 KB WC JS | 185 KB (monolithic) | ~350 KB (theme + components) |
| CSS variables | 322 (--dual-*) | ~663 (--iris-*) | ~2,400 (--theme-*) |
| Token layers | 3 (ref → semantic → component) | 2 (base → semantic) | 3 (core → theme → component) |
| Component token vars | color-only, per component (button 9, card 5, …) | ~1 per component | 25+ per component |
| JS runtime required | Optional (WC path only) | Yes (Stencil WCs) | Yes (Stencil WCs) |
§2 — Architecture Comparison
| Feature | Dual | iris | iX |
| CSS-only usage (no JS) |
✓ Light-DOM classes |
✗ Shadow-encapsulated only |
✗ Requires Stencil runtime |
| Web component output |
✓ Same SCSS source |
✓ Primary delivery |
✓ Primary delivery |
| Per-component CSS files |
✓ Per build output |
✗ Monolithic iris.css |
◐ In dist/collection/ (unexported) |
| Component-level token overrides |
✓ --dual-button-fill etc. |
✗ Global theme only |
✓ --theme-button--* vars |
| CSS variable theming |
✓ .dual-theme-dark |
✓ .iris-ui-theme-dark |
✓ .theme-dark class |
| SCSS pre-compile override |
✓ $var before @use |
✗ No public SCSS API |
✓ SCSS mixins/vars |
| Naming convention |
SUIT CSS (PascalCase) |
None (mixed kebab) |
BEM-like (kebab-case) |
| Utility classes |
✓ 93 (u-* SUIT) |
◐ 84 typography only |
✗ None shipped |
| Grid system |
✓ 49 classes (FlexGrid + CssGrid) |
✗ |
✗ |
| Token source format |
JSON (DTCG-inspired) |
Figma → SCSS |
SCSS maps → CSS vars |
§3 — Token Architecture
| Aspect | Dual | iris | iX |
| Reference layer |
296 JSON tokens (iris + iX sources) |
~150 SCSS vars in base-variables/ |
SCSS maps in theme/core/ |
| Semantic layer |
73 tokens per mode, mode-aware (light/dark/sand/wireframe/dark-brand) |
~180 vars per theme file |
~600+ --theme-color-* vars |
| Component layer |
Per-component _variables.scss (color-only) |
Absent (1 var max) |
SCSS @mixin setVars per component |
| Semantic → Reference traceability |
✓ ref() in JSON |
◐ SCSS $var references |
✗ Compiled away |
| Mode support |
Light, Dark, Sand, Wireframe, Dark-brand |
Light, Dark, Sand, Wireframe, Dark-brand |
Classic Dark, Classic Light |
| Status colors |
From iX (alarm, success, warning, info, critical, neutral) |
Own set (green, red, orange, yellow) |
Full set + states + hover/active variants |
§4 — Component Authoring Pattern
| Aspect | Dual | iris | iX |
| Source format |
SCSS + template.html |
SCSS (in Stencil component) |
SCSS (in Stencil component) |
| Build tool |
Node.js (sass + custom build-wc.mjs) |
Stencil compiler |
Stencil compiler |
| Namespace parameter |
$namespace switches output |
Hardcoded per component |
Hardcoded per component |
| Adding a component |
Create folder + template + SCSS → done |
Stencil boilerplate + TypeScript |
Stencil boilerplate + TypeScript + tests |
| Template co-location |
template.html next to SCSS |
TSX in component file |
TSX in component file |
§5 — Delivery & Consumption
| Aspect | Dual | iris | iX |
| Consumer can use |
CSS classes OR web components OR SCSS @use |
Web components only |
Web components (+ framework wrappers) |
| Tree-shaking CSS |
Per-file imports (button.css, card.css) |
All-or-nothing (iris.css) |
Lazy-load per component (Stencil) |
| Framework wrappers |
Not needed (CSS classes work everywhere) |
@siemens-iris/iris-react, iris-vue, iris-angular |
@siemens/ix-react, ix-angular, ix-vue |
| Override mechanism |
CSS var (runtime) + SCSS var (compile) |
CSS var (global theme only) |
CSS var + SCSS mixin |
§6 — Key Differentiators
| Dual Advantage | What it enables |
| CSS-only consumption path | No JS dependency, works in any framework/CMS, instant adoption |
| Same source → dual output | WC and CSS classes stay in sync by construction, not discipline |
| Component-level token overrides | Scope color changes to one component without affecting others |
| JSON token traceability | Every semantic value traces back to a named reference token |
| SUIT CSS naming | Clear, collision-free naming without tooling — readable in DevTools |
| Dynamic WC generation | template.html → zero-config web component (no Stencil/TypeScript) |
| Per-file CSS delivery | Import only what you use — no 185KB monolithic bundle |
§7 — Standardized Benchmarks & Adoption Impact
Package benchmarks measured on the same axes across all three systems. Dual figures are the real built dist/ outputs; iris/iX figures are their shipped bundles. Adoption-impact rows are estimates (labeled) of what iris-core changes to adopt the Dual POC concepts, not measured values.
Measured — bytes on the wire (CSS, uncompressed)
| Load tier | Dual POC | iris-core | iX |
| Variables only | 35.1 KB (preset/variables-only.css) | n/a (no variables-only entry) | ~13 KB (theme file, vars-heavy) |
| Foundation (vars + base + utilities + grid) | 51.7 KB (preset/foundation.css) | 185 KB (whole iris.css) | ~13 KB theme + component CSS in JS |
| Full (everything enabled) | 51.8 KB (preset/full.css) | 185 KB | ~350 KB (theme + all components) |
| Per-granular-layer | variables 35.1 · base 3.3 · utilities 6.0 · grid 7.5 KB | not separable (monolith) | per-component in JS chunks |
| Web-component JS | 23.0 KB (6 components, components.js) | bundled per Stencil chunk | bundled per Stencil chunk |
Measured — structural quality gates (POC, verified at build)
| Gate | Dual POC | Why it matters for adoption |
--dual-* declarations | 322 across 5 modes + per-breakpoint | per-mode scopes are not duplication — one mode loads at a time |
| variablePrefix assertion | 0 foreign custom properties (build fails on drift) | guarantees the namespace contract iris-core needs |
| Config-gated layers | 4 granular + 3 preset barrels from one config | size becomes a consumer choice, not a fixed cost |
Reference aliases in dist | 0 (--dual-ref-* never ship) | resolved values only — no runtime alias indirection |
Estimated — iris-core adoption impact estimates, not measured
What adopting each Dual POC concept into iris-core is estimated to change. These are planning estimates from the build plans (P1–P7), not measured outcomes — the KPI scorecard in P1 is where measured deltas will be recorded.
| Dual concept adopted | Estimated iris-core impact | Baseline it moves |
| Config-gated dist (granular + presets) | a consumer can drop from the 185 KB monolith to a variables-only / foundation tier — estimated 60–80% reduction for a tokens-only consumer | 185 KB single entry → tiered entries |
| Dual-output (one SCSS → shadow + light DOM) | adds a CSS-only consumption path with no new authoring burden (same source); estimated zero net component-source growth | WC-only → WC + CSS classes |
| Component-token tier (color-only) | adds a scoped override surface iris-core lacks today; estimated ~5–10 color vars per component | ~1 override var → per-component set |
| SUIT class naming | readable, collision-free light-DOM classes; estimated no runtime cost (naming only) | mixed kebab → .Iris-* SUIT |
| Structural gates (P1) | !important +0, specificity +0, ≤ +50 selectors — a hard ceiling on the CSS the adoption adds | ungated → gated at CI |
The KB budget is deliberately not a fixed target — delivered size is a config outcome (see the config-gated dist tiers above). P1 keeps the structural gates and drops a fixed KB budget; P4/P5 make size a consumer choice.
Related:
Dual CSS Overview •
Dual-Output Architecture •
Token Architecture •
iris CSS Overview •
iX CSS Overview