Repository navigation
Styling de
🌐 Diese Seite auf: English · Português · Español · Français · Deutsch
eQuantic.UI bietet eine von Flutter inspirierte Entwicklungserfahrung (DX) und nutzt dabei die volle Kraft jedes Render-Ziels. Die Styling-Architektur steht auf drei Säulen:
- Abstraktion: Komponenten schreiben, was gestylt wird, als typisierte C#-Werte, nie als CSS-Strings.
- Eine Semantik, zwei Ziele: Dasselbe Style-Vokabular treibt dp-Werte auf Native (Photon) und CSS im Web.
- Performance: Klassenerzeugung und Deduplizierung zur Build-Zeit, ohne den Mehraufwand einer CSS-in-JS-Runtime.
Das Styling von Komponenten wird vollständig in typisiertem C# geschrieben (BoxStyle, StyleDiff,
Design-Tokens) und von der atomaren Style-Engine abgesenkt: Jede gewöhnliche Deklaration wird zu
EINER deduplizierten atomaren Klasse, byte-identisch zwischen SSR (C#) und Hydration (TS);
Theme-Farben verweisen auf var(--eq-color-*)-Custom-Properties. Hover- und Focus-Zustände, die
Adaptivität nach Fenstergrößenklasse, klebende Positionierung und Transforms sind alle Teil des
Vokabulars, ohne jedes JavaScript.
Seit 0.2.0-preview.24
Die Variantenpalette bleibt bei fünf Sub-Tokens; der Hover löst sich durch ABLEITUNG auf:
VariantColors.Hover ist die Kanalmitte von Base → Pressed (ColorToken.MidpointWith, dieselbe
Farbe, auf der color-mix(in srgb, Base 50%, Pressed) landet), und stille Varianten hovern auf
SurfaceSubtle. Komponenten schreiben ihn als gewöhnlichen BoxStyle.Hover-Diff, beide Realisierer
wussten also schon, wie man ihn malt: das Web als atomare :hover-Regel, Photon über die
Zeiger-Pipeline.
Zwei Regeln reiten in der Ausgabe selbst mit. Jede atomare :hover-Regel wird in
@media (hover: hover) gehüllt, sodass der klebende, emulierte Hover eines Touch-Browsers keine
Regel findet, und das Gate hüllt die Regel, nie den Hash, sodass SSR und Hydration weiterhin
identische Klassenstrings vergleichen. Und auf Photon wird der Hover nach Layout-PFAD als KETTE
alles dessen verfolgt, was unter dem Zeiger liegt, denn CSS' :hover trifft jeden Vorfahren: Den
Auslöser eines Tooltips zu hovern hovert auch den Tooltip, und ein Komponenten-Neuaufbau, der jede
Knoteninstanz ersetzt, verliert den Hover, den er neu gemalt hat, nicht mehr.
Seit 0.2.0-preview.60
Der Browser hält eine Color als reine Daten, { r, g, b, a }, weil die Styles der Runtime, das
generierte Design System und ein Hydration-Payload alle eine ohne Konstruktor erzeugen. Der
browserseitige Code einer Komponente schreibt sie weiterhin als C#: Brand.WithOpacity(0.8f),
a.MidpointWith(b), new Color(…), default(Color), $"{color}" und eine Methodengruppe wie
Func<float, Color> fade = Brand.WithOpacity. eqc senkt jede davon auf die Color der Runtime ab,
und der Text ist der Record-Text, den .NET schreibt,
Color { R = 248, G = 113, B = 113, A = 204 }. Ein ausgerichtetes Interpolationsloch, eines mit Format und string.Format schreiben denselben Text, ausgerichtet, wo sie [object Object] schrieben, und ein Curve ebenso (Seit 0.2.0-preview.64).
Vor 0.2.0-preview.60 rendeten diese auf dem Server und warfen im Browser, sobald die Seite hydrierte
(withOpacity is not a function), und ein eigener Typ der Anwendung mit dem Namen Color wurde als
der des Vokabulars gebaut.
Seit 0.2.0-preview.61
Eine Curve ist im Browser ebenfalls reine Daten, { x1, y1, x2, y2 }, wo auch immer der Browser
einer begegnet: ein Preset, die Kurve einer Bewegungsrolle, das Easing einer Transition, eine Kurve,
die eine Komponente baut, eine Kurve im Zustand einer Seite. Der browserseitige Code einer Komponente
liest und baut eine Kurve also so, wie C# es tut: Curve.Standard.X1, Motion.Press.Curve,
new Curve(0.2f, 0.9f, 0.3f, 1.25f) als Easing eines TransitionSpec, ==, with, eine
Dekonstruktion und $"{curve}", was Curve { X1 = 0.2, Y1 = 0, X2 = 0, Y2 = 1 } druckt, wie .NET es
tut.
var easing = new Curve(0.2f, 0.9f, 0.3f, 1.25f);
var style = new BoxStyle
{
Transition = new TransitionSpec(StyleChannels.Colors, 150) { Easing = easing },
};Der Evaluator bleibt auf dem Server und in den nativen Shells. Eine Web-Transition ist eine
CSS-Timing-Funktion, der Browser wertet die Kurve also selbst aus, und eine Komponente, die
curve.Ease(t) oder CurveEvaluator.Ease(curve, t) aufruft, lässt den Build mit EQ2010
scheitern. Geben Sie die Kurve einem TransitionSpec, oder rufen Sie den Evaluator aus Servercode
auf.
Vor 0.2.0-preview.61 war ein Preset im Browser ein Array und die Kurve einer Bewegungsrolle ein Name,
also las ein Punkt undefined, new Curve(…) warf "is not a constructor", sobald die Seite
hydrierte, und curve.Ease(t) baute und warf.
Seit 0.2.0-preview.60
Ein StyleDiff ist, wie eine Box aussieht, während ein Zustand aktiv ist: BoxStyle.Hover unter dem
Zeiger und BoxStyle.Focus und BoxStyle.Pressed, während ihr Control fokussiert oder gedrückt ist
(siehe unten). Jedes Mitglied, das er setzt, gilt, im Web und auf Photon, und jedes Mitglied, das er
ungesetzt lässt, behält den Wert der Basis. Bis zu dieser Version wandte ein Hover auf Photon nur
seinen Hintergrund und seinen Rand an, und im Web konnte er eine Box nicht bewegen und seine eigenen
Schatten nicht ändern.
Box(new BoxStyle
{
Background = theme.Surface,
Elevation = 1,
Shadows = [new ShadowSpec(12, 30, -12, glow)],
Transition = new TransitionSpec(StyleChannels.Transform | StyleChannels.Shadow),
Hover = new StyleDiff
{
Transform = Transform2D.Translate(0, -2),
Elevation = 3,
Shadows = [new ShadowSpec(16, 36, -12, glow)],
},
}, content)Diese Karte hebt sich unter dem Zeiger um 2dp und vertieft ihr Glühen, und gleitet in beide Richtungen. Was jedes Mitglied tut, während der Zustand aktiv ist:
-
Background,BorderColor,BorderWidth,OpacityundBackdropBlurersetzen die der Basis. -
Transformersetzt dasTransformder Basis, so wie eine CSS-Regeltransformersetzt: Eine Karte, die im Ruhezustand gedreht ist und beim Hover angehoben wird, nennt beide in ihrem Hover,Transform2D.Rotate(2).WithTranslate(0, -2), undTransform2D.Scale(1)macht das des Ruhezustands rückgängig. -
ElevationundShadowsändern ihren Teil EINER Schattenliste, und der Rest der Liste bleibt: der Schatten der Elevation, dann die eigenen Schatten (die des Zustands ersetzen sowohlShadowals auchShadowsder Basis), dann das innere Highlight. Ein Zustand, der nichts zu zeichnen lässt, schreibt eine Liste, die nichts zeichnet. -
Gradientersetzt den Gradienten der Basis und behält das Glühen und das Gittermuster der Basis darunter.
Ein Schatten ohne Versatz, Weichzeichnung oder Spreizung zeichnet nichts, er bleibt also auf beiden
Zielen aus der Liste. Unter einer Transition gleiten beide Ziele die Deckkraft, das Transform, die
Farben und jeden Schatten, die eigenen eingeschlossen, und ein Schatten, den ein Zustand hinzufügt
oder wegnimmt, blendet ein oder aus (Photon seit 0.2.0-preview.61). Eine angehobene Box nimmt im Web
auch ihre Trefferfläche mit, wie jedes CSS-Transform, sodass ein Zeiger, der auf dem Rand ruht, von
dem sich die Box wegbewegt, sie anheben und wieder fallen lassen kann. Photon testet eine
transformierte Box noch dort auf Treffer, wo sie ausgelegt wurde, ihr ruhendes Transform
eingeschlossen (#513). Halten Sie eine Hebung
klein, oder legen Sie den Hover auf eine Box, die sich nicht bewegt.
StyleDiff ist seit dieser Version eine Klasse (es war ein Struct): Hover!.Value.Background wird
Hover!.Background, und eine Box, die keinen Zustand deklariert, trägt an seiner Stelle ein null.
Seit 0.2.0-preview.61
Ein Hover ist der eigene Zustand der Box: Der Zeiger ist über ihr. Ein Druck und ein Tastaturfokus
geschehen einem CONTROL, dem Pressable, also gelten Pressed und Focus für jede Box innerhalb
des Controls, während es gedrückt oder fokussiert ist: die Fläche und alles darin, was sagt, wie es
dann aussieht.
Pressable(Box(new BoxStyle
{
Background = theme.Surface,
Elevation = 2,
Transition = new TransitionSpec(StyleChannels.Transform, Motion.BaseMs),
Pressed = new StyleDiff { Transform = Transform2D.Scale(0.985f) },
Focus = new StyleDiff { BorderColor = theme.FocusRing, BorderWidth = 2 },
}, label), onPressed: save)Dieses Control drückt auf 98,5% ein und zurück und nimmt von der Tastatur einen Fokusrand, auf beiden Zielen. Die Regeln:
- Wenn Zustände zusammentreffen, schlägt ein Druck einen Fokus und ein Fokus einen Hover, Mitglied
für Mitglied, in der Reihenfolge, die §10 des Handoffs gibt.
Pressable.PressedBackgroundbleibt die stärkste Füllung. - Der Fokusring wird neben den Schatten der Box gezeichnet: Ein angehobenes oder glühendes Control behält seine Elevation und sein Glühen, während es den Fokus hat. Im Web ersetzte der Ring sie.
- Ein
Simulated-Druck oder -Fokus zeichnet, was ein echter zeichnet, den Ring eingeschlossen. - Ein Control ohne
PressedBackgroundbehält seine eigene Füllung, während es gedrückt ist. Im Web wurde es durchsichtig. - Eine Transition, die die Fläche deklariert, ist ihre eigene: Das Einblenden der gedrückten Füllung
ist eine Voreinstellung, die eine
Transitionan der Box überschreibt, sodass ein Druck, der skaliert, gleitet. Im Web ersetzte das Einblenden sie. - Ein deaktiviertes Control zeigt keinen Druck und keinen Fokus, auf beiden Zielen: nicht, wenn es
deaktiviert wurde, während es gehalten war, nicht in einem
Simulated-Bild und nicht, wenn ein aktiviertes Control um es herum gedrückt ist.
Wo die Ziele sich unterscheiden: Photon hat keine gehaltene Taste, eine Taste aktiviert ihr Control
beim Hinuntergehen, ein Pressed-Diff zeigt sich für eine gehaltene Taste also nur im Web. Und bei
einem Control innerhalb eines Controls drückt das Web auch das äußere, während das innere gedrückt
ist, so wie CSS' :active durch die Vorfahren aufsteigt, während Photon nur das Control drückt, das
der Zeiger getroffen hat.
Focus hat in dieser Version seine Bedeutung geändert, vom eigenen :focus-visible der Box, das eine
Box nie hat, zum Fokus ihres Controls: Ein früher geschriebenes Focus galt nirgends im Web und
nirgends auf Photon, und gilt nun.
Seit 0.2.0-preview.61
new Pinned(header)
{
Float = true,
ScrolledStyle = new StyleDiff
{
Background = theme.Surface,
Elevation = 2, // Flutters scrolledUnderElevation
BorderWidth = 1,
BorderColor = theme.Border, // die Haarlinie entlang der Unterkante des Headers
BackdropBlur = 24,
},
Transition = new TransitionSpec(StyleChannels.Colors | StyleChannels.Shadow),
}Jedes Mitglied von ScrolledStyle gilt, im Web und auf Photon, solange die Fläche, an die sich der
Header heftet, über Pinned.ScrolledThreshold (8dp) hinaus gescrollt ist. Der Diff wird über
Pinned.ScrolledBase gelegt: Ein Header zeichnet nichts Eigenes, und sein Rand ist die Haarlinie
entlang seiner Unterkante, der Kante, unter der der Inhalt scrollt. Unter der Transition des Headers
gleitet er auf beiden Zielen ein und aus.
Die Fläche ist die, an die sich position: sticky heftet: die nächste ScrollView um den Header,
oder die Seite, wenn er in keiner ist. Ein Header in einem scrollenden Panel folgt dem Panel, nicht
dem Fenster. Auf Photon scrollt die Seite selbst nie, ein Header außerhalb jeder ScrollView ist dort
also nie gescrollt.
Davor schrieb das Web vier von sieben Mitgliedern und einen Rand allein, also taten eine Elevation,
ein Gradient, ein Transform und die Shadows nichts, und Photon zeichnete davon nichts.
Seit 0.2.0-preview.23
Photon malt in KINDREIHENFOLGE: Ein späteres Geschwister liegt obenauf, und es muss nichts gesagt werden. CSS hört in dem Moment auf, so zu arbeiten, in dem Boxen Stapelkontexte erwerben, also buchstabiert der Web-Realisierer aus, was Native umsonst gibt, in den eigenen Worten des Design Systems, nie in denen von CSS.
Elevation entscheidet, was darüber liegt, nicht nur, wie tief der Schatten ist. Sie zeichnete
einen Schatten und sonst nichts, und das ist ein halber Satz: Eine erhobene Fläche, die alles
überdeckt, was nach ihr gemalt wird, ist nicht erhoben. Die Stufen 1 bis 5 sind die INHALTSEBENE, und
sie sind die einzigen Zahlen, die eine Autorin schreibt.
Chrome ist eine Ebene, keine erhobene Karte. Alles Angeheftete (ein Pinned-Header, schwebend
oder im Fluss) sitzt über jeder Inhaltselevation. Früher saß es eine Stufe über nichts, und das ist
eine Zahl, die mit anderen Zahlen konkurriert: Geben Sie der Elevation den Bereich 1 bis 5, und eine
bloß erhobene Karte würde den Header im Stapel überbieten und geradewegs darüber scrollen.
Darum hat Pinned kein Layer und wird nie eines haben. Ein z-index ist CSS-Vokabular, und von
einer Autorin, die eines wählen muss, wird verlangt zu wissen, welche Zahlen alles andere gewählt
hat. Die Elevation sagt wie hoch, der Realisierer entscheidet wie das geschrieben wird, und
Chrome liegt nicht auf derselben Skala wie der Inhalt.
Eine Ausnahme von eine Deklaration, eine Klasse, und es ist überhaupt keine, sobald sie benannt ist:
Ein Herstellerpräfix ist keine eigene Deklaration, es ist dieselbe, für eine andere Engine
geschrieben. Das Paar teilt eine Klasse, deren Regel beide nennt, die Standardeigenschaft zuletzt.
Allein in einer Regel wird -webkit-backdrop-filter ganz von einer Engine verworfen, die nur den
Standardnamen nimmt (insertRule lässt in Chromium eine LEERE Regel zurück, gemessen), also wurde
die Klasse berechnet, gehasht, ausgegeben, auf das Element gesetzt, und tat nichts. Seit
0.2.0-preview.21.
Eine atomare Klasse ist der schwächste Selektor, den es gibt. Eine Klasse ist Spezifität (0,1,0),
also überbietet jede Regel des generierten Stylesheets, die ein Element nennt (.eq-spinner rect ist
(0,1,1)), die Klasse, die ein Knoten trägt. Das macht die Einschränkung einseitig: Das generierte
Stylesheet darf nie eine Eigenschaft deklarieren, die ein Knoten atomar tragen kann, und eine
Kurzschreibweise zählt, weil sie jede Langform schreibt, die sie auslässt.
Der Spinner ist der gemessene Fall. Seine acht Balken tragen je ihr eigenes negatives
animation-delay, und diese Phase IST die Drehung; das Einblenden daneben war als
animation: eq-spinner-fade 800ms linear infinite geschrieben, was stillschweigend
animation-delay: 0s über alle acht schrieb. Serverseitig gerendert pulsierte der Ring an seinem
Platz, statt sich zu drehen, während der Client sich aus dem falschen Grund korrekt drehte, ein
Inline-Style, der jeden Selektor überbot. Diese Regel steht jetzt in Langformen, und eine Wache
behauptet, dass nichts außer Reduce Motion den Verzug eines Balkens deklariert. Dieselbe Form wie das
Herstellerpräfix oben: Die Klasse wurde berechnet, gehasht, ausgegeben, auf das Element gesetzt, und
etwas anderes gewann.
Seit 0.2.0-preview.7
Ein nicht proportionaler Lauf (ein Text mit Mono, ein reicher Lauf, ein CodeBlock) zeichnet in
einem Stapel, den das Framework besitzt, und er landet auf einer atomaren Klasse, die eine
body { font-family }-Regel schlägt und deren Name ein Inhaltshash ist, der sich zwischen Builds
bewegt. Es gab also keinen Ort, an dem man seine eigene Code-Schrift aufhängen konnte. Der Haken ist
eine Variable:
:root { --eq-font-mono: 'JetBrains Mono', ui-monospace, monospace; }Jeder Mono-Lauf folgt ihr, mit dem Plattformstapel als Rückfall, wenn nichts sie setzt.
Der Messer liest dieselbe Variable, und das ist kein Implementierungsdetail, das Sie ignorieren
können, wenn Sie einen Code-Editor ausliefern: CodeEditor setzt seinen Caret aus einem gemessenen
Spaltenvorschub, in einer Schrift zu messen und in einer anderen zu zeichnen setzt den Caret also
neben das Zeichen, auf dem er steht, statt darunter.
Die vollständige Engine (Gesetze, Pseudozustände, Größenklassen, das generierte Stylesheet) ist in
DesignSystem dokumentiert. Das abstrakte Vokabular selbst (Box, Row/Column,
Grid, Stack, …) ist in Write-once-Komponenten dokumentiert.
Themes sind ebenfalls typisiertes C# (Farben als Hell-/Dunkel-Paare von ColorToken, Typo-Rollen,
Formskala, Elevation), in einer Zeile gewählt und von SSR zum Client gebrückt:
builder.Services.AddUI(options =>
{
options.ScanAssembly(typeof(Program).Assembly)
.UseTheme(PhotonTheme.Instance); // oder MaterialTheme.FromSeed(color)
});Zwei Implementierungen werden ausgeliefert: PhotonTheme (die Voreinstellung) und
MaterialTheme (echtes Material 3, dynamische Farbe aus einem Saatwert eingeschlossen). Tauschen
Sie das Theme, und jede Komponente trägt die neue Marke, Server und Client, Web und Native. Das
Umschalten zwischen hell und dunkel zur Laufzeit ist ein Dienst (IThemeController); siehe
DesignSystem.
Ein Theme beantwortet auch, womit ein Diagramm Daten zeichnet: IAppTheme.Data ist eine
DataPalette: acht Serienfarben in fester Reihenfolge (Identität), eine sequenzielle Rampe eines
einzigen Farbtons (Größe), ein divergierendes Paar auf neutraler Mitte (Polarität), vier
reservierte Statusstufen (Zustand) und ein Grau zum Zurücknehmen. Sie ist auf eine validierte
Referenzpalette voreingestellt, sodass ein Theme, das nie an Diagramme gedacht hat, dennoch eine
korrekt bereitstellt; ein Theme mit Markenrampen überschreibt Data und hält das Ergebnis an dasselbe
Audit, PaletteAudit (OKLab-Helligkeit und -Chroma, Trennung für Farbsehen, Kontrast), und das ist,
was eine Palette sicher änderbar macht. Eine neunte Serie wird mit Namen abgelehnt, nie mit einem
generierten Farbton versehen. Der Plan dahinter ist docs/CHARTS-PLAN.md im Repository.
Ein Token INVERTIERT mit dem Modus, und das ist sein ganzer Sinn, und darum sollte fast alles eines sein. Die Ausnahme ist also alles, dessen Bedeutung nicht invertiert.
Der Fall, der das gefunden hat: ein Glanzlicht auf einer Blase, mit TextInverse gemalt, weil das im
hellen Modus weiß ist und ein Glanzlicht weiß ist. Im dunklen Modus ist dasselbe Token marineblau,
also zeichnete die Anwendung genau dort einen dunklen Fleck, wo eine Spiegelung hingehört. Licht
ist ein ausdrückliches Weiß, nie ein Token: Eine Spiegelung ist Licht und nicht Vordergrund, und
Licht wird nicht dunkel, wenn die Oberfläche es wird. Dieselbe Begründung deckt einen Schatten, der
dunkel bleiben muss, und eine Markenfarbe, die ein Unternehmen nach Sonnenuntergang nicht umstylt.
Seit 0.2.0-preview.13
new BoxStyle
{
BorderWidth = 1,
BorderColor = theme.Border,
BorderSides = BorderSides.Top, // eine Linie über einem Abschnitt
}
new BoxStyle
{
BorderWidth = 3,
BorderColor = theme.Colors(Variant.Primary).Base,
BorderSides = BorderSides.Start, // ein Akzentbalken an einem Hinweiskasten
}BorderSides.All ist die Voreinstellung, also rendert jede Box, die davor geschrieben wurde, Byte
für Byte gleich: Ein voller Rand gibt weiterhin die Ein-Deklarations-Kurzform und ihre eine atomare
Klasse aus.
Ein Rand liegt innerhalb der Box, und das Kind auch: Die Box hält ihr Kind innerhalb ihres Paddings und ihres Randes an den Seiten, an denen der Rand gezeichnet wird, und eine Box, die ihr Kind umschließt, zählt beides in ihre Größe, auf beiden Zielen (Photon legte das Kind bis 0.2.0-preview.61 über den Rand).
Der Rand eines Zustands folgt denselben Kanten: Ein Hover mit BorderWidth = 2 an einer Box, die
ihre Unterkante zeichnet, verdickt nur diese Kante, in der Farbe der Basis, wenn der Zustand keine
nennt. Das Web zeichnete ihn an allen vier Kanten und schrieb für eine Breite allein nichts
(seit 0.2.0-preview.61).
Keines davon ist ein Divider. Ein Divider ist ein Geschwister zwischen zwei Dingen; diese
gehören der Box selbst, und die einzige Art, einen zu zeichnen, war eine Row, die eine Ein-dp-Box
umhüllt, und das ist eine Layout-Lüge über das, was das Design meinte.
Start und End statt Left und Right, wie bei EdgeInsets: Sie spiegeln in einer
Rechts-nach-links-Lesung, und ein Akzentbalken, der links bleibt, wenn der Text in die andere
Richtung fließt, ist auf der falschen Seite davon.
Zaun. Mit einem Eckenradius unterscheidet sich ein teilweiser Rand zwischen den Zielen leicht an der Ecke, an der eine vorhandene Kante eine fehlende trifft: Das Web stößt sie auf Gehrung, Photon stößt sie rechtwinklig. Bei Radius 0 (eine Linie, ein Akzentbalken, eine Tabellenzelle, und dafür sind teilweise Ränder da) sind die beiden identisch. Nutzen Sie
BorderSides.Allfür eine gerundete Umrandung.
Die Web-Schicht auf niedriger Ebene (eQuantic.UI.Web) spiegelt das DOM eins zu eins. Jedes
HtmlElement legt die Attribute offen, die der Browser hat:
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; }
}Dieser Kontrakt bedeutet, dass die Web-Schicht kein CSS-Framework erzwingt, sie rendert
HTML-Attribute. DynamicElement geht weiter und rendert jedes Tag mit jedem Attribut. Gewöhnliche
CSS-Klassen aus jedem Stylesheet funktionieren nativ:
new DynamicElement("aside") { ClassName = "my-sidebar" }Das Framework liefert genau eine Style-Engine aus, die typisierte C#-Pipeline von oben. Es gibt
kein mitgeliefertes CSS-Framework und keine Adapterschicht: Eine Seite, die handgeschriebenes Markup
braucht, nutzt die Notluke (HtmlElement/DynamicElement + ClassName), und jedes Stylesheet, das
die Anwendung mitbringt (ihre eigene CSS-Datei, ein Utility-Framework, das sie selbst baut), wird
referenziert wie in jedem Webprojekt:
builder.Services.AddUI(options =>
{
options.ConfigureHtmlShell(shell => shell.AddStylesheet("/css/app.css"));
});Das Framework erzeugt dieses CSS nicht und verarbeitet es nicht; es ist Sache des eigenen Builds der Anwendung.
| Paket | Zweck |
|---|---|
| eQuantic.UI.Primitives | Typisierte Styles, Design-Tokens, das abstrakte Vokabular: die Quelle der Wahrheit für das Styling. |
| eQuantic.UI.Web | Der Web-Realisierer + die atomare Style-Engine (StyleAtomizer) + das generierte Stylesheet, und die DOM-spiegelnde Notluke: HtmlElement, ClassName, HtmlStyle, DynamicElement. |
🏁 Hier starten
🏗️ Architektur
- Architekturüberblick
- Write-once-Komponenten
- Deklarative Oberfläche
- Paketarchitektur
- Komponenten
- Styling
- Lokalisierung
- Analytics und GTM
📱 Write-once
- Photon-Engine
- Design System
- Fähigkeiten
- Speicher
- Formulare
- Code-Editor
- Markdown
- Mermaid
- E-Mail-Rendering
⚙️ Kompilierung
⚡ Runtime
🔌 Server
🎨 Ökosystem
🚀 Entwicklung