All margins, padding, and sizing values should be multiples of 4: 4, 8, 12, 16, 20, 24, 32, 40, 48, etc.
Odd values (3, 5, 7, 11, 15) cause blurry rendering at fractional DPI scales (125%, 150%, 175%).
// Correct: multiples of 4
VStack(8,
TextBlock("Item 1"),
TextBlock("Item 2")
).Padding(16).Margin(12)
// Wrong: odd values
VStack(5,
TextBlock("Item 1"),
TextBlock("Item 2")
).Padding(15).Margin(7)Windows 11 defines two standard corner radii:
| Token | Value | Use Case |
|---|---|---|
ControlCornerRadius |
4px | Controls, buttons, cards |
OverlayCornerRadius |
8px | Flyouts, dialogs, popups |
// Control corner radius (4px)
Border(child).CornerRadius(4)
Button("Action").CornerRadius(4)
// Overlay corner radius (8px)
Border(flyoutContent).CornerRadius(8)
// Selective rounding (top corners only for panels)
Border(panelContent).CornerRadius(8, 8, 0, 0)
// Wrong: non-standard radius
Border(child).CornerRadius(3) // Not in the design system
Border(child).CornerRadius(6) // Not in the design systemFigma caveat: Design tools sometimes show non-standard values. Always map to 4 or 8 in code.
Default for linear layout: FlexColumn / FlexRow. They implement CSS
Flexbox semantics via Yoga, so grow, shrink, basis, gap, wrap,
justify-content, and align-items all behave the way web-trained engineers
and designers expect. VStack / HStack (StackPanel) remain appropriate when
you specifically want StackPanel's shrink-wrap cross-axis behavior, are
porting existing StackPanel code, or prefer its terser single-arg spacing.
FlexColumn(
Heading("Title"),
TextBlock("Description"),
Button("Action", onClick)
) with { RowGap = 8 }
FlexRow(
Image(icon).Size(24, 24),
TextBlock("Label"),
TextBlock("Value").Foreground(Theme.SecondaryText)
) with { ColumnGap = 12, AlignItems = FlexAlign.Center }Linear vertical or horizontal layout with optional spacing. Good fit when StackPanel's shrink-wrap cross-axis is what you want.
VStack(8,
Heading("Title"),
TextBlock("Description"),
Button("Action", onClick)
)
HStack(12,
Image(icon).Size(24, 24),
TextBlock("Label"),
TextBlock("Value").Foreground(Theme.SecondaryText)
)Explicit row/column definitions with child positioning.
Grid(
columns: [GridSize.Auto, GridSize.Star(), GridSize.Auto],
rows: [GridSize.Auto, GridSize.Star()],
TextBlock("Label").Grid(row: 0, column: 0),
TextBox(value, setValue).Grid(row: 0, column: 1),
Button("Go", onClick).Grid(row: 0, column: 2),
ScrollView(
VStack(8, content)
).Grid(row: 1, column: 0, columnSpan: 3))Track helpers (spec 033 §1):
GridSize.Star()— flexible, takes remaining space (weight 1)GridSize.Star(2)— flexible, twice the weightGridSize.Auto— sizes to contentGridSize.Px(200)— fixed 200px
The legacy string-form overload (Grid(["Auto", "*"], …)) still compiles but
is soft-deprecated (CS0618).
Modern flexible layout via Yoga engine. Best for proportional, wrapping, and complex responsive layouts.
// Responsive row with grow
Flex(
TextBlock("Fixed").Flex(shrink: 0, basis: 100),
Border(child).Flex(grow: 1),
Button("Action").Flex(shrink: 0)
) with
{
JustifyContent = FlexJustify.SpaceBetween,
AlignItems = FlexAlign.Center
}
// Wrapping grid of cards
Flex(
cards.Select(card =>
CardComponent(card)
.Flex(basis: 300, grow: 1)
.WithKey(card.Id)
).ToArray()
) with
{
Wrap = FlexWrap.Wrap,
ColumnGap = 16,
RowGap = 16
}For a single child with background, border, padding, or corner radius.
// Correct: Border for styled single-child container
Border(
TextBlock("Card content")
).Padding(16).CornerRadius(4).Background(Theme.CardBackground)
// Wrong: Grid or VStack for single child
Grid([GridSize.Star()], [GridSize.Star()],
TextBlock("Card content")
).Background(Theme.CardBackground)Wrap content that may exceed available space. Default to ScrollView()
— the modern Microsoft.UI.Xaml.Controls.ScrollView. Reach for
ScrollViewer() only when you need a Control-shaped API
(HorizontalContentAlignment, parallax animations, the
ScrollViewer.SetXxx attached properties on templated parents, or the
IsIntermediate view-changed flag).
ScrollView(VStack(8, longContent))Configuration:
- Use
Autoscrollbar visibility (the default) — scrollbar appears only when content overflows. - Headers and action bars should remain outside the scroller — only the content area scrolls.
// Correct: fixed header, scrolling content
VStack(
Heading("Page Title").Padding(16, 16, 16, 8),
ScrollView(VStack(8, contentItems)))// Correct: flexible sizing
Button("Submit").MinHeight(40)
VStack(content).MinWidth(200).MaxWidth(600)
TextBox(value, setValue).MinHeight(32)
// Wrong: fixed sizing clips at larger text scales
Button("Submit").Height(32)
TextBox(value, setValue).Height(30).Width(200)Let content determine width, or set MinWidth:
// Correct
Button("Save", onSave)
Button("Save", onSave).MinWidth(120)
// Wrong
Button("Save", onSave).Width(100) // Clips long localized stringsFixed height clips text at larger text scaling settings:
// Correct
Border(TextBlock(message)).MinHeight(40).Padding(8)
// Wrong
Border(TextBlock(message)).Height(40) // Clips at 200% text scale// Horizontal alignment
TextBlock("Centered").HAlign(HorizontalAlignment.Center)
Button("Right").HAlign(HorizontalAlignment.Right)
TextBlock("Stretch").HAlign(HorizontalAlignment.Stretch)
// Vertical alignment
TextBlock("Middle").VAlign(VerticalAlignment.Center)
// Center both axes
TextBlock("Centered").Center()When placing different controls side by side (e.g., toggle + text), vertically center all participants:
HStack(8,
ToggleSwitch(isOn, setOn).VAlign(VerticalAlignment.Center),
TextBlock("Enable feature").VAlign(VerticalAlignment.Center))Use container spacing, not spacer elements:
// Correct: spacing parameter
VStack(8, TextBlock("A"), TextBlock("B"), TextBlock("C"))
// Wrong: spacer element
VStack(
TextBlock("A"),
Border(null).Height(8).Opacity(0),
TextBlock("B"))For Grid, use row/column spacing via .Set():
Grid(columns, rows, children).Set(g =>
{
g.RowSpacing = 8;
g.ColumnSpacing = 12;
})Border(content)
.CornerRadius(8)
.Translation(0, 0, 32) // Required: elevation makes shadow visible
.Set(b =>
{
b.Shadow = new ThemeShadow();
})Rules:
Translation(0, 0, 32)is required — without it, the shadow is invisible.- Add 12px padding on the parent to prevent shadow clipping.
- Shadow receivers must be at a lower z-order than the elevated element.
- Use
ThemeShadowinstead of composition drop shadows.
HStack (StackPanel) and FlexRow both give children unbounded main-axis width, so TextTrimming never activates inside them. Use a Grid with a GridSize.Star() column:
// Correct: Grid constrains width
Grid(
columns: [GridSize.Auto, GridSize.Star()],
rows: [GridSize.Auto],
Image(avatar).Size(32, 32).Grid(column: 0),
TextBlock(title)
.TextTrimming(TextTrimming.CharacterEllipsis)
.ToolTip(title) // Show full text on hover when trimmed
.Grid(column: 1))
// Wrong: TextTrimming never fires in HStack or FlexRow
HStack(8,
Image(avatar).Size(32, 32),
TextBlock(title).TextTrimming(TextTrimming.CharacterEllipsis))Important: GridSize.Auto also sizes to content and prevents trimming — always use GridSize.Star() for the column that contains trimmable text. This is a common mistake when multiple columns are present.
Remove wrapper containers (Border, Grid, VStack) that exist only for nesting without contributing layout, styling, or semantic purpose:
// Wrong: Grid wrapper serves no purpose
Grid([GridSize.Star()], [GridSize.Star()],
TextBlock("Hello").Grid(row: 0, column: 0))
// Correct: just the text
TextBlock("Hello")Use logical margin and padding for bidirectional layouts:
TextBlock("Label")
.MarginInlineStart(16) // Left in LTR, right in RTL
.PaddingInlineEnd(8) // Right in LTR, left in RTLTest at 100%, 150%, 200%, and 250% scaling. Common issues:
- Blurry edges from odd-numbered sizes (use 4px grid).
- Clipped text from fixed heights.
- Misaligned controls from hardcoded positioning.
- Shadow clipping from insufficient parent padding.
When implementing from Figma or design comps:
- Measure at 100% scale factor.
- Map corner radius to system values (4 or 8).
- Map spacing to 4px grid multiples.
- Validate against the running build on the same scale factor.