-
Notifications
You must be signed in to change notification settings - Fork 1
Styling
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:
- Abstraction: Components author what to style — typed C# values, never CSS strings.
- One semantics, two targets: the same style vocabulary drives dp values on native (Photon) and CSS on the web.
- Performance: build-time class generation and deduplication — no CSS-in-JS runtime overhead.
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.
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.
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" }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.
| 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. |