Component: Button (button-sm / card-btn) — absorbs the former Icon Tile via an optional leading Icon
Work Item: WI-2121
ViewModel: ButtonViewModel : SurfaceViewModel
Last Updated: 2026-06-05
Naming decision D3 (adopted): Button and Icon Tile are one component. The optional
Icon(ImageSource?) property is the variant: absent → text-only button; present → leading-icon button. An icon is data, not a separate type — this mirrorsMenuItemViewModel, where the icon is an optionalGlyph/ImageSource. Seenaming-decisions-record.html.
Shows how ButtonViewModel properties map to the rendered control.
ButtonText
│
▼
┌──────────────────────────────────────┐ ◄── 116×106px, fill #ffffff, stroke 1px
│ │
│ ┌──────────────────────────────┐ │ ◄── body (FRAME) 100×72px
│ │ │ │
ButtonText ────────► │ │ Action Button │ │ ◄── label text on button
│ │ │ │
│ └──────────────────────────────┘ │
│ │
Icon ──────────────► │ [i-heart] 18×18px │ ◄── i-heart (INSTANCE) icon
│ │
└──────────────────────────────────────┘
TapCommand ─────────► Executes on tap (navigate or custom action) ◄── ⚠️ TBD (context-dependent)
Link ──────────────► Destination page/route after tap ◄── ⚠️ TBD (context-dependent)
IsVisible ──────────► Controls card visibility ◄── ViewModel state
IsEnabled ──────────► Controls whether card is tappable ◄── ViewModel state
IsLoading ──────────► Loading/busy indicator state ◄── ViewModel state
⚠ Data Gaps: TapCommand action target, Link destination — sources TBD at presentation layer
Note: The Button is a generic action card — the button label, icon, and tap destination are all context-dependent and must be supplied by the consuming feature ViewModel. This document defines the property surface; specific use cases should derive
ButtonViewModeland fill in concrete values.
| Property | Status | Source | Notes |
|---|---|---|---|
| ButtonText | ⚠️ TBD | Static / feature ViewModel | Label to render on button (e.g. "Continue", "Get Started") |
| Icon | ⚠️ TBD | Static / feature ViewModel | i-heart INSTANCE — icon glyph/image source from Figma |
| TapCommand | ⚠️ TBD | Feature ViewModel | ICommand bound at presentation layer; action is context-dependent |
| Link | ⚠️ TBD | Feature ViewModel | Destination route/page resolved by SharedActionsService or Shell nav |
| IsVisible | ✅ Available | ViewModel state | Default true; feature can override |
| IsEnabled | ✅ Available | ViewModel state | Default true; feature can override (e.g. disable while loading) |
| IsLoading | ✅ Available | ViewModel state | Default strategy (no skeleton) |
ButtonText, Icon, TapCommand, and Link have no single backend source — they are
presentation-layer decisions set by whichever feature instantiates the Button. Concrete use-case
analysis is required for each feature that embeds this component.
Recommendation: When implementing a specific feature using Button, create a subclass:
ButtonViewModel (base)
├── DocumentButtonViewModel ← links to a document
├── FeatureButtonViewModel ← navigates to a feature page
└── ExternalButtonViewModel ← opens external URL
public class ButtonViewModel : SurfaceViewModel
{
// Button label text — set by consuming feature
private string _buttonText = string.Empty;
public string ButtonText
{
get => _buttonText;
set => SetProperty(ref _buttonText, value);
}
// Icon image source — maps to i-heart INSTANCE (18×18px) in Figma
// Set by consuming feature (FontImageSource, FileImageSource, etc.)
private ImageSource? _icon;
public ImageSource? Icon
{
get => _icon;
set => SetProperty(ref _icon, value);
}
// Tap command — wired at presentation layer; action is context-dependent
// ⚠️ TBD: navigation target resolved by SharedActionsService or Shell routing
public ICommand? TapCommand { get; set; }
// Destination route/page — may be null when TapCommand is a direct async action
// ⚠️ TBD: resolved by SharedActionsService or direct Shell.Current.GoToAsync
private string? _link;
public string? Link
{
get => _link;
set => SetProperty(ref _link, value);
}
// Visibility — default true; override in subclass when conditional display is needed
private bool _isVisible = true;
public bool IsVisible
{
get => _isVisible;
set => SetProperty(ref _isVisible, value);
}
// Enabled state — default true; set false while async operations are in progress
private bool _isEnabled = true;
public bool IsEnabled
{
get => _isEnabled;
set => SetProperty(ref _isEnabled, value);
}
// IsLoading — default strategy (no skeleton/overlay)
// Inherited from ComponentViewModel
}
"Continue", "Submit", "Get Started", "View Contract"string.Empty — renders an empty button if not setImageSource? (FontImageSource preferred — avoids Android Glide/RecordingCanvas crash, see BUG-2072)null → text-only button (centered label); set → leading-icon button (the former Icon Tile)MenuItemViewModel.GetImageFromGlyph() pattern for font-based icons — same idiom as the existing menu items, where an icon is an optional property rather than a distinct typeICommand assigned by consuming feature ViewModelMenuItemViewModel.TapCommand and KCMHomePageViewModel tap binding patternsShell.Current.GoToAsync(Link) or delegates to SharedActionsServiceSharedActionsService (for document links) or direct Shell route stringTapCommand is a self-contained async action (no navigation target)truefalse in subclass when card should be conditionally hiddentruefalse while async operations (data load, navigation) are in progress to prevent double-tapComponentViewModel| Service | Interface | Notes |
|---|---|---|
| None (base) | — | ButtonViewModel itself has no backend dependencies |
| Authentication | IAuthentication |
May be needed by subclasses that personalize button text or visibility |
| DataManager | DataManager |
May be needed by subclasses that link to documents or dynamic destinations |
| SharedActionsService | SharedActionsService |
Resolves document FileID → openable URL for TapCommand action |
| Shell Navigation | Shell.Current |
Standard MAUI navigation for route-based Link destinations |
docs/component-specifications/button/button-component.htmldocs/component-specifications/card/pilot-card-property-mapping.mdALPAMobile/ViewModels/HomePageViewModel.cs (tap command wiring pattern)ALPAMobile/ViewModels/KCMHomePageViewModel.cs (tap command + icon pattern)ALPAMobile/ViewModels/MenuItemViewModel.cs (icon source, TapCommand, Title)ALPAMobile/Services/IAuthentication.csDocument Owner: ALPA Mobile Team
Purpose: Data source analysis for Button component — context-dependent properties require per-feature subclass analysis
Figma Key: card-btn | Dimensions: 116×106px | Fill: #ffffff | Stroke: 1px