reactor-design
Skill839 starsChanged 6 days agoMITView on GitHub
Windows 11 design rules for Reactor — theme tokens, High Contrast, typography (`Heading`/`SubHeading`/`Caption`), 4px grid, acrylic surfaces, accessibility, animation, and a code-review checklist. Use when authoring, reviewing, or fixing visual styling.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- analyzer-dymSkill
- coverage-upliftSkill
- perf-compareSkill
- pr-lifecycleSkill
- pr-reviewSkill
- reactor-advancedSkill
- reactor-asyncSkill
- reactor-build-and-checkSkill
- reactor-chartsSkill
- reactor-commandingSkill
- reactor-devtoolsSkill
- reactor-dockingSkill
- reactor-dslSkill
- reactor-formsSkill
- reactor-getting-startedSkill
- reactor-inputSkill
- reactor-navigationSkill
- reactor-recipesSkill
- reactor-windowingSkill
- reactor-wrappingSkill
- reactor-appSkill
- ChartingSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
58 rules. About 0 are common, 31 are distinctive, and 27 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈31
Distinctive · estimate
This author's own choices. Worth a look.
27
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Verify changes using the testing guidance.
- Distinctive: line 30
Check these areas early in every review:
- Very specific: line 45
Use Theme.* tokens for all colors and brushes.
- Distinctive: line 217
No hardcoded colors in HC mode.
- Distinctive: line 218
No opacity on elements or brushes in HC — encode translucency in the alpha channel for Light/Dark only.
- Distinctive: line 219
No accent colors or regular WinUI brushes in HC.
- Distinctive: line 220
No gradient animations in HC — use a single system brush.
- Distinctive: line 221
Use 2px border thickness for flyouts, dialogs, and cards in HC.
- Very specific: line 234
Use the predefined text factories or WinUI style tokens.
- Very specific: line 314
Use SemiBold (600), never Bold (700) for emphasis — except Heading() which intentionally uses 700 for page titles.
- Very specific: line 316
Use SymbolThemeFontFamily for icon fonts, applied with the .FontFamily(...) modifier (not .Set, which re-runs every update and is never …
- Very specific: line 323
Never a bare "Segoe MDL2 Assets" (Windows 10 legacy) or a bare "Segoe Fluent Icons" (drops the Windows 10 tail).
- Distinctive: line 325
When icons and text are paired, top-align both in wrapping scenarios to prevent visual drift at larger text scales.
- Very specific: line 327
Smart tooltips for trimmed text: When text is trimmed with TextTrimming, add a tooltip that only appears when the text is actually trimmed:
- Distinctive: line 340
Use multiples of 4 for all margins, padding, and sizing values.
- Very specific: line 425
Use system values — ControlCornerRadius (4px) for controls and OverlayCornerRadius (8px) for overlays.
- Very specific: line 450
Mixed radii for nested surfaces: When nesting controls inside overlay containers, the outer container uses OverlayCornerRadius while inner …
- Very specific: line 482
Prefer MinHeight/MinWidth over fixed Height/Width.
- Distinctive: line 483
Avoid fixed widths on buttons — let content drive width.
- Very specific: line 504
Remove wrapper containers (Border, Grid, FlexColumn, VStack) that exist only for nesting without contributing layout, styling, or semantic …
- Very specific: line 524
Use Auto scrollbar visibility — scrollbar appears only when content overflows.
- Distinctive: line 526
Only the content area should scroll — headers and action bars remain outside the ScrollView.
- Very specific: line 537
Prefer TitleBar(...) as the top-of-window element for Reactor desktop apps where a title makes sense (most main windows, document shells, …
- Very specific: line 563
Use FlexColumn(...) with { RowGap = n } / FlexRow(...) with { ColumnGap = n } (or VStack(spacing, ...) / HStack(spacing, ...)) or Grid …
- Very specific: line 619
When integrating with existing INotifyPropertyChanged objects:
- Very specific: line 740
Use UseFocusTrap to keep focus inside a modal:
- Distinctive: line 797
Run WCAG checks programmatically:
- Distinctive: line 857
Use landmarks on major page regions.
- Very specific: line 859
Place announce.Region in the tree before calling .Announce().
- Distinctive: line 860
Test with Narrator + keyboard-only navigation.
- Distinctive: line 861
Test Light, Dark, and High Contrast themes (especially NightSky).
- Distinctive: line 862
Test at 100%, 150%, 200%, 250% display scaling.
- Distinctive: line 863
Test with maximum text scaling (Settings > Accessibility > Text size).
- Very specific: line 865
Use DividerStrokeColorDefaultBrush for dividers — custom brushes with opacity break in HC.
- Distinctive: line 896
Keep one acrylic layer per visual surface to avoid stacked-material artifacts.
- Very specific: line 937
Use FlyoutPresenterBackground and FlyoutBorderThemeBrush for standard popup surfaces.
- Very specific: line 982
Use ThemeShadow instead of composition drop shadows.
- Distinctive: line 983
Avoid gradient animations in High Contrast.
- Very specific: line 984
Use BrushTransition (via BackgroundTransition()) for smooth color changes.
- Very specific: line 985
Always set Translation(0, 0, 32) when using ThemeShadow.
- Very specific: line 991
Always set .WithKey() on items in dynamic lists.
- Very specific: line 1054
Use using static Microsoft.UI.Reactor.Factories.
- Very specific: line 1057
Use trailing .WithKey() as the last modifier.
- Distinctive: line 1082
Every UI change must survive text scaling and long strings:
- Very specific: line 1084
Use MinHeight instead of Height on containers with text.
- Distinctive: line 1085
Avoid fixed widths on buttons and text containers.
- Very specific: line 1086
Use VAlign(VerticalAlignment.Center) instead of margin-based centering.
- Distinctive: line 1087
Use tabular numerals for changing numbers (clock, battery, progress):
- Distinctive: line 1095
Do not explicitly set properties to their WinUI default values — it blocks future WinUI updates.
- Very specific: line 1109
Default Foreground on Text (it's TextFillColorPrimaryBrush already)
- Distinctive: line 1118
When reviewing Reactor UI code, verify:
- Distinctive: line 1170
If changing colors: Test in NightSky HC theme, hover on interactive elements.
- Distinctive: line 1172
If changing text/containers: Test with scaled text and long strings.
- Distinctive: line 1174
If changing layout: Test at 100%, 150%, 200%, 250% display scaling.
- Distinctive: line 1181
Test hover/pressed states on all interactive elements.
- Distinctive: line 1182
Test at 100%, 150%, 200%, and 250% display scaling.
- Distinctive: line 1183
Test with text scaling and long/localized strings for clipping and trimming.
- Distinctive: line 1184
Verify acrylic pairings and shadow clipping after layout changes.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Windows 11 Design for Reactor
Author, review, and fix Reactor UI code following Windows 11 design system rules.
Reactor is a functional UI framework for WinUI 3 that builds UI entirely in C# — no XAML, no data binding, no ViewModels. UI is described with immutable Element records, composed via bare factory methods imported with using static Microsoft.UI.Reactor.Factories (TextBlock(...), VStack(...), etc.), and updated through a React-style reconciler with hooks (UseState, UseEffect, etc.).
Controlled prop note: when design examples use inputs such as
TextBox(value, setValue)orSlider(value, ...), the factory keeps the simple call-site shape. Direct element-record reads of controlled props now returnOptional<T>; use.GetValueOrDefault(...)or.Value. Seemigration/050-optional-t.md.
This skill translates the Windows 11 design language into Reactor's C# projection so that apps built with Reactor look, feel, and behave like first-class Windows 11 applications.
Workflow
- Author new Reactor UI using the rules below.
- Review a PR using the checklist at the end of this file.
- Fix feedback by mapping issues to the specific rule and applying the correct pattern.
- Verify changes using the testing guidance.
Quick Scan Checklist
Check these areas early in every review:
- Theme tokens used for all colors — no hardcoded hex strings for themed surfaces.
- High Contrast works — no opacity, no accent colors, only system color brushes.
- Typography uses
Heading(),SubHeading(),Caption(), or WinUI style tokens — not rawFontSize/FontWeight. - Layout values use the 4px grid.
- Text scaling and localization are safe — no fixed heights on text containers.
- Shadows have elevation (
Translation(0, 0, 32)). - Acrylic surfaces use correct background + border pairings.
AutomationName()set on icon-only controls.- Keys set on list items for stable reconciliation.
Core Rules (Always Apply)
1. Theming Is the Primary Lens
Use Theme.* tokens for all colors and brushes. Never hardcode hex colors for themed surfaces.
// Correct: theme tokens
TextBlock("Hello").Foreground(Theme.PrimaryText)
Border(child).Background(Theme.CardBackground)
Button("Action").Background(Theme.Accent)
// Wrong: hardcoded colors on themed surfaces
TextBlock("Hello").Foreground("#000000")
Border(child).Background("#FFFFFF")Hardcoded colors are acceptable only for:
- One-off decorative elements that intentionally ignore theming (e.g., brand logos)
- Explicit hit-test targets (
Background("#00000000")for transparent hit areas)
Theme Token Reference
⚠️
Theme.Error,Theme.Success,Theme.Warning,Theme.ErrorTextdo NOT exist. UseTheme.SystemCritical(red/error),Theme.SystemSuccess(green),Theme.SystemCaution(yellow).
Text:
| Token | WinUI Resource | Purpose |
Theme.PrimaryText | TextFillColorPrimaryBrush | Primary body text, titles, and labels |
Theme.SecondaryText | TextFillColorSecondaryBrush | Captions, subtitles, and supplementary info |
Theme.TertiaryText | TextFillColorTertiaryBrush | Placeholder text and disabled secondary content |
Theme.DisabledText | TextFillColorDisabledBrush | Text in disabled controls or inactive states |
Theme.AccentText | AccentTextFillColorPrimaryBrush | Hyperlinks, accent-colored labels, and interactive text |
Accent fill:
| Token | WinUI Resource | Purpose |
Theme.Accent | AccentFillColorDefaultBrush | Primary accent buttons and controls at rest |
Theme.AccentSecondary | AccentFillColorSecondaryBrush | Accent button hover state |
Theme.AccentTertiary | AccentFillColorTertiaryBrush | Accent button pressed state |
Theme.AccentDisabled | AccentFillColorDisabledBrush | Disabled accent controls |
Control fill:
| Token | WinUI Resource | Purpose |
Theme.ControlFill | ControlFillColorDefaultBrush | Default rest state for standard controls |
Theme.ControlFillSecondary | ControlFillColorSecondaryBrush | Hover state of standard controls |
Theme.ControlFillTertiary | ControlFillColorTertiaryBrush | Pressed state of standard controls |
Theme.ControlFillDisabled | ControlFillColorDisabledBrush | Disabled state of standard controls |
Theme.ControlFillInputActive | ControlFillColorInputActiveBrush | Active/focused text input field backgrounds |
Surfaces & backgrounds:
| Token | WinUI Resource | Purpose |
Theme.SolidBackground | SolidBackgroundFillColorBaseBrush | App and page-level background (base layer) |
Theme.CardBackground | CardBackgroundFillColorDefaultBrush | Card and elevated surface backgrounds |
Theme.LayerFill | LayerFillColorDefaultBrush | Flyout, dialog, and pane backgrounds |
Theme.SubtleFill | SubtleFillColorSecondaryBrush | Subtle control highlights and section backgrounds |
Theme.SmokeFill | SmokeFillColorDefaultBrush | Semi-transparent overlay behind modal dialogs |
Stroke & border:
| Token | WinUI Resource | Purpose |
Theme.CardStroke | CardStrokeColorDefaultBrush | Borders on cards and elevated containers |
Theme.SurfaceStroke | SurfaceStrokeColorDefaultBrush | Borders on flyouts, dialogs, and pane surfaces |
Theme.DividerStroke | DividerStrokeColorDefaultBrush | Horizontal or vertical dividers between content |
Theme.ControlStroke | ControlStrokeColorDefaultBrush | Borders on interactive controls at rest |
Theme.ControlStrokeSecondary | ControlStrokeColorSecondaryBrush | Bottom edge of controls for depth effect |
System signal:
| Token | WinUI Resource | Purpose |
Theme.SystemAttention | SystemFillColorAttentionBrush | Informational indicators and badges |
Theme.SystemSuccess | SystemFillColorSuccessBrush | Success states and confirmations |
Theme.SystemCaution | SystemFillColorCautionBrush | Warning states and cautionary indicators |
Theme.SystemCritical | SystemFillColorCriticalBrush | Error states and critical alerts |
Theme.SystemNeutral | SystemFillColorNeutralBrush | Neutral status indicators |
Theme.SystemSolidNeutral | SystemFillColorSolidNeutralBrush | Solid neutral fill for icons and non-text indicators |
System signal backgrounds:
| Token | WinUI Resource | Purpose |
Theme.SystemAttentionBackground | SystemFillColorAttentionBackgroundBrush | Background for attention/info banners |
Theme.SystemSuccessBackground | SystemFillColorSuccessBackgroundBrush | Background for success banners and info bars |
Theme.SystemCautionBackground | SystemFillColorCautionBackgroundBrush | Background for warning banners and info bars |
Theme.SystemCriticalBackground | SystemFillColorCriticalBackgroundBrush | Background for error banners and info bars |
Theme.SystemNeutralBackground | SystemFillColorNeutralBackgroundBrush | Background for neutral status banners |
Theme.SystemSolidAttention | SystemFillColorSolidAttentionBackgroundBrush | Solid background for high-contrast attention surfaces |
For any WinUI resource not exposed as a named token, use Theme.Ref("ResourceKeyBrush").
Custom Theme Resource References
// Reference any WinUI resource by key
Border(child).Background(Theme.Ref("AcrylicBackgroundFillColorDefaultBrush"))
Border(child).WithBorder(Theme.Ref("SurfaceStrokeColorFlyoutBrush"), 1)Theme.Ref resolutions are cached per (key, theme). If you replace or add a resource at runtime without a theme change (swap a brand dictionary, replace a brush), call Theme.NotifyResourcesChanged() afterwards so every host re-resolves it.
Per-Subtree Theme Override
Force a subtree to a specific theme variant:
// Sidebar always renders in dark theme
VStack(sidebarContent).RequestedTheme(ElementTheme.Dark)See theme-aware-resources.md for full token list and pairing rules.
2. Lightweight Styling (Per-Control Resource Overrides)
Override control template resources using .Resources() to customize visual states without replacing the entire template:
// Correct: override button resources for a subtle button
Button("Action", onClick).Resources(r => r
.Set("ButtonBackground", Theme.SubtleFill)
.Set("ButtonBackgroundPointerOver", Theme.Ref("SubtleFillColorSecondaryBrush"))
.Set("ButtonBackgroundPressed", Theme.Ref("SubtleFillColorTertiaryBrush"))
.Set("ButtonBorderBrush", Theme.SubtleFill)
.Set("ButtonBorderBrushPointerOver", Theme.SubtleFill)
.Set("ButtonBorderBrushPressed", Theme.SubtleFill))
// Wrong: setting Background directly — loses hover/pressed/disabled states
Button("Action", onClick).Background(Theme.SubtleFill)Resource keys target the SolidColorBrush resource (ending in Brush), not the Color.
// Correct
.Set("ButtonBackground", Theme.Ref("ControlFillColorDefaultBrush"))
// Wrong — Color, not Brush
.Set("ButtonBackground", Theme.Ref("ControlFillColorDefault"))3. High Contrast Rules (Strict)
High Contrast users rely on a fixed set of 8 system color brushes. Your UI must work with these constraints.
Allowed HC system brushes:
| Brush | Purpose |
SystemColorWindowTextColorBrush | Text on window background |
SystemColorWindowColorBrush | Window/content background |
SystemColorHighlightTextColorBrush | Selected text foreground |
SystemColorHighlightColorBrush | Selection/hover background |
SystemColorButtonTextColorBrush | Button text |
SystemColorButtonFaceColorBrush | Button background |
SystemColorGrayTextColorBrush | Disabled/inactive text |
SystemColorHotlightColorBrush | Hyperlinks |
HC color pairings:
| Background | Foreground | Use Case |
SystemColorWindowColor | SystemColorWindowTextColor | General content |
SystemColorHighlightColor | SystemColorHighlightTextColor | Selected/hover states |
SystemColorButtonFaceColor | SystemColorButtonTextColor | Buttons |
SystemColorWindowColor | SystemColorGrayTextColor | Disabled content |
SystemColorWindowColor | SystemColorHotlightColor | Hyperlinks |
Rules:
- No hardcoded colors in HC mode.
- No opacity on elements or brushes in HC — encode translucency in the alpha channel for Light/Dark only.
- No accent colors or regular WinUI brushes in HC.
- No gradient animations in HC — use a single system brush.
- Use 2px border thickness for flyouts, dialogs, and cards in HC.
- No partial theme updates — when changing Light/Dark visual resources via
.Resources(), include matching HC-safe values in the same change. Don't leave HC untested. - Interactive containers in HC need a highlight border — for clickable cards or list items, add a
SystemColorHighlightColorborder in HC to indicate interactivity. - Empty HC dictionary is valid — when
.Resources()overrides target only Light/Dark and WinUI defaults already satisfy accessibility, you don't need to add HC-specific overrides. - Set
HighContrastAdjustmentat app level to prevent system overrides:Application.Current.HighContrastAdjustment = ApplicationHighContrastAdjustment.None;
Using Theme.* tokens correctly means HC usually "just works" because WinUI resolves them to appropriate system colors. Test to verify.
4. Typography Must Use Semantic Styles
Use the predefined text factories or WinUI style tokens. Never set FontSize and FontWeight directly for standard UI text.
Reactor text factories:
| Factory | Size | Weight | Use Case |
Caption("text") | 12px | Regular | Small labels, timestamps |
TextBlock("text") | 14px | Regular | Default body text |
Body("text") | 14px | Regular | WinUI BodyTextBlockStyle body text |
BodyStrong("text") | 14px | Semibold | Emphasized inline labels |
BodyLarge("text") | 18px | Regular | Prominent body text |
Subtitle("text") | 20px | Semibold | WinUI SubtitleTextBlockStyle — section headings |
SubHeading("text") | 20px | 600 | Section headers, card titles (Reactor preset) |
Title("text") | 28px | Semibold | WinUI TitleTextBlockStyle — page titles |
Heading("text") | 28px | 700 | Page titles (Reactor preset, slightly heavier) |
TitleLarge("text") | 40px | Semibold | WinUI TitleLargeTextBlockStyle — primary titles on feature/landing pages |
Display("text") | 68px | Semibold | WinUI DisplayTextBlockStyle — hero banners, at most one per page |
The Title/Subtitle/Body/BodyStrong/BodyLarge/TitleLarge/Display
factories map 1:1 to
WinUI's named TextBlock styles (Spec 039 §17.6). Prefer them when matching
WinUI design specs; the Heading/SubHeading factories are the older
Reactor presets and remain valid.
Font weight helpers:
| Helper | Weight | Use Case |
.SemiBold() | 600 | Emphasized body text, inline labels, field headers |
.Bold() | 700 | Reserved for Heading() page titles — avoid elsewhere |
TextBlock("Important label").SemiBold()
TextBlock("Page Title").Bold()WinUI type ramp (via .ApplyStyle()):
| Style | Size | Weight | Recommendation |
CaptionTextBlockStyle | 12px | Regular | Secondary labels, timestamps, footnotes |
BodyTextBlockStyle | 14px | Regular | Default body text, paragraphs, descriptions |
BodyStrongTextBlockStyle | 14px | Semibold | Emphasized body text, inline labels |
BodyLargeTextBlockStyle | 18px | Regular | Prominent body text |
SubtitleTextBlockStyle | 20px | Semibold | Section headings, card group labels |
TitleTextBlockStyle | 28px | Semibold | Page titles, dialog headings |
TitleLargeTextBlockStyle | 40px | Semibold | Primary page titles on feature pages — prefer the TitleLarge() factory |
DisplayTextBlockStyle | 68px | Semibold | Hero banners — one per page at most; prefer the Display() factory |
For sizes not covered by the factories, use .ApplyStyle() to apply WinUI text block styles. This sets size, weight, line height, and optical sizing in one call:
// Preferred: .ApplyStyle() for WinUI styles
TextBlock("Title").ApplyStyle("TitleTextBlockStyle")
TextBlock("Subtitle").ApplyStyle("SubtitleTextBlockStyle")
TextBlock("Body Strong").ApplyStyle("BodyStrongTextBlockStyle")
TextBlock("Caption").ApplyStyle("CaptionTextBlockStyle")
// Also correct: use the Reactor factories for common sizes
Heading("Page Title")
SubHeading("Section")
Caption("Fine print")
// Every WinUI type-ramp style has a factory except CaptionTextBlockStyle —
// Caption() is a size-only preset, so use .ApplyStyle("CaptionTextBlockStyle")
// when you need that style's line height and other setters
BodyLarge("Prominent text")
TitleLarge("Feature title")
Display("Hero")
// For any other named style, use .ApplyStyle(): it reports an unresolved key
// instead of throwing. Never assign Application.Current.Resources[...] through
// .Set() — that indexer throws out of the mount action on a misspelled key.
TextBlock("Custom").ApplyStyle("MyAppTextBlockStyle")
// Wrong: raw font properties for standard UI text
TextBlock("Title").FontSize(28).FontWeight(new FontWeight(700))Rules:
- Use
SemiBold(600), neverBold(700) for emphasis — exceptHeading()which intentionally uses 700 for page titles. - Minimum font size: 12px. Anything smaller makes complex scripts unreadable.
- Use
SymbolThemeFontFamilyfor icon fonts, applied with the.FontFamily(...)modifier (not.Set, which re-runs every update and is never unwound):
Where no liveTextBlock("\uE710") .FontFamily((FontFamily)Application.Current.Resources["SymbolThemeFontFamily"])Application.Currentis available, the explicit"Segoe Fluent Icons, Segoe MDL2 Assets"stack is an acceptable static fallback. Never a bare"Segoe MDL2 Assets"(Windows 10 legacy) or a bare"Segoe Fluent Icons"(drops the Windows 10 tail). - When icons and text are paired, top-align both in wrapping scenarios to prevent visual drift at larger text scales.
- TextWrapping:
NoWrapis the default — useTextWrapping.WraporTextWrapping.WrapWholeWordswhen text should flow to multiple lines. ChooseWrapWholeWordsfor body text to avoid mid-word breaks. - Smart tooltips for trimmed text: When text is trimmed with
TextTrimming, add a tooltip that only appears when the text is actually trimmed:TextBlock(longText) .TextTrimming(TextTrimming.CharacterEllipsis) .ToolTip(longText)
See typography-and-colors.md for the full type ramp and color token list.
5. Layout and Scaling
4px Grid
Use multiples of 4 for all margins, padding, and sizing values. The spacing scale is built on a 4px base unit:
| Value | Name | Usage |
| 0px | Flush | No gap between elements |
| 2px | Hairline | Tight spacing between related inline items |
| 4px | Compact | Icon-to-label gaps and small control internals |
| 8px | Standard | Default gap between sibling controls |
| 12px | Relaxed | Padding inside cards and grouped sections |
| 16px | Spacious | Between distinct content sections |
| 24px | Section | Major section boundaries and card padding |
| 36px | Page | Page-level margins around content |
| 48px | Hero | Large spacing for hero areas and visual breaks |
// Correct: multiples of 4
VStack(8, children).Padding(16)
Border(child).Margin(12).Padding(8)
HStack(4, items)
// Flex containers take FlexPadding, NOT Padding. `.Padding()` silently has no
// effect on a FlexElement (FlexRow / FlexColumn / Flex) — `mur check` reports
// this as REACTOR_MOD_003. Don't add a Border solely to get padding; a Border is
// still right when you also need its background, corner radius, or border brush.
FlexColumn(children).FlexPadding(16)
FlexRow(items).FlexPadding(horizontal: 16, vertical: 8)
// Wrong: odd values cause blurry rendering at fractional scales
VStack(5, children).Padding(15)
Border(child).Margin(3)
// Wrong: no effect, and costs a build-check cycle
FlexColumn(children).Padding(16)Margin vs Padding
Margin pushes an element away from its neighbors. It works on every Reactor element.
Padding adds space between a container's edge and its content. It works on Border, Control-based elements (Button, TextBox, etc.), Grid, StackPanel-based layout panels (VStack, HStack), RelativePanel, and TextBlock-based text (Text, TextBlock, and the typography factories). Image does not support .Padding() — wrap it in a Border if you need inner padding. Grid, VStack/HStack, and RelativePanel also accept .CornerRadius() directly.
| Element | .Margin() | .Padding() |
Border | ✓ | ✓ |
Button | ✓ | ✓ |
TextBox | ✓ | ✓ |
TextBlock | ✓ | ✓ |
VStack | ✓ | ✓ |
HStack | ✓ | ✓ |
Grid | ✓ | ✓ |
RelativePanel | ✓ | ✓ |
Image | ✓ | — |
// Margin — works on ALL elements
TextBlock("Hello").Margin(8)
VStack(children).Margin(16)
Border(child).Margin(12)
// Padding — Border, Control, Grid, StackPanel, RelativePanel and TextBlock
Border(child).Padding(16) // ✓ works
Button("Go").Padding(12) // ✓ works
Grid(columns, rows, cells).Padding(16) // ✓ works
VStack(8, items).Padding(16) // ✓ works
TextBlock("Hello").Padding(8) // ✓ worksBoth .Margin() and .Padding() accept four overloads:
// Uniform — same on all sides
element.Margin(16)
// Horizontal, Vertical
element.Margin(horizontal: 24, vertical: 8)
// Per-side: left, top, right, bottom
element.Margin(left: 4, top: 8, right: 16, bottom: 24)
// A Thickness you already have — useful when lifting a value out of a .Set(...)
element.Margin(new Thickness(68, 4, 40, 4))Corner Radius
Use system values — ControlCornerRadius (4px) for controls and OverlayCornerRadius (8px) for overlays.
WinUI provides two corner radius theme resources. Use ThemeResource.CornerRadius() to resolve the system values at render time, ensuring your UI adapts if these values are customized:
// Preferred: resolve system values at render time
var controlRadius = ThemeResource.CornerRadius("ControlCornerRadius");
var overlayRadius = ThemeResource.CornerRadius("OverlayCornerRadius");
// Apply to elements
Border(child).CornerRadius(controlRadius.TopLeft) // controls, buttons, cards
Border(dialog).CornerRadius(overlayRadius.TopLeft) // dialogs, flyouts, menus
// Also acceptable: hardcoded system values
Border(child).CornerRadius(4) // ControlCornerRadius equivalent
Border(child).CornerRadius(8) // OverlayCornerRadius equivalent
// Selective rounding (top corners only)
Border(child).CornerRadius(8, 8, 0, 0)
// Wrong: non-standard radii (even if from Figma)
Border(child).CornerRadius(3)
Border(child).CornerRadius(6)Mixed radii for nested surfaces: When nesting controls inside overlay containers, the outer container uses OverlayCornerRadius while inner controls use ControlCornerRadius:
var cr = ThemeResource.CornerRadius("ControlCornerRadius");
var or = ThemeResource.CornerRadius("OverlayCornerRadius");
// Dialog with control-radius inner elements
Border(
VStack(12,
TextBox("", placeholderText: "Username").CornerRadius(cr.TopLeft),
Button("Sign In", onClick)
.Background(Theme.Accent)
.CornerRadius(cr.TopLeft)
).Margin(24)
)
.Background(Theme.LayerFill)
.WithBorder(Theme.SurfaceStroke)
.CornerRadius(or.TopLeft) // outer overlay radiusSizing
// Correct: MinHeight for flexible sizing
Button("Action").MinHeight(40)
VStack(children).MinWidth(200)
// Wrong: fixed Height clips at larger text scales
Button("Action").Height(32)
TextBox(text, setText).Height(30)- Prefer
MinHeight/MinWidthover fixedHeight/Width. - Avoid fixed widths on buttons — let content drive width.
- Buttons achieve correct height through padding, not explicit
Height.
Container Choice
Pick the right container. Prefer FlexRow / FlexColumn as the default for
linear layout — they follow CSS Flexbox semantics (grow, shrink, basis, gap,
wrap, justify-content, align-items), which matches the mental model web-
trained engineers and designers already have. VStack / HStack remain a good
choice when you specifically want StackPanel's shrink-wrap cross-axis behavior
or are porting from existing StackPanel code.
| Need | Use | Not |
| Single child with background/border | Border(child) | Grid or FlexColumn with one child |
| Linear vertical layout | FlexColumn(children) (preferred), or VStack(children) | Nested Grid |
| Linear horizontal layout | FlexRow(children) (preferred), or HStack(children) | Nested Grid |
Grow/shrink, wrapping, or justify-content distribution | Flex(children) with .Flex(grow/shrink/basis) on children | Complex nested stacks |
| Positional row/column layout | Grid(columns, rows, children) | Canvas or absolute positioning |
| Text that needs trimming | Grid with "*" column | HStack / FlexRow (children get unbounded main-axis width) |
Remove wrapper containers (Border, Grid, FlexColumn, VStack) that exist only for nesting without contributing layout, styling, or semantic purpose.
Text trimming caveat: HStack (StackPanel) and FlexRow both give children unbounded width on the main axis, so TextTrimming never activates inside them. Use a Grid with a GridSize.Star() column. Note: GridSize.Auto also sizes to content and prevents trimming — always use GridSize.Star() for the column that contains trimmable text.
// Correct: Grid constrains width so trimming works
Grid(
columns: [GridSize.Auto, GridSize.Star()],
rows: [GridSize.Auto],
Image(source).Size(32, 32).Grid(column: 0),
TextBlock(title).TextTrimming(TextTrimming.CharacterEllipsis).Grid(column: 1))
// Wrong: TextTrimming never fires inside HStack
HStack(8,
Image(source).Size(32, 32),
TextBlock(title).TextTrimming(TextTrimming.CharacterEllipsis))ScrollView Configuration
- Use
Autoscrollbar visibility — scrollbar appears only when content overflows. - Set
HorizontalContentAlignment = Stretchon the ScrollView to prevent content from collapsing. - Only the content area should scroll — headers and action bars remain outside the ScrollView.
// Correct: header stays fixed, content scrolls
VStack(
Heading("Page Title"),
ScrollView(VStack(8, contentItems)))Window Title Bar
Prefer TitleBar(...) as the top-of-window element for Reactor desktop apps
where a title makes sense (most main windows, document shells, settings
windows). It integrates with the Windows caption (drag region, system menu,
min/max/close) and themes with the rest of the app, and it accepts inline
Content and RightHeader for branding, document context, or small inline
controls — which avoids the common mistake of stacking a custom header row
below the system chrome and ending up with two visual title zones.
// Preferred: real Windows title bar with app title + subtitle + inline content
var titleBar = (TitleBar("MyApp") with
{
Subtitle = currentDoc,
Content = ModeSwitcher(mode, setMode),
RightHeader = TextBlock($"{rowsLoaded:N0} rows").FontSize(12).Opacity(0.6),
}).Flex(shrink: 0);
return FlexColumn(titleBar, MainContent());Small dialogs, embedded pop-outs, and tool windows that already have a
distinct visual header don't need TitleBar — use it where you'd otherwise
reach for a custom header row across the full window width.
Spacing
Use FlexColumn(...) with { RowGap = n } / FlexRow(...) with { ColumnGap = n } (or VStack(spacing, ...) / HStack(spacing, ...)) or Grid RowSpacing/ColumnSpacing — not spacer elements.
// Correct
VStack(8, TextBlock("A"), TextBlock("B"), TextBlock("C"))
// Wrong: spacer element for spacing
VStack(
TextBlock("A"),
Border(null).Height(8), // Don't do this
TextBlock("B"))Shadows
ThemeShadow requires elevation to be visible. Add Translation(0, 0, 32) and ensure the parent has padding (12px) to prevent shadow clipping:
Border(
ScrollView(VStack(16, content))
).Background(Theme.Ref("AcrylicBackgroundFillColorDefaultBrush"))
.WithBorder(Theme.Ref("SurfaceStrokeColorFlyoutBrush"), 1)
.CornerRadius(8)
.Translation(0, 0, 32)
.Set(b =>
{
b.BackgroundSizing = BackgroundSizing.InnerBorderEdge;
b.Shadow = new ThemeShadow();
})See layout-and-scaling.md for full layout rules.
6. Data Flow and State
Reactor uses hooks, not MVVM data binding. Follow these patterns:
State-Driven UI (Preferred)
var (items, setItems) = UseState(new List<Item>());
var (filter, setFilter) = UseState("");
var filtered = UseMemo(() =>
items.Where(i => i.Name.Contains(filter, StringComparison.OrdinalIgnoreCase)).ToList(),
items, filter);
return VStack(
TextBox(filter, setFilter, placeholderText: "Filter..."),
VStack(filtered.Select(item =>
TextBlock(item.Name).WithKey(item.Id)
).ToArray()));UseObservable for External Models
When integrating with existing INotifyPropertyChanged objects:
var model = UseObservable(externalModel);
return VStack(
TextBlock(model.Title),
Slider(model.Volume, 0, 100, v => model.Volume = v));Hook Rules (Critical)
- Same order every render — no hooks inside
ifblocks, no hooks in variable-length loops. - Only call from
Render()— or from within a function component body. - Use
UseCallbackfor stable references — handlers passed to children should be memoized.
// Correct: hooks at top level, unconditional
var (count, setCount) = UseState(0);
var (name, setName) = UseState("");
var increment = UseCallback(() => setCount(count + 1), count);
// Wrong: conditional hook
if (showCounter)
{
var (count, setCount) = UseState(0); // BREAKS hook ordering
}7. Accessibility
Tier 1 Modifiers (Every Control)
These modifiers are zero-cost — apply them by default:
// Icon-only buttons MUST have AutomationName
Button(Content: Image(iconSource), onClick)
.AutomationName("Close dialog")
// Headings for screen reader navigation
Heading("Settings").HeadingLevel(AutomationHeadingLevel.Level1)
SubHeading("General").HeadingLevel(AutomationHeadingLevel.Level2)
// Tab order and access keys
Button("Save", onSave).IsTabStop(true).TabIndex(0).AccessKey("S")| Modifier | Purpose |
.AutomationName("text") | Screen reader label — required on icon-only controls |
.HeadingLevel(Level1..Level9) | Heading hierarchy for screen reader navigation |
.IsTabStop(true/false) | Include/exclude from tab order |
.TabIndex(n) | Explicit tab order |
.AccessKey("X") | Alt+X keyboard shortcut |
Tier 2 Modifiers (Lazy-Allocated)
These allocate an AutomationProperties peer only when set:
TextBox(name, setName)
.HelpText("Enter your full legal name")
.Required()
// Hide decorative elements from screen readers
Image(decorativeSource).AccessibilityHidden()
// Position-in-set for screen readers on list items
items.Select((item, i) =>
TextBlock(item.Name)
.PositionInSet(i + 1, items.Count)
.WithKey(item.Id))| Modifier | Purpose |
.HelpText("text") | Extended description (read after name) |
.FullDescription("text") | Detailed description for complex elements |
.AccessibilityHidden() | Hide decorative elements from AT |
.AccessibilityView(Raw/Control/Content) | Control AT tree visibility |
.Landmark(LandmarkType) | Navigation landmark (see below) |
.Required() | Marks a field as required for AT |
.LiveRegion(Polite/Assertive) | Announce dynamic content changes |
.PositionInSet(pos, size) | Position in a virtual list for AT |
Navigation Landmarks
Landmarks let screen reader users jump between page regions:
NavigationView(navItems, content)
.Landmark(AutomationLandmarkType.Navigation)
FlexColumn(mainContent)
.Landmark(AutomationLandmarkType.Main)
VStack(searchControls)
.Landmark(AutomationLandmarkType.Search)
VStack(formFields)
.Landmark(AutomationLandmarkType.Form)Heading Hierarchy
Maintain a logical heading structure for screen reader navigation:
// Good: proper hierarchy
Heading("Settings").HeadingLevel(AutomationHeadingLevel.Level1)
SubHeading("General").HeadingLevel(AutomationHeadingLevel.Level2)
SubHeading("Advanced").HeadingLevel(AutomationHeadingLevel.Level2)
// Bad: skipping levels
Heading("Settings").HeadingLevel(AutomationHeadingLevel.Level1)
Caption("Detail").HeadingLevel(AutomationHeadingLevel.Level4) // skipped 2 and 3Focus Trapping (Dialogs / Modals)
Use UseFocusTrap to keep focus inside a modal:
var trap = this.UseFocusTrap(isActive: true);
return Border(
VStack(12,
Heading("Confirm"),
TextBlock("Are you sure?"),
HStack(8,
Button("Cancel", onCancel),
Button("Confirm", onConfirm))
).Padding(24)
).FocusTrap(trap); // Tab cycles within this containerUseFocusTrap(isActive) returns a FocusTrapHandle. Apply it with
.FocusTrap(handle). Focus is trapped while isActive is true.
UseAnnounce (Live Announcements)
For dynamic status updates that aren't tied to a visible element:
var announce = UseAnnounce();
// The announce region must be in the visual tree
return VStack(12,
announce.Region, // invisible — just hosts the live region
Button("Save", async () =>
{
await Save();
announce.Announce("Document saved successfully");
}),
Button("Error demo", () =>
announce.Announce("Upload failed — check your connection", assertive: true))
);Important: announce.Region must be in the visual tree or announcements
are silently lost. Place it once near the root of your page.
SemanticPanel (Custom Roles)
For controls that don't map to standard WinUI automation peers:
Border(child).Semantics(
role: "Slider",
value: $"{percent}%",
rangeMin: 0,
rangeMax: 100,
rangeValue: percent);AccessibilityScanner (Automated Testing)
Run WCAG checks programmatically:
var results = AccessibilityScanner.Scan(rootElement);
// Built-in checks include:
// - Missing AutomationName on interactive controls
// - Missing HeadingLevel on heading text
// - Color contrast ratio < 4.5:1
// - Touch target size < 44x44
// - Missing landmark regions
// - Tab order gaps
// - Missing PositionInSet on list items
// - AccessKey conflicts
AccessibilityScanner.ExportJson(results, "a11y-report.json");Roslyn Analyzers
Three compile-time analyzers catch accessibility issues:
| Analyzer | Checks |
REACTOR_A11Y_001 | Icon-only Button/ToggleButton missing .AutomationName() |
REACTOR_A11Y_002 | Image missing .AutomationName() or .AccessibilityHidden() |
REACTOR_A11Y_003 | Form field (TextBox/NumberBox/PasswordBox) missing label |
These run as warnings by default. Promote to errors in CI:
<WarningsAsErrors>REACTOR_A11Y_001;REACTOR_A11Y_002;REACTOR_A11Y_003</WarningsAsErrors>Accessible Forms Pattern
Combine validation with accessibility:
var validation = this.UseValidationContext();
var (email, setEmail) = UseState("");
return FormField(
TextBox(email, setEmail)
.Validate("email", email, Validate.Required(), Validate.Email())
.Required()
.HelpText("We'll send a confirmation to this address"),
label: "Email",
required: true,
showWhen: ShowWhen.WhenTouched)
.Landmark(AutomationLandmarkType.Form);FormField automatically wires AutomationName from the label and
associates error messages with the input via DescribedBy.
Accessibility Rules
- Set
AutomationNameon every control without visible text. - Maintain heading hierarchy — don't skip levels.
- Use landmarks on major page regions.
- Focus-trap all dialogs and modals.
- Place
announce.Regionin the tree before calling.Announce(). - Test with Narrator + keyboard-only navigation.
- Test Light, Dark, and High Contrast themes (especially NightSky).
- Test at 100%, 150%, 200%, 250% display scaling.
- Test with maximum text scaling (Settings > Accessibility > Text size).
- Hit-test targets for light-dismiss must be visible:
Background("#00000000"). - Use
DividerStrokeColorDefaultBrushfor dividers — custom brushes with opacity break in HC. - Enable Roslyn analyzers in CI to catch common issues at build time.
See code-review-checklist.md for the full accessibility checklist.
8. Acrylic Surface Pairings
Acrylic backgrounds have specific border pairings. Using the wrong combination produces incorrect visuals.
| Surface Type | Background | Border |
| Flyouts, tooltips | AcrylicBackgroundFillColorDefaultBrush | SurfaceStrokeColorFlyoutBrush |
| UI surfaces | AcrylicBackgroundFillColorBaseBrush | SurfaceStrokeColorDefaultBrush |
// Correct: flyout acrylic pairing
Border(content)
.Background(Theme.Ref("AcrylicBackgroundFillColorDefaultBrush"))
.WithBorder(Theme.Ref("SurfaceStrokeColorFlyoutBrush"), 1)
.CornerRadius(8)
.Translation(0, 0, 32)
.Set(b =>
{
b.BackgroundSizing = BackgroundSizing.InnerBorderEdge;
b.Shadow = new ThemeShadow();
})BackgroundSizing = InnerBorderEdge is required on any bordered acrylic surface. It prevents the background from bleeding through the border edge. Always set it on acrylic containers with a border.
- Overlays on acrylic use
LayerOnAcrylicFillColorDefaultBrush. - Keep one acrylic layer per visual surface to avoid stacked-material artifacts.
Window Backdrops (Mica / Acrylic)
For window-level material (Mica/Acrylic showing through the title bar and
chrome), use the .Backdrop(BackdropKind) modifier on the root element —
not a brush. The modifier walks up to the host's Window and assigns the
right SystemBackdrop:
return Grid([GridSize.Star()], [GridSize.Auto, GridSize.Star()],
titleBar.Grid(row: 0),
content.Grid(row: 1)
).Backdrop(BackdropKind.Mica);Available kinds: BackdropKind.None, Mica, MicaAlt, DesktopAcrylic,
AcrylicThin. On ReactorHostControl (windowless host) the modifier
no-ops cleanly. For Mica to show through, the root element must not
paint an opaque background — drop Theme.SolidBackground from the root
when applying a backdrop. Spec 033 §6.
Flyout Surface Pattern
Flyout/popup surfaces should follow a standard elevation pattern:
Border(
ScrollView(VStack(8, flyoutContent))
)
.Background(Theme.Ref("FlyoutPresenterBackground"))
.WithBorder(Theme.Ref("FlyoutBorderThemeBrush"), 1)
.CornerRadius(8)
.Translation(0, 0, 32)
.Set(b =>
{
b.BackgroundSizing = BackgroundSizing.InnerBorderEdge;
b.Shadow = new ThemeShadow();
})Use FlyoutPresenterBackground and FlyoutBorderThemeBrush for standard popup surfaces. Use the explicit acrylic resource pairings (above) only when building custom surfaces that don't use WinUI's flyout presenter resources.
9. Animation and Motion
Implicit Transitions (Recommended)
Animate property changes smoothly — the framework handles interpolation:
// Opacity fade
Border(child)
.OpacityTransition()
.Opacity(isVisible ? 1.0 : 0.0)
// Background color crossfade
VStack(children)
.BackgroundTransition()
.Background(isActive ? Theme.Accent : Theme.ControlFill)
// Scale bounce
Border(child)
.ScaleTransition()
.Scale(isPressed ? 0.95f : 1.0f)Layout Animations
Animate children being added, removed, or repositioned:
VStack(items.Select(item =>
TextBlock(item.Name).WithKey(item.Id)
).ToArray()).LayoutAnimation()Element Enter/Exit Transitions
// Fade + slide from bottom on mount
Border(child).WithTransitions(
Transition.Fade,
Transition.Slide(Edge.Bottom))Rules:
- Use
ThemeShadowinstead of composition drop shadows. - Avoid gradient animations in High Contrast.
- Use
BrushTransition(viaBackgroundTransition()) for smooth color changes. - Always set
Translation(0, 0, 32)when usingThemeShadow.
10. Reconciliation and Performance
Keys for Lists
Always set .WithKey() on items in dynamic lists. Without keys, the reconciler matches by position, causing unnecessary re-mounts on insert/reorder:
// Correct: keyed children — stable identity
VStack(items.Select(item =>
HStack(8,
Image(item.Avatar).Size(32, 32),
TextBlock(item.Name)
).WithKey(item.Id)
).ToArray())
// Wrong: unkeyed — insert at index 0 re-renders everything
VStack(items.Select(item =>
HStack(8,
Image(item.Avatar).Size(32, 32),
TextBlock(item.Name)
).ToArray())Memoize Expensive Computations
var sorted = UseMemo(() =>
items.OrderBy(x => x.Name).ToList(),
items);
var handler = UseCallback(() => save(count), count);Avoid Deep Nesting
Flatten visual tree depth where possible. Use Border instead of single-child Grid/VStack.
// Correct: flat
Border(TextBlock("Hello")).Background(Theme.CardBackground).Padding(16)
// Even better: the Card(child) factory bakes in CardBackground +
// CardStroke 1px hairline + 8px corner radius + 16px padding.
Card(TextBlock("Hello"))
// Wrong: unnecessary nesting
VStack(
Grid([GridSize.Star()], [GridSize.Star()],
TextBlock("Hello")
).Background(Theme.CardBackground)
).Padding(16)Use .Set() Sparingly
.Set() is an escape hatch to raw WinUI. It's valid but bypasses the virtual element model — use it for properties Reactor doesn't expose, not as a general pattern.
// Good: property not exposed by Reactor
TextBlock("Clock").Set(tb => tb.Typography.NumeralAlignment = FontNumeralAlignment.Tabular)
// Bad: property that Reactor exposes as a modifier
TextBlock("Hello").Set(tb => tb.Margin = new Thickness(16)) // Use .Margin(16) instead11. Formatting Conventions
- Use
using static Microsoft.UI.Reactor.Factories;to access the DSL without prefix. - One element per line when nesting gets deep.
- Group modifiers logically: layout first, then appearance, then behavior.
- Use trailing
.WithKey()as the last modifier.
// Good: readable modifier order
Border(
VStack(16,
Heading("Title"),
TextBlock("Description").Foreground(Theme.SecondaryText),
HStack(8,
Button("Cancel", onCancel),
Button("Save", onSave).Resources(r => r
.Set("ButtonBackground", Theme.Accent)
.Set("ButtonForeground", Theme.Ref("TextOnAccentFillColorPrimaryBrush")))
)
)
)
.Padding(24)
.CornerRadius(8)
.Background(Theme.CardBackground)
.WithBorder(Theme.CardStroke, 1)
.AutomationName("Settings card")12. Text Scaling and Localization
Every UI change must survive text scaling and long strings:
- Use
MinHeightinstead ofHeighton containers with text. - Avoid fixed widths on buttons and text containers.
- Use
VAlign(VerticalAlignment.Center)instead of margin-based centering. - Use tabular numerals for changing numbers (clock, battery, progress):
TextBlock($"{percent}%").Set(tb => tb.Typography.NumeralAlignment = FontNumeralAlignment.Tabular)
13. Avoid Setting WinUI Defaults
Do not explicitly set properties to their WinUI default values — it blocks future WinUI updates.
// Wrong: these are all WinUI defaults
Button("Action")
.Padding(12) // Default button padding
.CornerRadius(4) // Default ControlCornerRadius
.Height(32) // Default button height
// Correct: only set what differs
Button("Action").MinHeight(40)Defaults you should not set:
- Default
Foregroundon Text (it'sTextFillColorPrimaryBrushalready) Padding(0)orMargin(0)(zero is the default)Opacity(1.0)(1.0 is the default)CornerRadius(4)on buttons (WinUI default isControlCornerRadius)
Code Review Checklist
When reviewing Reactor UI code, verify:
Theming:
- Uses
Theme.*tokens for colors/brushes — no hardcoded hex on themed surfaces - Resource keys end in
Brush(not Color name) - No opacity on elements in High Contrast
- Acrylic surfaces use correct background + border pairings
BackgroundSizing = InnerBorderEdgeset on bordered acrylic containers- No partial theme changes — Light/Dark
.Resources()updates tested with HC in the same change - Interactive containers (cards, list items) have visible borders in HC
Typography:
- Uses semantic text factories (
Heading,SubHeading,Caption) or WinUI style tokens FontWeightis SemiBold (600), not Bold (700) — exceptHeading()page titles- No fixed heights on text containers — uses
MinHeight - Trimmed text has a tooltip for overflow content
- Icons and text top-aligned in wrapping scenarios
Layout:
- Layout values use multiples of 4
- Corner radius is 4 (controls) or 8 (overlays) — no non-standard values
- Uses
MinHeight/MinWidthinstead of fixed sizing for text containers - Uses
Borderfor single-child containers (notVStack/Gridwrappers) - No unnecessary wrapper containers without layout or styling purpose
HStackdoes not contain text that needsTextTrimming- Text trimming columns use
"*"(not"Auto", which also prevents trimming) ThemeShadowhasTranslation(0, 0, 32)and 12px parent padding- ScrollView content uses
HorizontalContentAlignment = Stretch
Controls and Styling:
.Resources()used for button visual state overrides (not.Background()directly)- All visual states covered when overriding — rest + hover + pressed + disabled
- No explicit setting of WinUI default values
- No no-op
.Resources()overrides that repeat WinUI defaults - Uses existing WinUI styles before creating custom overrides
Accessibility:
AutomationNameset on icon-only controlsHeadingLevelset on heading textPositionInSet/SizeOfSetset on list items- Hit-test targets for light-dismiss are visible (
Background("#00000000"))
State and Reconciliation:
.WithKey()set on items in dynamic lists- Hooks are unconditional and in consistent order
UseCallbackwraps handlers passed to child componentsUseMemowraps expensive computations
PR Hygiene:
- PR scope excludes unrelated churn — every change maps to a concrete UX reason
- No broad
.Resources()or styling edits unrelated to the feature being changed
If changing colors: Test in NightSky HC theme, hover on interactive elements.
If changing text/containers: Test with scaled text and long strings.
If changing layout: Test at 100%, 150%, 200%, 250% display scaling.
Testing Guidance
- Test Light, Dark, and High Contrast themes (especially NightSky).
- Test hover/pressed states on all interactive elements.
- Test at 100%, 150%, 200%, and 250% display scaling.
- Test with text scaling and long/localized strings for clipping and trimming.
- Verify acrylic pairings and shadow clipping after layout changes.
- Validate Figma implementation measurements at 100% scale factor.
- Capture before/after screenshots for visual changes including Light/Dark/HC evidence.
References
Consult these for detailed guidance:
- Theme-aware resources
- Typography and colors
- Layout and scaling
- Control styles
- Code review checklist
External References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

