plugins/controls/skills/shiny-controls/SKILL.md
Generate UI for .NET MAUI (Shiny.Maui.Controls) and Blazor (Shiny.Blazor.Controls) - includes TableView with 14 cell types, TreeView with lazy loading, drag/drop reorder (above/below/into), and configurable expand/collapse icons, FloatingPanel/OverlayHost/ShinyContentPage (bottom/top overlay panels) with detents and header peek, ShinyDurationPicker (duration picker with FloatingPanel), FrostedGlassView (native blur/glass effect), Toast service (code-invoked toast notifications with queue/stack, auto-dismiss, spinner, progress bar, pill/fill modes), PillView status badges, BadgeView (content-wrapping corner badge with text/dot/count overflow and pulse), ImageViewer with pinch/pan/double-tap zoom, ImageEditor with crop/rotate/draw/text/undo/redo/export, ChatView with bubbles/typing/load-more/input-bar and custom MessageTemplate/MessageTemplateSelector for per-message rendering, SecurityPin entry, Fab and FabMenu (floating action button and expanding action menu), Scheduler views (calendar grid, agenda timeline, event list), Markdown controls (MarkdownView renderer, MarkdownEditor with toolbar), Barcodes & QR codes (separate Shiny.Maui.Controls.Barcodes / Shiny.Blazor.Controls.Barcodes packages — BarcodeView and QRCodeView with 13 symbologies including QR, Aztec, Data Matrix, PDF417, Code 128/39/93, Codabar, EAN-8/13, UPC-A/E, ITF — pure-managed ZXing.Net renderer, PNG output via built-in encoder on MAUI, SVG or PNG data-URI on Blazor, with a static BarcodeRenderer for raw bytes / SVG / data-URI from code), AutoCompleteEntry with debounced search and dropdown suggestions, CountryPicker with flag/dial code, AddressEntry with geocoding, SignaturePad for capturing signatures with canvas drawing and PNG export, TextEntry with animated floating placeholder/customizable border/tool slots/validation hints/character count, Slider with two-color gradient track and blended thumb, ProgressBar with gradient fill and Vista-style shimmer pulse sweep, ParallaxCollectionView (MAUI) / ParallaxList (Blazor) — a scrollable list with a hero header that translates at a configurable fraction of the scroll offset, with optional collapse-to-sticky and fade, Overlay/LoadingOverlay (full-screen overlay with configurable color/opacity, custom content template, and built-in loading mode with indeterminate spinner or determinate progress bar), SkeletonView (content-wrapping skeleton loader that shows animated shimmer placeholders while IsBusy is true, with built-in line placeholders or a custom placeholder template), Desktop add-on (separate Shiny.Maui.Controls.Desktop package) combining Tray Icon (cross-platform system tray / status-bar icon with context menus, click events, tooltips, and dynamic visibility on Windows/macOS AppKit/MacCatalyst/Linux), Docking (Visual-Studio-style dockable tool windows, tabbed groups, splitters, auto-hide rails, and tear-off floating windows), and an On-Screen Keyboard (touch / kiosk QWERTY soft keyboard with shift / numbers / symbols layers, bottom-docked auto-show on focus, dispatches into focused inputs without stealing focus, full a11y tree for switch input); companion Shiny.Blazor.Controls.Kiosk for Blazor packages docking + on-screen keyboard under one kiosk-shaped Blazor add-on, Feedback Service (extensible IFeedbackService with haptic default, replaceable with TTS/sound/analytics), and UseFeedback support across all interactive controls
npx skillsauth add shinyorg/skills shiny-controlsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in the Shiny Controls library, which ships a single shared control surface across two hosts:
Shiny.Maui.Controls (plus Shiny.Maui.Controls.Markdown, Shiny.Maui.Controls.MermaidDiagrams)Shiny.Blazor.Controls (plus Shiny.Blazor.Controls.Markdown, Shiny.Blazor.Controls.MermaidDiagrams)Every control below is available on both MAUI and Blazor. The feature set (properties, events, behavior) is intentionally mirrored — the same concepts apply on either host; only the syntax differs (XAML + BindableProperty on MAUI, Razor markup + [Parameter] on Blazor).
The library contains:
ChildrenLoader for per-node async, RootLoader for async root), ChildrenSelector for sync data, HasChildrenSelector/CanExpandSelector/CanSelectSelector predicates, configurable ExpandedIcon/CollapsedIcon/RetryIcon (ImageSource on MAUI, RenderFragment slots on Blazor), single/multi selection with two-way SelectedItem/SelectedItems, events + ICommand mirrors for ItemSelected/ItemExpanded/ItemCollapsed/LoadFailed/ItemDropped, indent + guide lines, drag/drop reorder with above/below/into drop positions and visual drop indicators (event-only — never mutates your data; native HTML5 drag via JS interop on Blazor for Safari/Firefox support, pan-gesture fallback on Catalyst/AppKit/GTK4), programmatic API (ExpandAll/ExpandAllAsync/CollapseAll/Expand/Collapse/Refresh/ReloadAsync with state preservation/FindNode), and keyboard navigation on BlazorOverlayHost (manual Grid setup) or ShinyContentPage (convenience ContentPage with built-in overlay). Blazor uses SheetView with CSS-based overlays insteadTopLeft/TopRight/BottomLeft/BottomRight) of a wrapped view. Setting Text to an empty string auto-hides the badge — bind your unread/count value directly. Supports configurable BadgeColor/BadgeTextColor/BadgeBorderColor/BadgeBorderThickness, IsDot mode for simple notification indicators, MaxCount numeric overflow rendering ("99+"), per-corner OffsetX/OffsetY nudge (default hangs the badge slightly outside the corner), scale-in/out animation (IsAnimated), and optional continuous IsPulsing to draw attention. Blazor honors prefers-reduced-motionFabMenuItem stack and two-way IsOpenDaysToShow 1–7) with overlap detection, switchable date picker modes (DatePickerMode: Carousel / Calendar / None), additional timezone columns, auto-updating current time marker, and 12/24-hour time — full feature parity on MAUI and BlazorStickyDayHeaders, default true, pins the current day header while scrolling)Shiny.Maui.Controls.Barcodes / Shiny.Blazor.Controls.Barcodes packages): Pure-managed barcode rendering powered by ZXing.Net. Supports 13 symbologies (QRCode, Aztec, DataMatrix, Pdf417, Code128, Code39, Code93, Codabar, Ean8, Ean13, UpcA, UpcE, Itf). MAUI renders to PNG via a built-in pure-managed encoder (no SkiaSharp / System.Drawing dependency, AOT-safe) and feeds an Image. Blazor renders inline SVG by default (crisp at any size, single-path output with shape-rendering="crispEdges") or a PNG data: URI. QRCodeView is a BarcodeView subclass that locks Format = QRCode and adds Size (square edge length) and ErrorCorrection (Low/Medium/Quartile/High). The static BarcodeRenderer exposes RenderPng, RenderSvg, and RenderDataUri for raw output without a view. XAML namespace xmlns:bc="http://shiny.net/maui/barcodes"OverlayHost or ShinyContentPage (MAUI). The Sign button is disabled until the user draws somethingIToaster (registered by UseShinyControls()). Supports auto-dismiss with configurable duration, manual dismiss via IDisposable, pill or fill-horizontal display modes, top/bottom positioning, queue or stack mode for multiple toasts, indeterminate spinner, countdown progress bar, icon, tap command, feedback, and screen reader announcement. No XAML or OverlayHost required — the overlay auto-attaches to the current page. Blazor uses IToastService with <ToastHost> componentPulseLength (width of sheen) and PulseSpeed (sweep duration). Triggers on value change or timed interval. Supports indeterminate mode and text overlayDataTemplate (MAUI) or RenderFragment (Blazor). LoadingOverlay extends it with built-in spinner (indeterminate) or progress bar (determinate) plus optional message textRefreshView) that shows animated shimmer placeholders while IsBusy is true, then reveals the real content when loading finishes. Built-in line placeholders (configurable ItemCount/ItemHeight/ItemSpacing/CornerRadius/BaseColor/ShimmerColor) or a custom placeholder layout via SkeletonTemplate (MAUI) / SkeletonContent (Blazor). Shimmer is a sweeping LinearGradientBrush band on MAUI and an animated CSS gradient (honoring prefers-reduced-motion) on Blazor. Use it for inline content regions; use LoadingOverlay for whole-page loadingSnapCount (0=free scroll, 1+=snap to item). Uses native recycler views on MAUI and CSS scroll-snap on BlazorParallaxFactor, default 0.5 = half speed). Optional CollapseToSticky clamps the header to a MinHeaderHeight minimum, and FadeHeaderOnScroll fades it out as it scrolls. MAUI wraps a real CollectionView (ItemTemplate, EmptyView, SelectionMode, SelectedItem, ScrollTo, custom ItemsLayout) in a Grid and drives the hero translation from CollectionView.Scrolled. Blazor uses a CSS-positioned hero plus a tiny JS scroll listener that mutates transform/opacity directly (rAF-throttled) so parallax runs at native scroll framerate without Razor re-renders. Both hosts fire a Scrolled event with ParallaxScrollEventArgs(verticalOffset, headerTranslation, headerVisibleHeight) for driving sticky titles, fading nav chrome, etc. No platform handlersShiny.Maui.Controls.Desktop bundles two features that share the desktop TFM matrix (Windows + macOS AppKit + MacCatalyst + Linux):
using Shiny.Maui.Controls.Desktop.TrayIcon;) — cross-platform system tray / status-bar icon. Windows (Shell_NotifyIcon), macOS AppKit (NSStatusItem, native net10.0-macos build), MacCatalyst (AppKit bridged via the Objective-C runtime), Linux (libayatana-appindicator3 + GTK 3 — requires the system library installed). API: ITrayIconFactory resolved from DI, then ITrayIcon with SetIcon(Func<Stream>) (PNG or ICO bytes — Windows auto-wraps PNG into ICO), Tooltip, Title (macOS/Linux label, ignored on Windows), IsVisible, IsTemplateImage (macOS auto-tint), SetMenu(TrayMenu), ShowMenu(), and PrimaryClick/SecondaryClick/DoubleClick events. Menus are built fluently with TrayMenu.Build(b => b.Item(...).Check(...).Separator().Submenu(...)) — TrayMenuItem, TrayCheckMenuItem, TraySeparator, and TraySubmenu. Mutating any item's Label/IsEnabled/IsVisible rebuilds the native menu automatically. No Blazor equivalent — tray icons are a desktop OS concept. Registered with .UseTrayIcon() in MauiProgram.csusing Shiny.Maui.Controls.Desktop.Docking;) — Visual-Studio-style window docking for MAUI desktop, with a companion Shiny.Blazor.Controls.Kiosk package for Blazor (kiosk-shaped Blazor features — docking + on-screen keyboard; namespace Shiny.Blazor.Controls.Kiosk.Docking). DockHostView attaches to any existing ContentPage (not a ContentPage subclass) and orchestrates DockGroupView, DockTabStrip, and DockSplitter building blocks. Public surface includes IDockHost (per-window controller — LoadAsync, Snapshot, ShowPanelAsync/HidePanelAsync/ActivatePanelAsync, ResetLayoutAsync, SetRailCollapsedAsync, IsLocked, Events; implemented directly by DockHostView / <DockHost>), IDockableContent (optional interface on panel views — per-instance Title/Icon, CanClose/CanFloat, OnActivated/OnDeactivated, WantsPointerDown for embedded editors), IDockableContentFactory (async Task<View> CreateAsync(string instanceId, ...) + DisplayName/Icon, registered with .AddDockPanel<TView>("panel-id", displayName: …, icon: …)), IDockLayoutStore (bring-your-own persistence — no default ships; attach via the host's LayoutStore property for auto-load at startup + debounced auto-save), IDockLayoutMigrator (forward-only schema migrations), IDockEvents (LayoutChanged, PanelActivated, DragStarted/Completed/Cancelled), and IDockCommandScope (scopes Ctrl+W, Ctrl+Tab MRU, Ctrl+Alt+PgUp/Dn to the dock surface). The layout schema is a pure POCO tree (DockRoot, DockWindowState, DockSplit, DockGroup, DockTab, DockEmpty, DockCollapsedPanel) with a source-generated System.Text.Json context and SchemaVersion + MinReadableVersion for migration; DockSerialization.Serialize/Deserialize round-trip layouts to JSON. Fully interactive: tab drag to merge (drop center) / split (drop edge) / reorder (drop in tab strip) / tear off floating windows (drop outside the host — movable, resizable, re-dockable), draggable splitters with persisted clamped ratios, per-panel collapse to slim edge bars (restore on click; whole rails via SetRailCollapsedAsync), locked/read-only mode, and persisted floating-window bounds + collapsed state. Registered with .UseShinyDocking() + .AddDockPanel<TView>("id") on MAUI, services.AddShinyDocking() + .AddDockPanel<TComponent>("id") on Blazor; host controls accept InitialLayout, LayoutStore, and IsLockedusing Shiny.Maui.Controls.Desktop.OnScreenKeyboard; / using Shiny.Blazor.Controls.Kiosk.OnScreenKeyboard;) — Touch / kiosk soft keyboard. US-QWERTY with three layers (lowercase / Shift / 123-symbols), bottom-docked, auto-shows when an Entry / Editor (MAUI) or <input> / <textarea> (Blazor) gains focus. Critically does NOT steal focus when keys are tapped — every key uses pointerdown + preventDefault() (Blazor) or Focusable = false + intercepted PointerPressed (MAUI). Press-and-hold autorepeat (400ms delay, 50ms interval, configurable). Dispatches via managed Text mutation at CursorPosition (MAUI) or document.execCommand('insertText', char) after focus restore (Blazor) — no native synthetic key events in v0.1, so no macOS Accessibility entitlement and Mac App Store distribution works. Full AutomationPeer (Windows / MAUI / GTK) and ARIA role="button" + aria-keyshortcuts (Blazor) for switch-input accessibility. Theme tokens mirror docking — OnScreenKeyboardKeyBrush / --shiny-osk-key-bg etc. Public surface: IOnScreenKeyboard (MAUI) / IOnScreenKeyboardService (Blazor) with Show() / Hide() / Toggle() / IsVisible / VisibilityChanged, plus OnScreenKeyboardOptions for auto-show-on-focus, push-content vs overlay, height, theme, and autorepeat timing. Register with .UseOnScreenKeyboard(opts => ...) on MAUI or services.AddShinyOnScreenKeyboard(opts => ...) + place <OnScreenKeyboardHost /> once in MainLayout.razor on Blazor. v0.1 is planned but not yet implemented; limitations: MAUI inputs / DOM inputs only (no system-wide injection — opt-in pluggable IKeyDispatcher arrives in v0.4), no Shadow DOM, no IME / dead-key composition (v0.5), no language switching (v0.3 via JSON layout files)IFeedbackService. Default HapticFeedbackService provides tactile feedback. Replace with SetCustomFeedback<T>() in UseShinyControls() for TTS, sounds, analytics, or custom responses. The control parameter is the actual control instance (use pattern matching like control is ChatView), and args carries context — ChatMessage for ChatView events, native EventArgs for standard MAUI controls. Standard MAUI control integration is pluggable and AOT-compatible via MauiControlFeedbackBuilder — use AddDefaultMauiControlFeedback() for all built-in hooks, add custom hooks with Hook<TControl>(eventName, subscribe, unsubscribe), or use AddMauiControlFeedback() for only the hooks you configureInvoke this skill when the user wants to:
CanSelect/CanExpand predicatesdata: URIs from code without a view (e.g., for PDF export, file save, email attachment)libayatana-appindicator) for tray icons on GNOME/KDEDockHostView for MAUI / <DockHost /> for Blazor) to an existing page without subclassing ContentPageAddDockPanel<TView>("solution-explorer")) so layout JSON can resolve them back to actual viewsIDockLayoutMigratorIDockLayoutStore (e.g. backed by Shiny.Stores, a file, or a remote service) to save/load DockRoot snapshotsIDockEvents for telemetry or undo-stack integrationIDockHost.IsLocked = trueEntry/Editor/<input>/<textarea> gains focus and types into it without stealing focusIOnScreenKeyboard.Show()/Hide()/Toggle()PushContent option)osk.exe / TabTip) — Shiny's OSK is an in-app control, not a wrapper around the OS oneNuGet: Shiny.Maui.Controls (+ Shiny.Maui.Controls.Markdown, Shiny.Maui.Controls.MermaidDiagrams, Shiny.Maui.Controls.Barcodes, Shiny.Maui.Controls.Desktop for tray icon + docking)
Namespace: Shiny.Maui.Controls
XAML Namespace: http://shiny.net/maui/controls (prefix: shiny)
Desktop add-on namespaces: Shiny.Maui.Controls.Desktop.TrayIcon, Shiny.Maui.Controls.Desktop.Docking, Shiny.Maui.Controls.Desktop.OnScreenKeyboard (extension methods UseTrayIcon, UseShinyDocking, AddDockPanel<T>, UseOnScreenKeyboard live in Shiny)
NuGet: Shiny.Blazor.Controls (+ Shiny.Blazor.Controls.Markdown, Shiny.Blazor.Controls.MermaidDiagrams, Shiny.Blazor.Controls.Barcodes, Shiny.Blazor.Controls.Kiosk for the Blazor docking host + on-screen keyboard)
Namespaces: Shiny.Blazor.Controls, Shiny.Blazor.Controls.Cells, Shiny.Blazor.Controls.Sections, Shiny.Blazor.Controls.Scheduler, Shiny.Blazor.Controls.Chat, Shiny.Blazor.Controls.Markdown, Shiny.Blazor.Controls.MermaidDiagrams, Shiny.Blazor.Controls.Kiosk.Docking, Shiny.Blazor.Controls.Kiosk.OnScreenKeyboard
Install the NuGet package
dotnet add package Shiny.Maui.Controls
Configure in MauiProgram.cs
using Shiny;
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseShinyControls();
Add the XAML namespace to your pages
xmlns:shiny="http://shiny.net/maui/controls"
Install the NuGet package
dotnet add package Shiny.Blazor.Controls
Add @using directives (typically in _Imports.razor)
@using Shiny.Blazor.Controls
@using Shiny.Blazor.Controls.Cells
@using Shiny.Blazor.Controls.Sections
@using Shiny.Blazor.Controls.Scheduler
@using Shiny.Blazor.Controls.Chat
@using Shiny.Blazor.Controls.Markdown
@using Shiny.Blazor.Controls.MermaidDiagrams
No DI registration is required for Blazor — components are used directly in .razor pages.
All controls exist on both hosts, but the Blazor surface is idiomatic Razor, not a 1:1 XAML port. When generating Blazor code, translate with these rules:
| MAUI (XAML) | Blazor (Razor) | Notes |
|-------------------------|-------------------|-------------------------------------------------|
| shiny:TableView | <TableView> | No prefix on Blazor; TableRoot is not needed |
| shiny:TableRoot | (omitted) | Sections go directly inside <TableView> |
| shiny:TreeView | <TreeView TItem="…"> | Strongly typed on Blazor; ExpandedIcon/CollapsedIcon/RetryIcon are RenderFragment slots, not ImageSource; Blazor adds keyboard navigation (↑/↓/←/→/Enter/Home/End) and a <LoadingTemplate> slot for the root-load spinner |
| shiny:TableSection | <TableSection> | |
| shiny:PillView | <Pill> | Renamed to just Pill on Blazor |
| shiny:BadgeView | <BadgeView> | Wraps Content (MAUI) / ChildContent (Blazor). Colors are CSS strings on Blazor; Position is the BadgePosition enum on both hosts; empty Text auto-hides unless IsDot=true |
| shiny:FloatingPanel in shiny:OverlayHost | <SheetView> | MAUI uses FloatingPanel+OverlayHost; Blazor uses SheetView with CSS overlay. Content goes in <SheetContent> named slot on Blazor |
| shiny:Fab | <Fab> | Icon takes inline SVG/text string, not ImageSource |
| shiny:FabMenu | <FabMenu> | Items passed via Items parameter (List<FabMenuItem>), not as children |
| shiny:ImageViewer | <ImageViewer> | Source is a URL string |
| shiny:ImageEditor | <ImageEditor> | Source is byte[] (MAUI) or URL string + ImageData byte[] (Blazor); colors are CSS strings on Blazor |
| shiny:ChatView | <ChatView> | Colors are CSS strings on Blazor; SendCommand is EventCallback<string> on Blazor; uses @using Shiny.Blazor.Controls.Chat |
| shiny:SecurityPin | <SecurityPin> | |
| md:MarkdownView | <MarkdownView> | |
| md:MarkdownEditor | <MarkdownEditor>| |
| diagram:MermaidDiagramControl | <MermaidDiagramControl> | |
| shiny:AutoCompleteEntry | <AutoCompleteEntry> | Colors are CSS strings; SearchCommand is EventCallback<string>; supports CssClass, InputClass, DropDownClass, and AdditionalAttributes on Blazor |
| shiny:CountryPicker | <CountryPicker> | Colors are CSS strings on Blazor |
| shiny:AddressEntry | <AddressEntry> | Colors are CSS strings on Blazor; uses IAddressSearchProvider on both hosts |
| Scheduler views | <SchedulerCalendarView>, <SchedulerAgendaView>, <SchedulerCalendarListView> | Same names |
| IToaster.ShowAsync(text, cfg => {}) | IToastService.ShowAsync(text, cfg => {}) | MAUI uses DI-injected IToaster (registered by UseShinyControls()); Blazor uses DI-injected IToastService. Blazor requires AddShinyToast() in DI and <ToastHost /> in layout |
| shiny:TextEntry | <TextEntry> | Colors are CSS strings on Blazor; tools are List<TextEntryTool> on both hosts. MAUI uses ICommand, Blazor uses Action callback. Blazor TextEntryTool has Icon as string (not ImageSource) |
| MAUI | Blazor |
|------------------------------------------------------|--------------------------------------------------|
| IsOpen="{Binding IsOpen, Mode=TwoWay}" | @bind-IsOpen="isOpen" |
| Value="{Binding Pin, Mode=TwoWay}" | @bind-Value="pin" |
| Command="{Binding AddCommand}" | Clicked="OnAdd" / OnClick="OnAdd" (event callback) |
| FontAttributes="Bold" (PillView) | Bold="true" |
| Color="DodgerBlue" (MAUI Color) | Color="#1E90FF" (CSS color strings) |
| ItemsSource + ItemTemplate (DataTemplate) | ItemsSource + ItemTemplate (RenderFragment<object>) |
| <shiny:FloatingPanel> content is [ContentProperty] PanelContent | <SheetContent>…</SheetContent> named slot (Blazor SheetView) |
| <shiny:FabMenu><shiny:FabMenuItem /></shiny:FabMenu> | Items="List<FabMenuItem>" parameter |
"#RRGGBB", "rgb(...)", named colors) — there is no MAUI Color type on BlazorIcon on Fab/FabMenuItem is a string — pass an inline SVG, emoji, or single characterRenderFragment<object> is the Blazor equivalent of DataTemplate for ItemsSource/ItemTemplateCompleted="OnCompleted" where OnCompleted(SecurityPinCompletedEventArgs e)), not ICommandISchedulerEventProvider — the same interface and models work on both hostsTableView
<TableView CellAccentColor="#10B981">
<TableSection Title="Profile">
<LabelCell Title="Name" ValueText="Allan Ritchie" />
<LabelCell Title="Plan" ValueText="Pro" />
</TableSection>
<TableSection Title="Danger zone">
<ButtonCell Title="Delete account"
ButtonTextColor="#DC2626"
OnClick="@(() => deleted = true)" />
</TableSection>
</TableView>
SheetView (Blazor only — MAUI uses FloatingPanel+OverlayHost)
<button @onclick="() => isOpen = true">Open Sheet</button>
<SheetView @bind-IsOpen="isOpen"
Detents="detents"
SheetCornerRadius="20">
<SheetContent>
<h2>Hello from a sheet</h2>
<button @onclick="() => isOpen = false">Close</button>
</SheetContent>
</SheetView>
@code {
bool isOpen;
IList<DetentValue> detents = new List<DetentValue>
{
DetentValue.Quarter, DetentValue.Half, DetentValue.Full
};
}
Pill
<Pill Text="Success" Type="PillType.Success" />
<Pill Text="Brand" PillColor="#312E81" PillTextColor="#E0E7FF" />
<Pill Text="Bold" Type="PillType.Info" Bold="true" />
Fab / FabMenu
<Fab Icon="+" FabBackgroundColor="#EC4899" Clicked="OnAdd" />
<FabMenu Items="items"
Icon="+"
FabBackgroundColor="#7C3AED"
ItemTapped="OnItemTapped" />
@code {
readonly List<FabMenuItem> items = new()
{
new FabMenuItem { Text = "New Note", Icon = "📝", FabBackgroundColor = "#10B981", Tag = "note" },
new FabMenuItem { Text = "New Photo", Icon = "📷", FabBackgroundColor = "#F59E0B", Tag = "photo" }
};
void OnItemTapped(FabMenuItem item) { /* ... */ }
void OnAdd() { /* ... */ }
}
ImageViewer
<img src="@url" @onclick="() => Open(url)" />
<ImageViewer Source="@current" @bind-IsOpen="isOpen" MaxZoom="6" />
@code {
bool isOpen;
string? current;
void Open(string url) { current = url; isOpen = true; }
}
ImageEditor
<ImageEditor @ref="editor"
Source="@imageUrl"
ImageData="@imageData"
AllowCrop="true"
AllowDraw="true"
AllowRotate="true"
AllowTextAnnotation="true"
DrawStrokeColor="#ff0000"
DrawStrokeWidth="3"
CanUndoChanged="v => canUndo = v"
CanRedoChanged="v => canRedo = v" />
@code {
ImageEditor? editor;
string? imageUrl = "https://example.com/photo.jpg";
byte[]? imageData;
bool canUndo, canRedo;
async Task Export()
{
var bytes = await editor!.ExportAsync("png");
}
}
ChatView
@using Shiny.Blazor.Controls.Chat
<div style="height:600px;">
<ChatView Messages="messages"
Participants="participants"
IsMultiPerson="true"
TypingParticipants="typingParticipants"
SendCommand="OnSend"
AttachImageCommand="OnAttach"
LoadMoreCommand="OnLoadMore"
MyBubbleColor="#DCF8C6"
OtherBubbleColor="#FFFFFF" />
</div>
@code {
List<ChatMessage> messages = new();
List<ChatParticipant> participants = new();
List<ChatParticipant> typingParticipants = new();
Task OnSend(string text)
{
messages.Add(new ChatMessage { Text = text, SenderId = "me", IsFromMe = true });
StateHasChanged();
return Task.CompletedTask;
}
Task OnAttach() => Task.CompletedTask;
Task OnLoadMore() => Task.CompletedTask;
}
SecurityPin
<SecurityPin @bind-Value="pin"
Length="6"
HideCharacter="●"
Completed="OnCompleted" />
@code {
string pin = "";
void OnCompleted(SecurityPinCompletedEventArgs e) { /* verify e.Value */ }
}
Markdown
<MarkdownView Markdown="@content" />
<MarkdownEditor @bind-Markdown="content" Placeholder="Write markdown…" />
When generating code with Shiny.Maui.Controls:
xmlns:shiny="http://shiny.net/maui/controls" to the pagexmlns:md="http://shiny.net/maui/markdown" to the pageshiny:TableView > shiny:TableRoot > shiny:TableSectionshiny:ShinyContentPage as the page base class with PageContent for main content and Panels for FloatingPanels. Alternatively, place shiny:OverlayHost with shiny:FloatingPanel children inside a Grid. Supports Position="Bottom" (default), Position="Top", or Position="BottomTabs" (for use inside Shell TabBar)<SheetView> with <SheetContent> childshiny:ImageViewer inside a Grid that fills the page (it overlays on top, same pattern as SheetView)shiny:ImageEditor with Source bound to byte[] image data. Set AllowX properties to toggle features. Use CurrentToolMode (TwoWay) to control the active tool. Use CanUndo/CanRedo (OneWayToSource) to observe undo state. Call ExportAsync() to save.shiny:SchedulerCalendarView, shiny:SchedulerAgendaView, or shiny:SchedulerCalendarListView and bind Provider to an ISchedulerEventProvidermd:MarkdownView anywhere you need to render markdown contentmd:MarkdownEditor for editable markdown with toolbar and previewSwitchCell for on/off togglesCheckboxCell for accept/agree checkboxesSimpleCheckCell for selection lists (shows/hides checkmark)RadioCell for mutually exclusive choices within a sectionEntryCell for text inputCommandCell for navigation/action items with disclosure arrowButtonCell for destructive or primary actionsLabelCell for read-only displayDatePickerCell / TimePickerCell for date/time selectionTextPickerCell for dropdown selection from a listNumberPickerCell for numeric input with min/maxPickerCell for full-page single or multi-selectCustomCell for any custom MAUI viewMode=TwoWay for editable properties (On, Checked, ValueText, Date, Time, Number, SelectedIndex, SelectedItem, SelectedItems, IsOpen, IsViewerOpen, IsPreviewVisible)Mode=TwoWay for MarkdownEditor.Markdown (editor content)Mode=OneWay (default) for display-only properties (Title, Description, ValueText on LabelCell, Text on PillView, Source on ImageViewer, Markdown on MarkdownView)Mode=OneWayshiny:RadioCell.SelectedValue="{Binding Prop, Mode=TwoWay}"Use ShinyContentPage for the simplest setup:
<shiny:ShinyContentPage xmlns:shiny="http://shiny.net/maui/controls">
<shiny:ShinyContentPage.PageContent>
<ScrollView>
<VerticalStackLayout>
<Button Text="Open Panel" Command="{Binding OpenCommand}" />
</VerticalStackLayout>
</ScrollView>
</shiny:ShinyContentPage.PageContent>
<shiny:ShinyContentPage.Panels>
<shiny:FloatingPanel IsOpen="{Binding IsOpen, Mode=TwoWay}">
<Label Text="Panel content" />
</shiny:FloatingPanel>
</shiny:ShinyContentPage.Panels>
</shiny:ShinyContentPage>
Or use OverlayHost manually in a Grid:
<ContentPage>
<Grid>
<ScrollView><!-- page content --></ScrollView>
<shiny:OverlayHost>
<shiny:FloatingPanel IsOpen="{Binding IsOpen, Mode=TwoWay}">
<Label Text="Panel content" />
</shiny:FloatingPanel>
</shiny:OverlayHost>
</Grid>
</ContentPage>
null to inherit system defaults.Application.Current.UserAppTheme automatically.shiny:TableView for consistent appearanceCellAccentColor for switches, checkboxes, and radio buttons globally<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:shiny="http://shiny.net/maui/controls"
x:Class="MyApp.SettingsPage"
Title="Settings">
<shiny:TableView CellSelectedColor="#E0E0E0" CellAccentColor="#007AFF">
<shiny:TableRoot>
<shiny:TableSection Title="General">
<shiny:SwitchCell Title="Notifications" On="{Binding NotificationsOn, Mode=TwoWay}" />
<shiny:SwitchCell Title="Sound" On="{Binding SoundOn, Mode=TwoWay}" />
<shiny:CheckboxCell Title="Accept Analytics" Checked="{Binding AnalyticsAccepted, Mode=TwoWay}" />
</shiny:TableSection>
<shiny:TableSection Title="Account">
<shiny:EntryCell Title="Name" ValueText="{Binding Name, Mode=TwoWay}" Placeholder="Your name" />
<shiny:EntryCell Title="Email" ValueText="{Binding Email, Mode=TwoWay}" Keyboard="Email" />
<shiny:CommandCell Title="Change Password" Command="{Binding ChangePasswordCommand}" />
</shiny:TableSection>
<shiny:TableSection Title="Theme" shiny:RadioCell.SelectedValue="{Binding Theme, Mode=TwoWay}">
<shiny:RadioCell Title="Light" Value="Light" />
<shiny:RadioCell Title="Dark" Value="Dark" />
<shiny:RadioCell Title="System" Value="System" />
</shiny:TableSection>
<shiny:TableSection Title="Preferences">
<shiny:DatePickerCell Title="Birthday" Date="{Binding Birthday, Mode=TwoWay}" Format="D" />
<shiny:TimePickerCell Title="Daily Reminder" Time="{Binding ReminderTime, Mode=TwoWay}" />
<shiny:NumberPickerCell Title="Font Size" Number="{Binding FontSize, Mode=TwoWay}"
Min="10" Max="36" Unit="pt" />
</shiny:TableSection>
<shiny:TableSection Title="About">
<shiny:LabelCell Title="Version" ValueText="1.0.0" />
<shiny:CommandCell Title="Privacy Policy" Command="{Binding PrivacyCommand}" />
<shiny:CommandCell Title="Terms of Service" Command="{Binding TermsCommand}" />
</shiny:TableSection>
<shiny:TableSection Title="Actions">
<shiny:ButtonCell Title="Sign Out" Command="{Binding SignOutCommand}" ButtonTextColor="Red" />
</shiny:TableSection>
</shiny:TableRoot>
</shiny:TableView>
</ContentPage>
<shiny:ShinyContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:shiny="http://shiny.net/maui/controls"
x:Class="MyApp.StatusPage"
Title="Status">
<shiny:ShinyContentPage.PageContent>
<ScrollView>
<VerticalStackLayout Padding="20" Spacing="10">
<Label Text="System Status" FontSize="24" FontAttributes="Bold" />
<HorizontalStackLayout Spacing="8">
<shiny:PillView Text="API" Type="Success" />
<shiny:PillView Text="Database" Type="Warning" />
<shiny:PillView Text="Queue" Type="Critical" />
</HorizontalStackLayout>
<Button Text="View Details" Command="{Binding OpenDetailsCommand}" />
</VerticalStackLayout>
</ScrollView>
</shiny:ShinyContentPage.PageContent>
<shiny:ShinyContentPage.Panels>
<shiny:FloatingPanel IsOpen="{Binding IsDetailsOpen, Mode=TwoWay}"
PanelCornerRadius="20">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="Service Details" FontSize="18" FontAttributes="Bold" />
<HorizontalStackLayout Spacing="6">
<shiny:PillView Text="Healthy" Type="Success" />
<Label Text="API Server" VerticalOptions="Center" />
</HorizontalStackLayout>
<HorizontalStackLayout Spacing="6">
<shiny:PillView Text="Degraded" Type="Warning" />
<Label Text="Database Cluster" VerticalOptions="Center" />
</HorizontalStackLayout>
<HorizontalStackLayout Spacing="6">
<shiny:PillView Text="Down" Type="Critical" />
<Label Text="Message Queue" VerticalOptions="Center" />
</HorizontalStackLayout>
</VerticalStackLayout>
</shiny:FloatingPanel>
</shiny:ShinyContentPage.Panels>
</shiny:ShinyContentPage>
Mode=TwoWay for user-editable propertiesShowArrow="True" and KeepSelectedUntilBack="True"SelectedValue at the section levelItemsSource on sections for data-driven cellsShinyContentPage as the page base class, or place OverlayHost in a Grid for overlay panels. Place ImageViewer in a Grid as beforeText directly to your unread/count source (empty string auto-hides). Use MaxCount for numeric overflow ("99+") and IsDot for plain "has new" indicators. Reserve IsPulsing for genuinely important badgesstatic (T item) => item.Property lambda bindings, never string-basedOpenViewerOnTap="False" when controlling the viewer programmaticallydevelopment
Guide for generating code that uses Shiny.Data.Sync for reliable, background-capable bidirectional JSON sync over HTTP on iOS, Android, Windows, Linux, macOS, and Blazor WASM
devops
Guide for implementing push notifications in .NET MAUI apps using Shiny.Push (native FCM/APNs) and Shiny.Push.AzureNotificationHubs
tools
Cross-platform local notification management for .NET MAUI apps using Shiny, supporting scheduled, repeating, and geofence-triggered notifications with channels, badges, and interactive actions.
tools
GPS tracking, geofence monitoring, and motion activity recognition for .NET MAUI, iOS, and Android using Shiny.Locations