Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a2bb96a
cornerradius resource guidance
karkarl May 4, 2026
921acd5
app theme register guidance
karkarl May 4, 2026
c769d35
figma plugin live re-write wip
karkarl May 6, 2026
5faef96
codegen and copilot cli hybrid arch
karkarl May 6, 2026
ab6459a
verify code generatio flow
karkarl May 6, 2026
1888e4c
figma plugin UI update
karkarl May 6, 2026
a8fb9ab
Remove figmaapp and figma-codegen-test sample apps
karkarl May 7, 2026
d1a7875
Remove critical-review.md changes from this branch
karkarl May 7, 2026
0457458
security: remove FigmaBridge and Figma plugin (localhost attack surface)
karkarl May 8, 2026
f2c899c
feat(cli): add mur figma watch — polling-based live sync replacement
karkarl May 8, 2026
1b8e2be
feat(figma-plugin): reinstate plugin with secure CLI-based generation
karkarl May 8, 2026
91bd237
fix(figma-plugin): target ES6 to avoid nullish coalescing in sandbox
karkarl May 8, 2026
d540ad5
fix(figma-plugin): accept all container node types + remove template …
karkarl May 8, 2026
5551952
fix(figma-plugin): enable Generate even when figma.fileKey is null
karkarl May 8, 2026
1202816
fix(figma-plugin): resolve fileKey via enablePrivatePluginApi + URL f…
karkarl May 8, 2026
f03239b
feat(figma-plugin): auto-launch terminal + reference skill files in p…
karkarl May 8, 2026
791be4f
fix(figma-plugin): revert to simple copilot -p clipboard copy
karkarl May 8, 2026
d1dff22
docs(figma.md): clarify watch-based sync agent instructions
karkarl May 8, 2026
92838d4
chore: remove FigmaApp generated during testing
karkarl May 8, 2026
d94d663
feat(figma): comprehensive control mapping + watch-integrated prompts
karkarl May 8, 2026
64e2c30
fix(figma-plugin): use single quotes for copilot -p to avoid shell es…
karkarl May 8, 2026
23a1264
remove bridge and use figma mcp for livesync
karkarl May 8, 2026
bb0348a
feat: add FigmaApp sample — Figma-to-Reactor translated test app
karkarl May 8, 2026
a98ef62
security: implement STRIDE threat model mitigations
karkarl May 8, 2026
5fbafeb
fix(figma-plugin): remove status indicators from buttons
karkarl May 9, 2026
75e5374
live sync
karkarl May 12, 2026
fda8db1
wip
karkarl May 18, 2026
63f121c
Remove FigmaApp sample and add Figma plugin README
karkarl May 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
697 changes: 697 additions & 0 deletions docs/specs/033-figma-to-reactor.md

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions skills/design-docs/theme-aware-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,108 @@ return VStack(
// Adjust layout or content based on theme
);
```

## App-Level Custom Theme Resources

Define brand colors and app-specific semantic tokens that adapt to Light, Dark, and High Contrast using `AppTheme.Register()`. This injects custom brushes into WinUI's `Application.Current.Resources.ThemeDictionaries` at app startup — making them available throughout the app via `Theme.Ref()` and lightweight styling `.Resources()`.

### Defining Custom Theme Resources

Call `AppTheme.Register()` in the App constructor or `OnLaunched`, **before** building the visual tree:

```csharp
AppTheme.Register(theme => theme
.Add("BrandPrimaryBrush",
light: "#005A9E",
dark: "#4FC3F7",
highContrast: "SystemColorHighlightColorBrush")
.Add("BrandSecondaryBrush",
light: "#106EBE",
dark: "#81D4FA",
highContrast: "SystemColorHotlightColorBrush")
.Add("BrandSurfaceBrush",
light: "#F0F6FF",
dark: "#1A1A2E",
highContrast: "SystemColorWindowColorBrush")
.Add("BrandSubtleBrush",
light: "#E8F0FE",
dark: "#2A2A3E",
highContrast: "SystemColorButtonFaceColorBrush"));
```

Under the hood, `AppTheme.Register()`:
1. Gets or creates the Light, Dark, and HighContrast `ResourceDictionary` entries in `Application.Current.Resources.ThemeDictionaries`
2. Adds a `SolidColorBrush` for each key in each theme variant
3. For HC values that reference a system brush key (e.g., `"SystemColorHighlightColorBrush"`), resolves the brush at registration time

### Consuming Custom Resources

Custom resources use the same `Theme.Ref()` API as WinUI built-in resources — no new syntax:

```csharp
// Direct property usage
Border(child).Background(Theme.Ref("BrandPrimaryBrush"))
TextBlock("Branded heading").Foreground(Theme.Ref("BrandSecondaryBrush"))

// Lightweight styling — brand button with all states
Button("Brand Action", onClick).Resources(r => r
.Set("ButtonBackground", Theme.Ref("BrandPrimaryBrush"))
.Set("ButtonBackgroundPointerOver", Theme.Ref("BrandSecondaryBrush"))
.Set("ButtonBackgroundPressed", Theme.Ref("BrandPrimaryBrush"))
.Set("ButtonForeground", Theme.Ref("TextOnAccentFillColorPrimaryBrush")))

// Brand-colored card surface
Border(
VStack(12, cardContent).Margin(16)
)
.Background(Theme.Ref("BrandSurfaceBrush"))
.WithBorder(Theme.CardStroke, 1)
.CornerRadius(ThemeResource.CornerRadius("ControlCornerRadius").TopLeft)
```

### Gradient Brushes

For surfaces that accept arbitrary `Brush` types (e.g., `Border.Background`), `AddGradient()` registers `LinearGradientBrush` resources. In High Contrast, gradients must fall back to a solid color.

**Important:** Most control template resource keys (e.g., `ButtonBackground`, `TextControlBackground`) expect `SolidColorBrush`. Assigning gradients to those keys may not render correctly. Use gradients only on direct surface properties.

```csharp
AppTheme.Register(theme => theme
.AddGradient("BrandAccentGradientBrush",
light: new GradientDef(("0", "#7cb6e9"), ("0.5", "#335fe3"), ("1.0", "#ee9bbf")),
dark: new GradientDef(("0", "#7cb6e9"), ("0.5", "#335fe3"), ("1.0", "#ee9bbf")),
highContrast: "#48B1E9")); // Solid fallback in HC

// Use on surfaces, not control template keys
Border(hero).Background(Theme.Ref("BrandAccentGradientBrush"))
```

### XAML Equivalent

`AppTheme.Register()` is the Reactor equivalent of defining custom brushes in XAML's `App.xaml` or a `Colors.xaml` resource dictionary with `ThemeDictionaries`:

```xml
<!-- XAML equivalent — what AppTheme.Register() generates under the hood -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="BrandPrimaryBrush" Color="#005A9E" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="BrandPrimaryBrush" Color="#4FC3F7" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="BrandPrimaryBrush"
Color="{ThemeResource SystemColorHighlightColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
```

### Rules

1. **Register before building the visual tree** — call `AppTheme.Register()` in the App constructor or `OnLaunched`, before any component renders.
2. **Always provide all three variants** (light, dark, highContrast). Omitting HC causes accessibility regressions. There is no optional-HC overload by design.
3. **HC values must reference system color brushes or solid hex colors** — no gradients, no opacity, no custom colors in HC. Use WinUI system brush keys like `"SystemColorHighlightColorBrush"`, `"SystemColorHotlightColorBrush"`, etc.
4. **Custom keys must end in `Brush`** — matches WinUI naming conventions and ensures `Theme.Ref()` resolves them correctly.
5. **Don't re-register WinUI built-in keys** — use `Theme.*` tokens or `Theme.Ref()` for existing WinUI resources. `AppTheme.Register()` is for app-defined resources only.
6. **Duplicate key registration throws** — registering the same key twice throws at startup. Libraries and app code must coordinate key names to avoid collisions.
7. **HC brush references are snapshots** — HC values that reference system brush keys are resolved at registration time. If the HC palette changes at runtime (rare), custom brushes won't update until the next full re-render.
75 changes: 66 additions & 9 deletions skills/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,65 @@ Border(child).Background(Theme.Ref("AcrylicBackgroundFillColorDefaultBrush"))
Border(child).WithBorder(Theme.Ref("SurfaceStrokeColorFlyoutBrush"), 1)
```

#### App-Level Custom Theme Resources

Define brand colors and app-specific semantic tokens that adapt to Light, Dark, and High Contrast themes. Use `AppTheme.Register()` at app startup to inject custom brushes into WinUI's `ThemeDictionaries` — making them available to `Theme.Ref()` and lightweight styling throughout the app.

**Registration (in App constructor or OnLaunched, before building the visual tree):**

```csharp
AppTheme.Register(theme => theme
.Add("BrandPrimaryBrush",
light: "#005A9E",
dark: "#4FC3F7",
highContrast: "SystemColorHighlightColorBrush")
.Add("BrandSecondaryBrush",
light: "#106EBE",
dark: "#81D4FA",
highContrast: "SystemColorHotlightColorBrush")
.Add("BrandSurfaceBrush",
light: "#F0F6FF",
dark: "#1A1A2E",
highContrast: "SystemColorWindowColorBrush"));
```

**Consumption (same `Theme.Ref()` API — no new syntax):**

```csharp
// Custom resources work everywhere WinUI theme resources work
Border(child).Background(Theme.Ref("BrandPrimaryBrush"))
TextBlock("Branded heading").Foreground(Theme.Ref("BrandSecondaryBrush"))

// Works with lightweight styling
Button("Brand Action", onClick).Resources(r => r
.Set("ButtonBackground", Theme.Ref("BrandPrimaryBrush"))
.Set("ButtonBackgroundPointerOver", Theme.Ref("BrandSecondaryBrush")))
```

**Gradient brushes** are supported for surfaces that accept arbitrary `Brush` types (e.g., `Border.Background`). Most control template resource keys (e.g., `ButtonBackground`) expect `SolidColorBrush` — gradients assigned to those keys may not render correctly. In High Contrast, gradients must fall back to a solid color.

```csharp
AppTheme.Register(theme => theme
.AddGradient("BrandAccentGradientBrush",
light: new GradientDef(("0", "#7cb6e9"), ("0.5", "#335fe3"), ("1.0", "#ee9bbf")),
dark: new GradientDef(("0", "#7cb6e9"), ("0.5", "#335fe3"), ("1.0", "#ee9bbf")),
highContrast: "#48B1E9")); // Solid fallback in HC — no gradients or opacity

// Use on surfaces — not control template keys
Border(hero).Background(Theme.Ref("BrandAccentGradientBrush"))
```

**Rules:**

- **Register before building the visual tree** — call `AppTheme.Register()` in the App constructor or `OnLaunched`, before any component renders.
- **Always provide all three variants** (light, dark, highContrast). Omitting HC causes accessibility regressions.
- **HC values must reference system color brushes or solid hex colors** — no gradients, no opacity, no custom colors. Use WinUI system brush keys like `"SystemColorHighlightColorBrush"`.
- **Custom keys must end in `Brush`** for `SolidColorBrush` resources — matches WinUI naming conventions and ensures `Theme.Ref()` resolves them correctly.
- **Don't re-register WinUI built-in keys** — use `Theme.*` tokens or `Theme.Ref()` for existing WinUI resources. `AppTheme.Register()` is for app-defined resources only.
- **Duplicate key behavior** — registering the same key twice throws at startup. Libraries and app code must coordinate key names to avoid collisions.

See [theme-aware-resources.md](design-docs/theme-aware-resources.md) for the full pattern with examples.

#### Per-Subtree Theme Override

Force a subtree to a specific theme variant:
Expand Down Expand Up @@ -488,7 +547,7 @@ element.Margin(left: 4, top: 8, right: 16, bottom: 24)

#### Corner Radius

Use system values — `ControlCornerRadius` (4px) for controls and `OverlayCornerRadius` (8px) for overlays.
Use system values — `ControlCornerRadius` (4px) for controls and `OverlayCornerRadius` (8px) for overlays. Setting `CornerRadius` with hardcoded number values is not recommended; always use `ControlCornerRadius` and `OverlayCornerRadius` theme resources instead.

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:

Expand All @@ -501,16 +560,14 @@ var overlayRadius = ThemeResource.CornerRadius("OverlayCornerRadius");
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)
// Not recommended: hardcoded number values
// Border(child).CornerRadius(4)
// Border(child).CornerRadius(8)
// Border(child).CornerRadius(8, 8, 0, 0)

// Wrong: non-standard radii (even if from Figma)
Border(child).CornerRadius(3)
Border(child).CornerRadius(6)
// 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`:
Expand Down
Loading
Loading