Prioritized list of components to create for ShellUI Native, aligned with ShellUI Components patterns. All components should follow compositional patterns using Dependencies (parent + sub-components) instead of monolithic ChildContent.
Last revised: 2026-10-07 (first prerelease, 0.1.0-alpha.1).
Status at a glance: P0–P5 ✅ done · P6 partly done (resizable, navbar, sidebar open) · P7 partly done · shipped as 0.1.0-alpha.1 (58 families, 89 CLI targets) · Avalonia (Phase 2) ⏭️ next.
Every single-line form control renders at 40px high (matches shadcn h-10 / default ButtonSize.Default). Multi-line controls use MinimumHeightRequest. Horizontal padding is 12px; vertical padding is 0 when HeightRequest is fixed (the fixed height owns the vertical rhythm), 8px when it isn't.
| Component | Height | Padding | Notes |
|---|---|---|---|
Button (Default) |
40 (from ButtonStyle.Height) |
(16, 0) |
Sm=36 (12,0), Lg=44 (32,0), Icon=40×40. Sizes to content (HorizontalOptions=Start) |
Input |
40 | (12, 0) |
Native Entry frame stripped (ShellPlatform.StripNativeChrome) — one border only |
Select |
40 | (12, 0) |
Custom-drawn trigger + floating list (rows 32) since 2026-09-27 |
DatePicker |
40 | (12, 0) |
Custom-drawn trigger + floating Calendar (32px day cells) since 2026-10-02 |
TimePicker |
40 | (12, 0) |
Custom-drawn trigger + floating hour / minute / AM-PM columns (32px cells) since 2026-10-04 |
Textarea |
MinimumHeightRequest=80 |
(12, 8) |
Multi-line — grows with content |
TabsList |
40 | (4) |
Triggers fill the remaining 32 |
Checkbox / RadioGroupItem |
16×16 | — | Icon-shaped, not a field (shadcn h-4 w-4) |
Switch |
44×24 track, 20 thumb | inset 2 | Thumb travels exactly 44 - 2*2 - 20 = 20 |
Rule for new form controls: if it visually sits in a form row next to Input, it MUST be 40px tall. If it's a compositional container that hosts its own field (e.g. Combobox, InputOTP), the inner field carries the 40. Don't rely on platform default heights — MAUI's Entry / Picker defaults vary wildly across Android / iOS / Windows. When wrapping a platform control in a ShellUI Border, call ShellPlatform.StripNativeChrome so it doesn't draw a second frame inside ours.
Menu / list rows (DropdownItem, Select items) are 32 tall with Padding = (8, 0) inside a panel with 4px padding — shadcn's px-2 py-1.5 rows.
Row labels that sit in a fixed-height row set both VerticalOptions = Center and VerticalTextAlignment = Center; without the latter a label that gets stretched to the row height draws its text at the top.
Icon-shaped controls keep their exact pixel dimensions — those numbers are the design, not a fill. Their outer row is the hit target.
Since 2026-09-27 colors live in one place: ShellTheme in the shell template (Shell.cs). It
holds a Light and a Dark palette keyed by ShellToken (ShellUI's CSS variables:
Background, Foreground, Card, Popover, Primary, Secondary, Muted, Accent,
Destructive, Border, Input, Ring, plus Success / Warning / Info / Overlay), and
publishes the active palette as app resources (ShellUIPrimary Color + ShellUIPrimaryBrush
Brush, …) that it swaps on theme change. Values follow ShellUI's default neutral theme.
Components bind with element.Token(property, ShellToken.X) (a SetDynamicResource wrapper), so a
theme switch or a runtime override repaints everything. The init XAML
(StyleTemplates.ThemeResourceDictionary) mirrors the same palettes for design-time use.
Rules:
- Never write a hex color in a component template —
TemplateContentTests.MAUI_components_use_theme_tokens_not_hardcoded_colorsfails the build. Shadows may useColors.Blackwith an opacity. - Need a new role? Add it to
ShellToken, both palettes andStyleTemplates(checked byStyleTemplatesTests), then use it. - Avalonia (Phase 2) maps the same keys to
{DynamicResource ShellUIPrimary}in its own dictionaries.
Components are written and run as normal C# in examples/MAUI.Demo/Components/UI/, then copied
into the CLI templates by a script — the demo always tests exactly what shellui-native add installs.
- Edit or add
examples/MAUI.Demo/Components/UI/<Name>.csand exercise it inMainPage.xaml. - New component? Add it to
NEWinscripts/sync-templates.py(registry key, display name, category, tags). python scripts/sync-templates.py— rewrites each<Name>Template.cscontent string (namespace placeholder restored, quotes escaped) and recomputesshell/icon/element-extensions/buttondependencies from the code. Running it twice writes nothing.- Register new templates in
ComponentRegistry, thendotnet test.
Icons: python scripts/generate-icons.py regenerates Icon.cs from the ShellIcons catalog
(../../icons/shell-icons); edit its ICONS list to change the curated set.
ShellUI Blazor approach: Parent components use CascadingValue to provide themselves; child components use [CascadingParameter] to consume the parent. This enables:
- Compositional API:
Dialog+DialogTrigger+DialogContentinstead of a singleDialogwith internal slots - Flexible layout: Children can be placed anywhere in the tree
- State sharing: Parent holds state (Open, Value); Trigger/Content react to it
ShellUI Native (MAUI) equivalent: Use Dependencies in ComponentMetadata so shellui-native add dialog auto-installs dialog-trigger, dialog-content, etc. Children find parent via visual tree: this.FindParentOfType<Dialog>() or similar.
| Parent | Sub-components (Dependencies) | Usage |
|---|---|---|
| Dialog | DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose | Modal overlay |
| Drawer | DrawerTrigger, DrawerContent | Slide-out panel |
| Sheet | SheetTrigger, SheetContent | Bottom/top sheet |
| Dropdown | DropdownTrigger, DropdownContent, DropdownItem | Dropdown menu |
| Popover | PopoverTrigger, PopoverContent | Popover overlay |
| Tabs | TabsList, TabsTrigger, TabsContent | Tabbed navigation |
| Accordion | AccordionItem, AccordionTrigger, AccordionContent | Expandable sections |
| Collapsible | CollapsibleTrigger, CollapsibleContent | Expand/collapse |
| Select | SelectTrigger, SelectContent, SelectItem | Select dropdown |
| HoverCard | HoverCardTrigger, HoverCardContent | Hover popover |
| ContextMenu | ContextMenuTrigger, ContextMenuContent, ContextMenuOption | Context menu |
| Card | CardHeader, CardContent, CardFooter | (Already implemented) |
| Component | Status | Dependencies | ShellUI Ref |
|---|---|---|---|
| shell | ✅ Done | — | Shell |
| button | ✅ Done | button-variants | Button |
| button-variants | ✅ Done | — | ButtonVariants |
| input | ✅ Done (height fix 2026-08-29) | — | Input |
| label | ✅ Done | — | Label |
| checkbox | ✅ Done | — | Checkbox |
| switch | ✅ Done | — | Switch |
| card | ✅ Done | card-header, card-content, card-footer | Card |
| card-header | ✅ Done | — | CardHeader |
| card-content | ✅ Done | — | CardContent |
| card-footer | ✅ Done | — | CardFooter |
| separator | ✅ Done | — | Separator |
| badge | ✅ Done | — | Badge |
| progress | ✅ Done | — | Progress |
| alert | ✅ Done | — | Alert |
Compositional: Parent + Trigger + Content. Needed for most app flows.
| Component | Status | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| dialog | ✅ Done | dialog-trigger, dialog-content, dialog-header, dialog-footer, dialog-title, dialog-description, dialog-close | Dialog | Modal overlay, center screen |
| drawer | ✅ Done | drawer-trigger, drawer-content | Drawer | Slide-out from side (Bottom/Left/Right/Top) |
| sheet | ✅ Done | sheet-trigger, sheet-content | Sheet | Similar to Drawer, bottom sheet variant |
| dropdown | ✅ Done | dropdown-trigger, dropdown-content, dropdown-item | Dropdown | Menu dropdown. Item hit target raised to 40px 2026-08-29 |
| popover | ✅ Done | popover-trigger, popover-content | Popover | Floating content popover |
Essential form controls for full form coverage.
| Component | Status | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| textarea | ✅ Done | — | Textarea | Multi-line text input |
| select | ✅ Done | — | Select | Custom-drawn trigger + floating list at the 40px baseline (native Picker dropped 2026-09-27) |
| slider | ✅ Done | — | Slider | Range input |
| radio-group | ✅ Done | radio-group-item | RadioGroup | Radio button group. Item checked-border fixed 2026-08-29 |
| date-picker | ✅ Done | calendar | DatePicker | Custom trigger + floating calendar (2026-10-02) |
| time-picker | ✅ Done | — | TimePicker | Custom trigger + hour / minute / AM-PM columns (2026-10-04) |
See DEVELOPMENT_PLAN.md § Phase 1c for scope, order, and exit criteria.
| Component | Priority | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| collapsible ✅ | P3.1 (build first — primitive) | collapsible-trigger, collapsible-content, element-extensions | Collapsible | Single expand/collapse — the IsOpen + animation primitive accordion-item composes on top of. |
| accordion ✅ | P3.2 | accordion-item, accordion-trigger, accordion-content, element-extensions | Accordion | Multiple sections. Type=Single (radio-style) or Multiple. |
| tabs ✅ | P3.3 | tabs-list, tabs-trigger, tabs-content, element-extensions | Tabs | Tab bar + one visible panel. Trigger MUST render at the 40px baseline (row-level control). |
| breadcrumb ✅ | P3.4 | breadcrumb-item | Breadcrumb | Nav trail with separator between items. |
| skeleton ✅ | P3.5 | — | Skeleton | Animated grey block, sized by parent — use for perceived-perf on lists. |
| scroll-area ✅ | P3.6 | — | ScrollArea | ScrollView wrapper. Cross-platform scrollbar styling is thin — consider whether this is worth the wrapper vs documenting native ScrollView. |
| Component | Priority | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| tooltip ✅ | P4.1 | shell | Tooltip | Done 2026-10-02 (floats in the page layer) |
| toast ✅ | P4.2 | shell, icon, button | Sonner | Done 2026-09-27 — Toaster host + static Toast.Show/Success/Error/... |
| spinner ✅ | P4.3 | shell, icon | Loading | Done 2026-09-27 as spinner (Loading's spinner variant) |
| alert-dialog ✅ | P4.4 | shell, element-extensions, button | AlertDialog | Done 2026-09-27 — ShowAsync() returns the choice |
| hover-card ✅ | P4.5 | shell, hover-card-trigger, hover-card-content | HoverCard | Done 2026-10-02 |
| Component | Priority | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| avatar ✅ | P5.1 | shell, icon | Avatar | Done 2026-09-27 |
| table ✅ | P5.2 | shell | Table | Done 2026-10-05 — one template (Table, TableHeader, TableRow, TableHead, TableCell); hover, selection, caption, sideways scroll |
| empty-state ✅ | P5.3 | shell, icon | EmptyState | Done 2026-10-04 — icon tile, title, description, action row, optional dashed border |
| callout ✅ | P5.4 | shell, icon | Callout | Done 2026-10-04 — Info / Warning / Danger / Tip / Default, tinted background |
| pagination ✅ | P5.5 | shell, icon | Pagination | Done 2026-10-04 — previous/next, sibling window, ellipses |
| toggle ✅ | — | shell, icon | Toggle | Done 2026-10-04 — pressed-state button (Default / Outline) |
| Component | Priority | Dependencies | ShellUI Ref | Notes |
|---|---|---|---|---|
| context-menu ✅ | P6.1 | shell, icon, element-extensions | ContextMenu | Done 2026-10-05 — one template; opens at the pointer on right-click, under the trigger on long-press |
| carousel ✅ | P6.2 | shell, icon | Carousel | Done 2026-10-05 — one template; swipe, arrows, dots, loop, auto-play |
| stepper ✅ | P6.3 | shell, icon, button | Stepper | Done 2026-10-05 — one template (Stepper + StepperStep) with built-in navigation |
| resizable | P6.4 | — | Resizable | Resizable panels |
| navbar | P6.5 | nav-trigger, nav-content, nav-list, nav-item | Navbar | Collapsible nav bar |
| sidebar | P6.6 | — | Sidebar | App sidebar layout |
| Component | Priority | Notes |
|---|---|---|
| chart (line, bar, pie, area) | P7 | Consider external chart lib integration |
| combobox ✅ | P7 | Done 2026-10-04 — Select with a filter field, Enter picks the first match |
| file-upload | P7 | File picker |
| input-otp ✅ | P7 | Done 2026-10-04 — slots over one hidden field (paste / autofill / numeric keyboard) |
| date-range-picker | P7 | Date range selection |
| toggle-group ✅ | — | Done 2026-10-05 — single or multiple selection, Default / Outline |
| number-input ✅ | — | Done 2026-10-05 — − / + buttons, min / max clamp, decimal value |
| tag-input ✅ | — | Done 2026-10-05 — chips with remove, Enter / comma / semicolon to commit |
| kbd ✅ | — | Done 2026-10-05 |
| stat-card ✅ | — | Done 2026-10-05 — title, value, trend pill, description, icon |
| timeline ✅ | — | Done 2026-10-05 — dot or icon markers joined by a line |
| multi-select ✅ | — | Done 2026-10-05 — searchable, chips in the trigger, panel stays open while picking |
| tree-view ✅ | — | Done 2026-10-05 — nested items, expand / collapse, selection |
| copy-button ✅ | P7 | Done 2026-10-05 — clipboard copy with a check confirmation |
| link-card ✅ | — | Done 2026-10-05 — tappable card that can open a URL |
| aspect-ratio ✅ | — | Done 2026-10-05 |
| wrap-layout ✅ | — | Done 2026-10-05 — wrapping layout used by tag-input and the demo's button rows |
| theme-toggle ✅ | P7 | Done — ThemeToggle component |
Components that should be reviewed for consistency with the compositional pattern:
| Component | Current | Target | Action |
|---|---|---|---|
| Card | ✅ Uses Dependencies (card-header, card-content, card-footer) | Keep | Card uses [ContentProperty("Children")] so XAML <Card><CardHeader/><CardContent/><CardFooter/></Card> works. Sub-components are layout slots (compositional). |
| Button | ✅ Uses Dependencies (button-variants) | Keep | Single component + variants; no Trigger/Content pattern. Correct. |
| Alert | No sub-components | Keep | Standalone feedback; no composition needed. |
No breaking changes needed for current components. New components (Dialog, Drawer, etc.) will follow the Trigger/Content pattern from the start.
For Trigger/Content components to talk to their parent:
// Extension method (add to shared utilities)
public static T? FindParentOfType<T>(this Element element) where T : Element
{
var parent = element.Parent;
while (parent != null)
{
if (parent is T match) return match;
parent = parent.Parent;
}
return null;
}- Single template per component — one
.csfile per logical component - Shared enums/variants —
ButtonVariants,AlertVariant, etc. inVariants/folder - Design tokens — bind colors with
.Token(property, ShellToken.X); never hardcode a hex (see Design Token Contract) - Composition over configuration — prefer Trigger+Content over
IsModal/RenderModeflags - Minimal surface — only expose properties that map to real use cases
<!-- Good: compositional -->
<ui:Dialog IsOpen="{Binding IsOpen}" OpenChanged="OnOpenChanged">
<ui:DialogTrigger>
<ui:Button Text="Open" />
</ui:DialogTrigger>
<ui:DialogContent>
<ui:DialogHeader>
<ui:DialogTitle Text="Title" />
</ui:DialogHeader>
<Label Text="Body content" />
<ui:DialogFooter>
<ui:Button Text="Close" Clicked="Close" />
</ui:DialogFooter>
</ui:DialogContent>
</ui:Dialog>Shipped in 0.1.0-alpha.1 (see DEVELOPMENT_PLAN.md and
RELEASE_NOTES.md): P0–P5, P6 context-menu / carousel / stepper, and the
P7 items marked ✅ above — 58 families, 89 CLI targets, MAUI only.
Next — Phase 2 (Avalonia). Port in the same order as MAUI (P0 → P1 first), reusing the component APIs, tokens and sizing contract. MAUI is far ahead, so new MAUI components wait until Avalonia has P0 + P1.
MAUI backlog after that: resizable, navbar, sidebar (P6); chart, file-upload, date-range-picker (P7); and the ShellUI Blazor targets not yet ported (button-group, input-group, command / command-palette, menubar, navigation-menu, data-table, …).