Repository navigation
Icons
🌐 This page in: English · Português
eQuantic.UI provides a comprehensive and extensible icon ecosystem. It supports multiple popular icon sets out of the box, all integrated through a unified component model.
To use icons in your project, install the desired icon set package and the core components package.
dotnet add package eQuantic.UI.Components
dotnet add package eQuantic.UI.LucideNothing to register. A pack is a catalog of IconGlyph: you name the glyph you want and the
compiler inlines that one:
using eQuantic.UI.Lucide;
Glyph(LucideIcons.Search)
Glyph(LucideIcons.Check, size: 20)Sizes are the §07 whitelist (16 / 20 / 24 / 32). For a size off that list, Vector(glyph, size)
draws the same paths without the icon contract, and Vector(glyph, size, height: h) gives a
non-square figure its own aspect (since 0.2.0-preview.28; a diagram's connector was the case). Icon(...) is the factory for the framework's own
curated Icons enum. See DeclarativeSurface for why the two have different
names.
Because you name the glyph, nothing resolves at run time and two packs can never disagree about a name: there is no lookup to lose.
| Package | Description |
|---|---|
eQuantic.UI.Lucide |
Beautiful & consistent icons (default set). |
eQuantic.UI.Heroicons |
Beautiful hand-crafted SVG icons by the makers of Tailwind CSS. |
eQuantic.UI.RadixIcons |
A crisp set of 15×15 icons designed by the Radix UI team. |
eQuantic.UI.TablerIcons |
Over 4200 pixel-perfect icons for web design. |
eQuantic.UI.FontAwesome6.Solid |
The world's most popular icon set (Solid variant). |
eQuantic.UI.FontAwesome6.Regular |
Font Awesome 6 Regular icons. |
eQuantic.UI.FontAwesome6.Brands |
Font Awesome 6 Brand icons. |
eQuantic.UI.Phosphor |
A flexible icon family for interfaces, diagrams, and more. |
eQuantic.UI.SimpleIcons |
Over 3000 SVG icons for popular brands. |
eQuantic.UI.BootstrapIcons |
Free, high quality, open source icon library with 2,000+ icons. |
eQuantic.UI.Iconoir |
A high-quality set of 1500+ open-source icons. |
eQuantic.UI.MaterialSymbols |
Google's Material Symbols: 16,284 glyphs, the largest pack here. |
An icon package is a catalog of IconGlyph: target-neutral path data, so the same entry serves
the web realizer as inline SVG and the native atlas as glyph geometry. Draw one with Glyph:
using eQuantic.UI.MaterialSymbols;
Glyph(MaterialSymbolsIcons.PlayArrowRounded)
Glyph(MaterialSymbolsIcons.ExpandMore, size: 20)Glyph, not Icon: Icon is the factory for the framework's own curated Icons enum, and the
declarative surface has no overloads. See DeclarativeSurface. Sizes are the
§07 whitelist (16 / 20 / 24 / 32); for anything off it, Vector(glyph, size) draws the same paths
without the icon contract, and a height: argument frees the box from the square.
You pay for what you name. eqc inlines only the glyphs a page actually references: a page using two Material Symbols glyphs emits a 627-byte module, out of a catalog whose source is ~8 MB. Pack size is a build-time cost, not a download.
Since 0.2.0-preview.7
Google ships Material Symbols as a variable font with FILL, wght, GRAD and opsz axes,
and the three cuts (outlined, rounded, sharp) are what those axes resolve to. A GPU display list
cannot draw a font, so this package carries the resolved paths instead: the cut lives in the icon's
name (Home, HomeRounded, HomeSharp) and there is no FILL axis, so X and XOutline
are two entries. Names are the Iconify ones in PascalCase, so material-symbols:play-arrow-rounded
is PlayArrowRounded.
It is also the heaviest pack the framework publishes (~13 MB of assembly against Phosphor's 8) — which used to be advice about which pack to choose, and no longer is. See below.
A published app carries the icons it USES, not the pack it referenced. Measured on the biggest one: the pack assembly goes from 14,558,720 bytes to 7,680, and a publish that carried it from 35,256 KB to 20,952 KB — 41% smaller, for an app drawing a handful of glyphs out of 16,284.
Nothing to configure and nothing to name. What changed is the SHAPE the packs are generated in: a field initializer compiles into one static constructor for the whole class, and a static constructor is unremovable — so a field pack shipped whole, all of it, to draw five glyphs. An expression-bodied property is a separate method body, and the IL trimmer drops the ones nothing calls:
// before: one static constructor, 16,284 glyphs, unremovable
public static readonly IconGlyph Camera = new("camera", "M14.5 4h-5L7 7", …);
// now: a method body per glyph, and the trimmer keeps the ones you name
public static IconGlyph Camera => new("camera", "M14.5 4h-5L7 7", …);It needs PublishTrimmed, which is what makes the trimmer run at all. Without it the pack is
the same size it always was, so "choose a smaller pack" is still the advice for an untrimmed
publish. With it, pack size stops being a reason to choose one set over another — pick the icons
that look right.
Since 0.2.0-preview.47
Since 0.2.0-preview.29
An icon is one path in one tint. A LOGO is several shapes, each in the colour its designer chose,
that is Drawing, and it is the node you point at a file.
Put the file in the app's Assets folder and it becomes a typed member:
// Assets/mark.svg → Vectors.Mark
column.Add(new Drawing(Vectors.Mark, width: 240, label: "eQuantic"));The reading happens at build time: nothing reads a file at runtime on either target, because a
browser cannot and a native app would be carrying its artwork twice. A typed member rather than a
path string, for the same reason the rest of this SDK generates surfaces: Vectors.Mark is a
compile error when it is wrong, and Drawing("mark.svg") is a blank space at runtime.
One number keeps the artwork's own aspect (an icon defaults to a square because that is what an icon is; a squashed logo is a wrong logo). Give both to letterbox or stretch deliberately.
At the width of its column. The width is a SizeValue, so SizeValue.Fill takes the width
the parent offers and the artwork's aspect decides the height: a map in a responsive column, which
no number says for every window. The web writes width: 100% and aspect-ratio; Photon resolves
the width as it resolves a canvas's and paints at the box it laid out. A height you give still wins.
A fill on a width the parent sizes from its own content has nothing to fill and is zero wide, as a
canvas is, and a drawing refuses SizeValue.Hug, since it has no content to hug.
column.Add(new Drawing(Vectors.Mark, SizeValue.Fill, label: "eQuantic"));Since 0.2.0-preview.61
Tinting. A shape the file left as currentColor is the artwork asking to be tinted, so pass
tint: and it follows, while every shape that chose its own colour ignores it. On the web the word
currentColor reaches the markup and the tint is set as CSS color, so the cascade can answer it;
on Photon each shape is rasterized and painted in its own colour.
new Drawing(Vectors.Mark, 240, tint: theme.TextPrimary) // the monochrome half follows the themeThe subset, named the way the Markdown one is. Understood: viewBox, path, rect (with
rx/ry), circle, ellipse, line, polyline, polygon, nested g, transform
(translate / scale / rotate / skew / matrix, baked into the path data rather than carried),
fill, stroke, stroke-width, fill-rule, the three opacities, the same properties written in
a style attribute, and colours as #rgb / #rrggbb / #rrggbbaa / rgb() / rgba() / none /
currentColor / the basic names. Dropped, deliberately: patterns, clipPath, mask, filter,
use, image and text: each needs a paint server this model has no room for, or a font. Those
shapes arrive MISSING rather than filled with a colour nobody chose, and the build says which file
it skipped.
Since 0.2.0-preview.31
linearGradient and radialGradient are understood, with their stops, in either units
(objectBoundingBox, the default, is a FRACTION of the shape's own box; userSpaceOnUse measures
on the viewBox grid), and href is followed for a palette two gradients share. The defs are read in
a pass of their own, so a fill may name a gradient the file declares after it, which is what half
the exporters in the world write.
Each target paints the run the way it natively can. On the web the artwork carries its own defs
and every shape names one with url(#…), so the browser interpolates at whatever size the box ends
up; the id is a hash of the gradient itself, so one run used by two shapes is declared once and the
server and the client twin land on the same string. On Photon the same alpha mask the rasterizer
already made is filled with the engine's own gradient paint, which is the path gradient TEXT
already used: no new draw command and no new shader entry point.
Two fences: a run crosses with two stops on Photon (the first and the last, because the engine's
paint interpolates between a pair; multi-stop is its own slice), and gradientTransform,
spreadMethod and a radial's focal point are dropped. A paint server the reader does not
understand still paints NOTHING, which is what every paint server did before this.
An icon is a VALUE, not a service. Icons.Heart is an IconGlyph — a name, a path, and whether it
is stroked or filled — and the Icon node draws it. The web realizer writes an inline <svg>; the
Photon realizer rasterizes the path once and caches it. There is no registry to consult and nothing
to register.
It was a provider interface once (IIconProvider in eQuantic.UI.Core, resolving names to
components through DI), and that went with the pre-write-once component model in
0.2.0-preview.48. A provider can only answer on the target it was written for, and an icon has to
be the same icon on three.
Two ways, both without a provider:
-
A
.svgfile inAssets/becomes aVectors.Markat build time — see Artwork from a.svgfile above. -
An
IconGlyphyou construct, when you already have the path data:
public static class MyIcons
{
// A property and not a field, deliberately: a field pack compiles into one static
// constructor and ships whole, which is what the trimming note above is about.
public static IconGlyph Logo => new("logo", "M12 2L2 7l10 5 10-5-10-5z", IconGlyphStyle.Fill);
}
Icon(MyIcons.Logo, size: IconSize.Lg)🌐 English · Português
🏁 Start here
🏗️ Architecture
- Architecture Overview
- Write-Once Components
- Declarative Surface
- Package Architecture
- Components
- Styling
- Localization
- Analytics & GTM
📱 Write-once
⚙️ Compilation
⚡ Runtime
🔌 Server
🎨 Ecosystem
🚀 Development