Skip to content

Styling

Edgar Mesquita edited this page Aug 8, 2026 · 9 revisions

eQuantic.UI Styling Architecture

Core Philosophy

eQuantic.UI provides a Flutter-inspired developer experience (DX) while leveraging the full power of each rendering target. The styling architecture is built on three pillars:

  1. Abstraction: Components author what to style — typed C# values, never CSS strings.
  2. One semantics, two targets: the same style vocabulary drives dp values on native (Photon) and CSS on the web.
  3. Performance: build-time class generation and deduplication — no CSS-in-JS runtime overhead.

1. Styling Components — Typed C#, No CSS Plane

Component styling is authored entirely in typed C# (BoxStyle, StyleDiff, design tokens) and lowered by the atomic style engine: every regular declaration becomes ONE deduplicated atomic class, byte-identical between SSR (C#) and hydration (TS); theme colors reference var(--eq-color-*) custom properties. Hover/focus states, window-size-class adaptivity, sticky positioning and transforms are all part of the vocabulary — zero JavaScript involved.

The full engine — laws, pseudo-states, size classes, the generated stylesheet — is documented in DesignSystem. The abstract vocabulary itself (Box, Row/Column, Grid, Stack, …) is documented in Write-Once Components.

Theming = providing an IAppTheme

Themes are typed C# too — colors as light/dark ColorToken pairs, type roles, shape scale, elevation — selected in one line and bridged SSR→client:

builder.Services.AddUI(options =>
{
    options.ScanAssembly(typeof(Program).Assembly)
           .UseTheme(PhotonTheme.Instance);          // or MaterialTheme.FromSeed(color)
});

Two implementations ship: PhotonTheme (the default) and MaterialTheme (real Material 3, including dynamic color from a seed). Swap the theme and every component rebrands — server and client, web and native. Runtime light/dark switching is a service (IThemeController); see DesignSystem.


2. The HTML Escape Hatch

The low-level web layer (eQuantic.UI.Core) mirrors the DOM 1:1. Every HtmlElement exposes the attributes the browser has:

public abstract class HtmlElement : IComponent {
    /// <summary>Raw CSS classes (space-separated).</summary>
    public string? ClassName { get; set; }

    /// <summary>Inline styles for dynamic values (e.g., coordinates, colors from DB).</summary>
    public HtmlStyle? Style { get; set; }
}

This contract means the web layer does not enforce any CSS framework — it renders HTML attributes. DynamicElement goes further and renders any tag with any attributes. Standard CSS classes from any stylesheet work natively:

new DynamicElement("aside") { ClassName = "my-sidebar" }

3. Bringing external CSS

The framework ships exactly one styling engine — the typed C# pipeline above. There is no bundled CSS framework and no adapter layer: a page that needs hand-written markup uses the escape hatch (HtmlElement/DynamicElement + ClassName), and any stylesheet the app brings — its own CSS file, a utility framework it builds itself — is referenced like on any web project:

builder.Services.AddUI(options =>
{
    options.ConfigureHtmlShell(shell => shell.AddStylesheet("/css/app.css"));
});

The framework neither generates nor processes that CSS; it is the app's own build concern.

4. Package Roles

Package Purpose
eQuantic.UI.Primitives Typed styles, design tokens, the abstract vocabulary — the styling source of truth.
eQuantic.UI.Core The DOM-mirror layer: HtmlElement, ClassName, HtmlStyle, DynamicElement.
eQuantic.UI.Web The web realizer + atomic style engine (StyleAtomizer) + generated stylesheet.

Clone this wiki locally