Skip to content

WriteOnceComponents

Edgar Mesquita edited this page Jul 31, 2026 · 6 revisions

Write-Once Components (Shared Architecture)

The core promise of eQuantic.UI's multi-target story: components are authored once, in C#, against an abstract vocabulary — and realized per target: DOM + CSS on the web, GPU pixels through Photon on native. Not "two similar APIs" — literally the same class.

Living plan: docs/SHARED-COMPONENTS-PLAN.md in the main repo (layering, styling rules, migration log). This page is the overview.

The layers

Package Role Depends on
eQuantic.UI.Primitives The shared foundation: colors, design tokens, typed styles, abstract nodes, component model — (zero)
eQuantic.UI.Components The write-once component library (28 components) Primitives only
eQuantic.UI.Web Web realizer: abstract tree → HtmlElement/DOM + CSS (SSR) + generated stylesheet Core, Primitives
eQuantic.UI.Native.Components Native realizer: abstract tree → Photon display list Primitives, Engine
TS runtime (@equantic/runtime) Client-side lowering (hydration) + embedded library modules

The abstract vocabulary

Structure: Box (+BoxStyle — paint, opacity, transform, aspect-ratio, hover/focus diffs), Row/Column (gap-owned flex + Wrap/RunGap), Grid (+GridTrack — the CSS Grid twin), Stack/Positioned (z-order + ZIndex), AdaptiveNode (window size classes, zero listeners), ScrollView (+Sticky), Overlay (viewport layer, Modal flag), Anchored (floating panels: placements, outside-tap scrim, MatchAnchorWidth, OpenOnHover). Content: Text (type roles), TextEntry, Icon (curated + any pack glyph), Image, Spinner. Interaction & motion: Pressable (≥48dp hit contract), Link (real anchors on web, the host navigation seam on native), DragDismiss (the sheet gesture), LoopMotion, Presence (enter/exit motion). Plus value types (EdgeInsets, SizeValue Hug/Fill/Fixed, CornerRadii, ColorToken light/dark pairs, StyleDiff, Transform2D) and the component model (StatelessComponent, StatefulComponent + SetState, mode-free ComponentContext).

Styling has no CSS plane — see the atomic style engine in DesignSystem: every style declaration becomes one deduplicated atomic class, byte-identical between SSR (C#) and hydration (TS).

Layout parity is a contract: the C# flex engine (native) and CSS flex (web) implement the same spec — leftover-by-weight Flexible, the truncation contract (text shrinks to ellipsis before pushing siblings), stretch-fills-auto-only. Components author tokens, never resolved colors — one built tree realizes in light or dark.

How the same class reaches both targets

Native: PhotonHost expands Build() inline during layout and the realizer emits draw commands.

Web, server (SSR): the WebRealizer lowers the tree to HtmlElements. Colors become light-dark(#l, #d) so the DOM stays mode-free (theme switch = color-scheme). Core pages can embed write-once subtrees via the VisualNodeComponent bridge — and a Primitives StatefulComponent with [Page] is a full page (the server bridges it automatically).

Web, client: eqc (the C#→JS compiler) transpiles the same sources; the runtime's lowering (lowerVisualNode) mirrors the WebRealizer rule-for-rule — hydration parity is enforced by cross-pinned byte-exact style strings asserted in both the C# and TS test suites.

The library is runtime-provided

The transpiled eQuantic.UI.Components modules ship inside runtime.js, byte-pinned in CI against the live eqc output — apps import them from @equantic/runtime. User-authored write-once components need no wiring: they live in the app and flow through the normal page scan (eqc detects the Primitives component shapes, including direct-SetState stateful).

Generated, never hand-written

Every design-system value on the client is generated from the C# single source and byte-pinned:

  • PhotonCssGenerator → the normative stylesheet (custom properties, type-role classes, elevation, motion).
  • DesignSystemTsGeneratordesign-system.generated.ts (tokens, theme, Button size table).
  • IconTsGeneratoricons.generated.ts (glyph path data from the C# IconRegistry).

See DesignSystem.

The library (2026-07-31) — write-once is the ONLY architecture

The legacy web component library was excised (2026-07-31, −18,792 lines): eQuantic.UI.Web.Components, the old Core theme world and the Tailwind theme layer are gone. There is one library, written once.

28 components: Button, IconButton, Card, Divider, Badge, Chip, ProgressBar, Avatar, Banner, Checkbox, Switch, RadioGroup, ListItem/List, Tabs, EmptyState, Skeleton, AppBar, BottomNavigation, TextInput, SearchField, Dialog, Toast, BottomSheet (drag-to-dismiss), Select, Menu, Popover, Tooltip (hover-revealed, zero JS), Drawer, Accordion, Table.

Each ships with web realizer pins, native goldens (light+dark), pinned transpiled fixtures executed in vitest, and the live showroom (/ and /shared in DefaultUIDashboard — SSR + hydration identity + interaction verified end-to-end by the Playwright suite). Systems shipped alongside: the state-transition motion system (Presence enter/exit), the pointer pipeline (hover, drag-to-dismiss with slop/cancel/glide), the scroll compositor (real Sticky pinning) and anchored overlays.

Clone this wiki locally