Badges, Icons & Status

Beyond text and buttons, the framework ships a set of small display controls for status, decoration, and spacing. Each is a typed control — never fall back to hand-built HTML for these.


Badge

BadgeControl renders a small status pill. Appearance accepts the Fluent values "Accent", "Neutral", and "Lightweight"; custom colors go through BackgroundColor + Color.

Controls.Stack
    .WithOrientation(Orientation.Horizontal)
    .WithHorizontalGap("8px")
    .WithView(Controls.Badge("Released").WithAppearance("Accent"))
    .WithView(Controls.Badge("Draft").WithAppearance("Neutral"))
    .WithView(Controls.Badge("Deprecated")
        .WithBackgroundColor("var(--error)")
        .WithColor("white"))

Icon

IconControl renders any icon from the FluentIcons catalog (in MeshWeaver.Application.Styles). Icons theme with currentColor; pass an explicit size with IconSize from MeshWeaver.Domain (e.g. FluentIcons.Star(IconSize.Size16)), or take the 24px default.

Controls.Stack
    .WithOrientation(Orientation.Horizontal)
    .WithHorizontalGap("12px")
    .WithView(Controls.Icon(FluentIcons.CheckmarkCircle())
        .WithStyle("color: var(--success, green);"))
    .WithView(Controls.Icon(FluentIcons.Warning())
        .WithStyle("color: var(--warning, orange);"))
    .WithView(Controls.Icon(FluentIcons.Info())
        .WithStyle("color: var(--accent-fill-rest);"))
    .WithView(Controls.Icon(FluentIcons.Star()))

Progress

ProgressControl shows a message with a percentage bar — the standard way to surface long-running work (imports, compiles, exports) in a layout area. In real use the percentage comes from an observable, so the bar advances as the operation reports progress.

Controls.Stack
    .WithView(Controls.Progress("Importing nodes…", 65))
    .WithView(Controls.Progress("Building search index…", 30))

Spacer

SpacerControl fills the remaining space in a horizontal stack — the idiomatic way to push content to opposite edges without CSS.

Controls.Stack
    .WithOrientation(Orientation.Horizontal)
    .WithWidth("100%")
    .WithView(Controls.Label("Quarterly Report 2026"))
    .WithView(Controls.Spacer)
    .WithView(Controls.Badge("Final").WithAppearance("Accent"))

Menu Item

MenuItemControl is a titled, icon-carrying dropdown trigger. On its own it renders the trigger button; add child views for the dropdown content. It backs the node menus described in Node Menu.

Controls.MenuItem("Export", FluentIcons.ArrowDownload())
    .WithView(Controls.Label("As PDF"))
    .WithView(Controls.Label("As Word"))

Card Skin

Any control becomes a card by attaching Skins.Card — a bordered, elevated surface. Skins compose: the control keeps its own behavior, the skin only wraps its presentation.

Controls.Stack
    .WithView(Controls.H4("Funding Ratio"))
    .WithView(Controls.Markdown("**112.4%** — up from 109.8% last year"))
    .WithView(Controls.Badge("BVV2 Art. 44").WithAppearance("Neutral"))
    .AddSkin(Skins.Card.WithWidth("320px"))

QR Code, Share Link & Install

Three controls for a page a person carries on their phone — first used by the action-link phone card (MeshWeaver.Plugins#2062). All three take a literal or a data-binding pointer for every value, so a template emits them at once and the values arrive bound.

QrCodeControl draws a QR code from a module matrix the SERVER computed: Controls.QrCodeOf(text) encodes on the spot, and QrCodes.Encode(text) returns the matrix (rows of '1'/'0') for a projection to bind (Controls.QrCode(pointer)). Every renderer — Blazor, React, React Native — only paints squares, so none needs a QR library and no third-party service sees the text. It is always black on white with the standard four-module quiet zone, in the dark theme too (cameras need the contrast), and nothing is drawn over it. Size is the rendered width in CSS pixels (default 240), Label the accessible name; QrCodes.Encode takes a QrErrorCorrection level (default Medium) and refuses one it does not know by name. An empty matrix draws nothing, so clearing the bound value removes the code. 🚨 Everything encoded is readable by any camera that sees the screen: encode a public address, never a credential or a personal detail, and always show the same address as text beside it.

ShareLinkControl is Copy link and Share for Url. Copy writes to the clipboard inside the click (Safari refuses a write that has left the gesture). Share opens the device's share sheet and is offered only where the browser has one; elsewhere Copy stands alone. The outcome is announced in a polite live region. An empty Url disables both.

InstallAppControl is Install on iPhone / Android for the page it is on. It adds nothing by itself. Opened, it guides iPhone and iPad through Share → Add to Home Screen (Safari has no programmatic prompt). On Android it raises the browser's own install prompt when the browser offered one, and shows the browser-menu steps when it did not. An in-app browser is told to open the page in Safari or Chrome first, a page already opened from the home screen says so, and a desktop browser shows the address to open on the phone. The detected platform can be switched by hand. While it is rendered, the page's manifest is ManifestHref, so each page installs under its own identity and scope. The previous manifest comes back when the control goes away.

Controls.Stack
    .WithView(Controls.QrCode(new JsonPointerReference("qrModules")).WithLabel("Invitation"))  // QrCodes.Encode(url)
    .WithView(Controls.Body(new JsonPointerReference("url")))          // the address, readable
    .WithView(Controls.ShareLink(new JsonPointerReference("url")).WithTitle("Welcome"))
    .WithView(Controls.InstallApp("/a/0123/manifest.webmanifest").WithAppName("Course card"))

The labels and install steps are the platform's own (qr.*, share.*, install.* in the string catalog), in the viewer's language.


See Also

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.