# eQuantic.UI Styling Architecture
> đ This page in: English · **[PortuguĂȘs](Styling-pt-BR)**
## 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, as 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, with 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, with zero JavaScript involved.
### Hover is derived, and it is pointer-only
*Since **0.2.0-preview.24***
The variant palette stays five sub-tokens; hover resolves by DERIVATION: `VariantColors.Hover` is
the per-channel midpoint of `Base` â `Pressed` (`ColorToken.MidpointWith`, the same color
`color-mix(in srgb, Base 50%, Pressed)` lands on), and quiet variants hover on `SurfaceSubtle`.
Components author it as an ordinary `BoxStyle.Hover` diff, so both realizers already knew how to
paint it: the web as an atomic `:hover` rule, Photon through the pointer pipeline.
Two rules ride the emission itself. Every atomic `:hover` rule is wrapped in
`@media (hover: hover)`, so a touch browser's sticky emulated hover finds no rule, and the gate wraps
the rule, never the hash, so SSR and hydration still compare identical class strings. And on
Photon, hover is tracked by layout PATH as a CHAIN of everything under the pointer, because CSS
`:hover` matches every ancestor: hovering a tooltip's trigger hovers the tooltip too, and a
component rebuild, which replaces every node instance, no longer drops the hover it repainted.
### What is on top
*Since **0.2.0-preview.23***
Photon paints in CHILD ORDER: a later sibling is on top, and nothing has to be said. CSS stops
working that way the moment boxes acquire stacking contexts, so the web realizer spells out what
native gives for free, in the design system's own words, never in CSS's.
**`Elevation` decides what is above, not only how deep the shadow is.** It drew a shadow and
nothing else, which is half a sentence: a raised surface that anything painted after it covers is
not raised. Levels 1â5 are the CONTENT plane, and they are the only numbers an author writes.
**Chrome is a plane, not a raised card.** Anything pinned (a `Sticky` header, floating or in flow)
sits above every content elevation. It used to sit one step above nothing, which is a number
competing with other numbers: give elevation the 1â5 range and a merely raised card would out-stack
the header and scroll straight over it.
That is why `Sticky` has no `ZIndex` and never will. A z-index is CSS vocabulary, and an author who
has to pick one is being asked to know which numbers everything else chose. Elevation says *how
high*, the realizer decides *how that is spelled*, and chrome is not on the same scale as content.
One exception to *one declaration, one class*, and it is not an exception at all once named: a
**vendor prefix is not a declaration of its own**, it is the same one written for another engine.
The pair shares a class whose rule names both, standard property last. Alone in a rule,
`-webkit-backdrop-filter` is dropped whole by an engine that only takes the standard name
(`insertRule` leaves an EMPTY rule in Chromium, measured), so the class was computed, hashed,
emitted, put on the element, and did nothing. *Since 0.2.0-preview.21.*
### The code face
*Since **0.2.0-preview.7***
A monospaced run (`Text` with `Mono`, a rich run, a `CodeBlock`) draws in a stack the framework
owns, and it lands on an **atomic class**, which beats a `body { font-family }` rule, and whose
name is a content hash that moves between builds. So there was nowhere to hang your own code face.
The hook is a variable:
```css
:root { --eq-font-mono: 'JetBrains Mono', ui-monospace, monospace; }
```
Every mono run follows it, with the platform stack as the fallback when nothing sets it.
The **measurer reads the same variable**, and that is not an implementation detail you can ignore
if you ship a code editor: `CodeEditor` places its caret from a measured column advance, so
measuring in one face while drawing in another puts the caret beside the character it is on rather
than under it.
The full engine (laws, pseudo-states, size classes, the generated stylesheet) is documented in
[DesignSystem](DesignSystem). The abstract vocabulary itself (`Box`, `Row`/`Column`, `Grid`,
`Stack`, âŠ) is documented in [Write-Once Components](WriteOnceComponents).
### 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:
```csharp
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](DesignSystem).
---
### A border on some edges
*Since **0.2.0-preview.13***
```csharp
new BoxStyle
{
BorderWidth = 1,
BorderColor = theme.Border,
BorderSides = BorderSides.Top, // a rule above a section
}
new BoxStyle
{
BorderWidth = 3,
BorderColor = theme.Colors(Variant.Primary).Base,
BorderSides = BorderSides.Start, // an accent bar down a callout
}
```
`BorderSides.All` by default, so every box written before this renders byte for byte the same: a
full border still emits the one-declaration shorthand and its single atomic class.
None of these is a `Divider`. A `Divider` is a sibling **between** two things; these belong to the
box itself, and the only way to draw one was a `Row` wrapping a one-dp `Box`, which is a layout lie
about what the design meant.
`Start` and `End` rather than `Left` and `Right`, matching `EdgeInsets`: they mirror in a
right-to-left reading, and an accent bar that stays on the left when the text flows the other way is
on the wrong side of it.
> **Fence.** With a corner radius, a partial border differs slightly between targets at the corner
> where a present edge meets an absent one: the web mitres it, Photon squares it. At radius 0 (a
> rule, an accent bar, a table cell, which is what partial borders are for) the two are identical. Use
> `BorderSides.All` for a rounded outline.
## 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:
```csharp
public abstract class HtmlElement : IComponent {
/// Raw CSS classes (space-separated).
public string? ClassName { get; set; }
/// Inline styles for dynamic values (e.g., coordinates, colors from DB).
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:
```csharp
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:
```csharp
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. |