HTML / Image → Power Apps Canvas YAML
Turn a mockup into a .pa.yaml file that pastes cleanly into Power Apps Studio ("Paste code" on a
blank screen). This skill is built to work even on a small/low-effort model: follow the workflow in
order, fill in the templates, and only use controls listed in the catalog. Don't improvise schema.
Why a whole skill for "just YAML"
.pa.yaml looks like ordinary YAML but the schema is narrow, version-pinned, and full of naming
quirks that you cannot derive from web/HTML intuition:
Label has no prefix, but Classic/Button has a Classic/ prefix.
Icon and Classic/Icon are different controls with different capabilities.
- Every property value must start with
=, and commas inside RGBA(...) break flow-style YAML.
Guessing an unconfirmed control type or property is what makes a paste fail. The whole method is:
use only what's confirmed, label every guess honestly, and let the user paste-test small pieces
before trusting them. Cleverness is not required; discipline is.
The workflow (do these in order)
- Read
references/confirmed-controls.md in full. It is the source of truth for which controls
and properties actually work. Re-read it fresh each time — the user may have added confirmed
entries after a Studio test.
- Open the closest example in
assets/examples/ and copy its structure (see the picker below).
- Decompose the mockup into controls, pulling real values (text, hex colors, sizes, X/Y).
- Write the full
.pa.yaml using the templates in this file. Tag anything unconfirmed.
- Hand the whole file to the user to paste, plus any isolated test snippet for unconfirmed bits.
- Read back the Studio result (error, warning, or screenshot) and fix only what broke.
- If a Studio test confirms something new, record it in
confirmed-controls.md.
Pick the closest example (do this before writing)
| The mockup is mostly… |
Start from |
It shows the pattern for |
| A left nav / sidebar + top bar + page shell |
assets/examples/example-app-shell.yaml |
Sidebar buttons, active indicator, icons, Navigate(), top bar |
| A multi-section form (inputs + dropdowns in cards) |
assets/examples/example-form.yaml |
Card containers, label+input pairs, TextInput, DropDown, tall scrolling screen |
| A grid/list of selectable cards (checklist, tiles) |
assets/examples/example-card-grid.yaml |
Repeated cards, badges, selectable tiles, footer nav buttons |
Follow the example's real naming, grouping, and coordinate math instead of inventing a new pattern.
If the mockup combines archetypes (e.g. sidebar and a form), take the shell from one example and
the inner content from another.
Control picker (mockup element → what to write)
Only these are confirmed. If the mockup needs something not in this table, see "Handling
anything unconfirmed" below — don't just pick a plausible-sounding control.
| Mockup element |
Control to use |
Notes |
| Text / heading / label |
Label@2.5.1 |
No prefix. |
| Button, and every sidebar/nav item |
Classic/Button@2.2.0 |
Nav items must be Button (clickable), never Label. |
| Card / panel / any box that holds children |
GroupContainer@1.5.0 + Variant: ManualLayout |
Real children go in a nested Children: key. |
| Colored bar / divider / badge block / active-indicator |
Rectangle@2.3.0 |
Rounded corners not verified — don't assume Radius*. |
| Image / avatar / logo image |
Image@2.2.3 |
Only Image/X/Y/Width/Height verified. |
| Icon (that needs a color) |
Classic/Icon@2.5.0 |
Use this, not plain Icon (plain Icon can't take Color). |
| Single-line text field |
Classic/TextInput@2.3.2 |
See confirmed props in the catalog. |
| Dropdown / select |
Classic/DropDown@2.3.1 |
Items: =["a","b"] (array literal). |
| Radio group |
Classic/Radio@2.3.0 |
Items: =["a","b"]. |
Slider, DatePicker, Toggle, ComboBox, Gallery, DataTable, Camera, Barcode, etc. are not verified —
propose an isolated test snippet first.
Templates (fill in the blanks)
1. Screen wrapper — always the outer shell, never a bare control
Screens:
MyScreen: # any non-empty name; keep it simple (letters/digits/_)
Properties:
Fill: =RGBA(244, 244, 246, 1)
# Height: =2232 # ONLY if the content scrolls below the viewport
Children:
- # ... controls go here ...
2. Label
- lblTitle:
Control: Label@2.5.1
Properties:
Text: ="Page title"
Color: =RGBA(24, 28, 35, 1)
Font: =Font.'Segoe UI'
FontWeight: =FontWeight.Bold # optional
Size: =26
X: =304
Y: =104
Width: =1000
Height: =40
3. Container (card / panel) with nested children
- grpCard:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
Fill: =RGBA(255, 255, 255, 1)
BorderColor: =RGBA(193, 198, 215, 1)
BorderThickness: =1
X: =304
Y: =168
Width: =1038
Height: =288
RadiusBottomLeft: =0
RadiusBottomRight: =0
RadiusTopLeft: =0
RadiusTopRight: =0
Children: # child X/Y are relative to the container
- # ... nested controls ...
4. Form field = Label + TextInput pair
- fldNameLabel:
Control: Label@2.5.1
Properties:
Text: ="Full name"
Color: =RGBA(65, 71, 84, 1)
Font: =Font.'Segoe UI'
Size: =12
X: =32
Y: =96
Width: =479
Height: =20
- fldNameInput:
Control: Classic/TextInput@2.3.2
Properties:
Default: =""
HintText: ="Enter your name"
BorderColor: =RGBA(193, 198, 215, 1)
BorderThickness: =1
Color: =RGBA(65, 71, 84, 1)
Size: =10
X: =32
Y: =120
Width: =479
Height: =48
RadiusBottomLeft: =0
RadiusBottomRight: =0
RadiusTopLeft: =0
RadiusTopRight: =0
5. Dropdown
- ddCountry:
Control: Classic/DropDown@2.3.1
Properties:
Items: =["Option A", "Option B", "Option C"]
Items.Value: =Value
BorderColor: =RGBA(193, 198, 215, 1)
BorderThickness: =1
ChevronBackground: =ColorValue("#007FFA")
Color: =RGBA(65, 71, 84, 1)
Size: =10
X: =32
Y: =208
Width: =479
Height: =48
6. Sidebar nav item = Button + Icon (+ Rectangle indicator for the active one)
- btnNavHome:
Control: Classic/Button@2.2.0
Properties:
Text: ="Home"
BorderStyle: =BorderStyle.None
Color: =RGBA(65, 71, 84, 1)
Fill: =RGBA(255, 255, 255, 1)
Font: =Font.'Segoe UI'
HoverColor: =ColorValue("#414754")
HoverFill: =RGBA(230, 232, 243, 1)
PressedColor: =ColorValue("#414754")
PressedFill: =ColorValue("#e6e8f3")
OnSelect: =Navigate('MyRequests', ScreenTransition.Fade) # target screen must exist
Size: =16
Width: =280
Height: =48
Y: =104
RadiusBottomLeft: =0
RadiusBottomRight: =0
RadiusTopLeft: =0
RadiusTopRight: =0
- icoNavHome:
Control: Classic/Icon@2.5.0
Properties:
Icon: =Icon.Home
Color: =RGBA(65, 71, 84, 1)
X: =16
Y: =116
Width: =24
Height: =24
Rules while writing (each is one line for a reason)
- Full wrapper always.
Screens: -> <name> -> Properties/Children. Never paste a bare control.
- Every value starts with
=. Text: ="hi", X: =0, Fill: =RGBA(...).
- Block-style Properties only. Never
{X: =0, Fill: =RGBA(...)} — the RGBA commas break it.
- Real nesting = a real nested
Children: key. Visual overlap in Studio does NOT nest controls.
- Sidebar/nav items are
Classic/Button@2.2.0, never Label, even if the mockup looks like plain text — nav needs to be clickable.
- Only confirmed controls/properties in the main file. Anything else gets tagged and tested (below).
OnSelect: =Navigate('Target', ...) only if Target exists. If not, use a placeholder and comment that it isn't wired up — don't invent a screen name.
- Tag every control/property you're not sure of with an inline comment:
# CONFIRMED (backed by the catalog or an example) or # UNVERIFIED — <why you guessed>. Never present a guess as confirmed.
Sensible defaults (so you don't have to re-derive them)
- Font:
=Font.'Segoe UI' · Bold: =FontWeight.Bold
- Screen bg:
=RGBA(244, 244, 246, 1) · Card bg: =RGBA(255, 255, 255, 1)
- Body text:
=RGBA(65, 71, 84, 1) · Heading text: =RGBA(24, 28, 35, 1) · Border: =RGBA(193, 198, 215, 1)
- Square corners: set all four
Radius*: =0.
- Pull hover/pressed colors from the mockup's CSS/Tailwind config, not by eyeballing a screenshot.
These are starting values from the example files — override them with the mockup's real values.
Handling anything unconfirmed
If the mockup needs a control or property not in the catalog (a Slider, a DatePicker, rounded
Rectangle corners, a one-sided border, a shadow, etc.):
- In the main file, mark it
# UNVERIFIED — <reasoning>.
- Separately give the user a tiny isolated snippet (just that one control, in a full wrapper)
labeled "paste-test this one first." Only trust it after Studio accepts it.
- If it has no confirmed Power Fx equivalent (shadows, blur, CSS transitions,
group-hover,
one-sided borders — see the catalog), say so plainly and offer the closest real substitute (e.g.
a thin Rectangle for a left border). Don't silently drop it or fake a property.
Hand off + read back results
- Send the entire file (Studio needs a full-file paste), plus any isolated test snippets as
clearly labeled separate blocks.
- When the user reports back, classify the message first using the Error-vs-warning table in the
catalog:
PA1001 / PA2108 = blocks the whole paste (must fix); PA2105 / PA2106 = non-blocking
version warning (Studio auto-substitutes — don't panic-fix it).
- If they send a screenshot, compare it point-by-point to the mockup — position, color, size,
font, border, radius — and name the specific remaining differences instead of saying "looks close."
- Fix only what's broken. A one-property fix doesn't justify rewriting the file.
Keep the catalog honest
This skill is only as good as references/confirmed-controls.md. When a Studio test in a
conversation confirms or disconfirms something, update that file (add a row, move an item from
unverified to confirmed, or fix a wrong assumption) so the next conversion benefits. Don't let
hard-won test results live only in chat history.
Pre-send checklist (run through this before replying)
1---2name: power-app-yaml3description: Converts an HTML/CSS mockup (e.g. from Google Stitch, v0, Figma exports) or a screenshot of a UI into Power Apps Canvas source code (.pa.yaml, source schema v3.0) that pastes directly into a blank screen in Power Apps Studio via "Paste code". Use whenever someone shares an HTML mockup, a UI screenshot, or a design export and wants it turned into a Power Apps screen, or asks to "convert this to pa.yaml", "make this a Power Apps screen", "turn this design into code I can paste into Power Apps", or similar — even without the exact term ".pa.yaml". Also use to fix/debug a .pa.yaml that failed to paste in Studio (PA1001 / PA2108-style errors), to add a new screen to an existing app, or to extend a screen made this way before. The skill carries a catalog of control types and properties that are confirmed to work versus merely guessed — the #1 cause of paste failures — so consult it before writing any .pa.yaml.4---56# HTML / Image → Power Apps Canvas YAML78Turn a mockup into a `.pa.yaml` file that pastes cleanly into Power Apps Studio ("Paste code" on a9blank screen). This skill is built to work even on a small/low-effort model: follow the workflow in10order, fill in the templates, and only use controls listed in the catalog. Don't improvise schema.1112## Why a whole skill for "just YAML"1314`.pa.yaml` looks like ordinary YAML but the schema is narrow, version-pinned, and full of naming15quirks that you cannot derive from web/HTML intuition:1617- `Label` has **no** prefix, but `Classic/Button` has a `Classic/` prefix.18- `Icon` and `Classic/Icon` are **different controls** with different capabilities.19- Every property value must start with `=`, and commas inside `RGBA(...)` break flow-style YAML.2021Guessing an unconfirmed control type or property is what makes a paste fail. The whole method is:22**use only what's confirmed, label every guess honestly, and let the user paste-test small pieces23before trusting them.** Cleverness is not required; discipline is.2425---2627## The workflow (do these in order)28291. **Read `references/confirmed-controls.md` in full.** It is the source of truth for which controls30 and properties actually work. Re-read it fresh each time — the user may have added confirmed31 entries after a Studio test.322. **Open the closest example** in `assets/examples/` and copy its structure (see the picker below).333. **Decompose the mockup** into controls, pulling real values (text, hex colors, sizes, X/Y).344. **Write the full `.pa.yaml`** using the templates in this file. Tag anything unconfirmed.355. **Hand the whole file to the user to paste**, plus any isolated test snippet for unconfirmed bits.366. **Read back the Studio result** (error, warning, or screenshot) and fix only what broke.377. **If a Studio test confirms something new**, record it in `confirmed-controls.md`.3839---4041## Pick the closest example (do this before writing)4243| The mockup is mostly… | Start from | It shows the pattern for |44|---|---|---|45| A left nav / sidebar + top bar + page shell | `assets/examples/example-app-shell.yaml` | Sidebar buttons, active indicator, icons, `Navigate()`, top bar |46| A multi-section form (inputs + dropdowns in cards) | `assets/examples/example-form.yaml` | Card containers, label+input pairs, `TextInput`, `DropDown`, tall scrolling screen |47| A grid/list of selectable cards (checklist, tiles) | `assets/examples/example-card-grid.yaml` | Repeated cards, badges, selectable tiles, footer nav buttons |4849Follow the example's real naming, grouping, and coordinate math instead of inventing a new pattern.50If the mockup combines archetypes (e.g. sidebar **and** a form), take the shell from one example and51the inner content from another.5253---5455## Control picker (mockup element → what to write)5657Only these are confirmed. If the mockup needs something **not** in this table, see "Handling58anything unconfirmed" below — don't just pick a plausible-sounding control.5960| Mockup element | Control to use | Notes |61|---|---|---|62| Text / heading / label | `Label@2.5.1` | No prefix. |63| Button, **and every sidebar/nav item** | `Classic/Button@2.2.0` | Nav items must be Button (clickable), never Label. |64| Card / panel / any box that holds children | `GroupContainer@1.5.0` + `Variant: ManualLayout` | Real children go in a nested `Children:` key. |65| Colored bar / divider / badge block / active-indicator | `Rectangle@2.3.0` | Rounded corners not verified — don't assume `Radius*`. |66| Image / avatar / logo image | `Image@2.2.3` | Only `Image/X/Y/Width/Height` verified. |67| Icon (that needs a color) | `Classic/Icon@2.5.0` | Use this, not plain `Icon` (plain `Icon` can't take `Color`). |68| Single-line text field | `Classic/TextInput@2.3.2` | See confirmed props in the catalog. |69| Dropdown / select | `Classic/DropDown@2.3.1` | `Items: =["a","b"]` (array literal). |70| Radio group | `Classic/Radio@2.3.0` | `Items: =["a","b"]`. |7172Slider, DatePicker, Toggle, ComboBox, Gallery, DataTable, Camera, Barcode, etc. are **not verified** —73propose an isolated test snippet first.7475---7677## Templates (fill in the blanks)7879### 1. Screen wrapper — always the outer shell, never a bare control8081```yaml82Screens:83 MyScreen: # any non-empty name; keep it simple (letters/digits/_)84 Properties:85 Fill: =RGBA(244, 244, 246, 1)86 # Height: =2232 # ONLY if the content scrolls below the viewport87 Children:88 - # ... controls go here ...89```9091### 2. Label9293```yaml94- lblTitle:95 Control: Label@2.5.196 Properties:97 Text: ="Page title"98 Color: =RGBA(24, 28, 35, 1)99 Font: =Font.'Segoe UI'100 FontWeight: =FontWeight.Bold # optional101 Size: =26102 X: =304103 Y: =104104 Width: =1000105 Height: =40106```107108### 3. Container (card / panel) with nested children109110```yaml111- grpCard:112 Control: GroupContainer@1.5.0113 Variant: ManualLayout114 Properties:115 Fill: =RGBA(255, 255, 255, 1)116 BorderColor: =RGBA(193, 198, 215, 1)117 BorderThickness: =1118 X: =304119 Y: =168120 Width: =1038121 Height: =288122 RadiusBottomLeft: =0123 RadiusBottomRight: =0124 RadiusTopLeft: =0125 RadiusTopRight: =0126 Children: # child X/Y are relative to the container127 - # ... nested controls ...128```129130### 4. Form field = Label + TextInput pair131132```yaml133- fldNameLabel:134 Control: Label@2.5.1135 Properties:136 Text: ="Full name"137 Color: =RGBA(65, 71, 84, 1)138 Font: =Font.'Segoe UI'139 Size: =12140 X: =32141 Y: =96142 Width: =479143 Height: =20144- fldNameInput:145 Control: Classic/TextInput@2.3.2146 Properties:147 Default: =""148 HintText: ="Enter your name"149 BorderColor: =RGBA(193, 198, 215, 1)150 BorderThickness: =1151 Color: =RGBA(65, 71, 84, 1)152 Size: =10153 X: =32154 Y: =120155 Width: =479156 Height: =48157 RadiusBottomLeft: =0158 RadiusBottomRight: =0159 RadiusTopLeft: =0160 RadiusTopRight: =0161```162163### 5. Dropdown164165```yaml166- ddCountry:167 Control: Classic/DropDown@2.3.1168 Properties:169 Items: =["Option A", "Option B", "Option C"]170 Items.Value: =Value171 BorderColor: =RGBA(193, 198, 215, 1)172 BorderThickness: =1173 ChevronBackground: =ColorValue("#007FFA")174 Color: =RGBA(65, 71, 84, 1)175 Size: =10176 X: =32177 Y: =208178 Width: =479179 Height: =48180```181182### 6. Sidebar nav item = Button + Icon (+ Rectangle indicator for the active one)183184```yaml185- btnNavHome:186 Control: Classic/Button@2.2.0187 Properties:188 Text: ="Home"189 BorderStyle: =BorderStyle.None190 Color: =RGBA(65, 71, 84, 1)191 Fill: =RGBA(255, 255, 255, 1)192 Font: =Font.'Segoe UI'193 HoverColor: =ColorValue("#414754")194 HoverFill: =RGBA(230, 232, 243, 1)195 PressedColor: =ColorValue("#414754")196 PressedFill: =ColorValue("#e6e8f3")197 OnSelect: =Navigate('MyRequests', ScreenTransition.Fade) # target screen must exist198 Size: =16199 Width: =280200 Height: =48201 Y: =104202 RadiusBottomLeft: =0203 RadiusBottomRight: =0204 RadiusTopLeft: =0205 RadiusTopRight: =0206- icoNavHome:207 Control: Classic/Icon@2.5.0208 Properties:209 Icon: =Icon.Home210 Color: =RGBA(65, 71, 84, 1)211 X: =16212 Y: =116213 Width: =24214 Height: =24215```216217---218219## Rules while writing (each is one line for a reason)2202211. **Full wrapper always.** `Screens: -> <name> -> Properties/Children`. Never paste a bare control.2222. **Every value starts with `=`.** `Text: ="hi"`, `X: =0`, `Fill: =RGBA(...)`.2233. **Block-style Properties only.** Never `{X: =0, Fill: =RGBA(...)}` — the RGBA commas break it.2244. **Real nesting = a real nested `Children:` key.** Visual overlap in Studio does NOT nest controls.2255. **Sidebar/nav items are `Classic/Button@2.2.0`,** never `Label`, even if the mockup looks like plain text — nav needs to be clickable.2266. **Only confirmed controls/properties in the main file.** Anything else gets tagged and tested (below).2277. **`OnSelect: =Navigate('Target', ...)` only if `Target` exists.** If not, use a placeholder and comment that it isn't wired up — don't invent a screen name.2288. **Tag every control/property you're not sure of** with an inline comment: `# CONFIRMED` (backed by the catalog or an example) or `# UNVERIFIED — <why you guessed>`. Never present a guess as confirmed.229230### Sensible defaults (so you don't have to re-derive them)231232- Font: `=Font.'Segoe UI'` · Bold: `=FontWeight.Bold`233- Screen bg: `=RGBA(244, 244, 246, 1)` · Card bg: `=RGBA(255, 255, 255, 1)`234- Body text: `=RGBA(65, 71, 84, 1)` · Heading text: `=RGBA(24, 28, 35, 1)` · Border: `=RGBA(193, 198, 215, 1)`235- Square corners: set all four `Radius*: =0`.236- Pull hover/pressed colors from the mockup's CSS/Tailwind config, not by eyeballing a screenshot.237238These are starting values from the example files — override them with the mockup's real values.239240---241242## Handling anything unconfirmed243244If the mockup needs a control or property not in the catalog (a Slider, a DatePicker, rounded245`Rectangle` corners, a one-sided border, a shadow, etc.):2462471. In the main file, mark it `# UNVERIFIED — <reasoning>`.2482. **Separately** give the user a tiny isolated snippet (just that one control, in a full wrapper)249 labeled **"paste-test this one first."** Only trust it after Studio accepts it.2503. If it has **no** confirmed Power Fx equivalent (shadows, blur, CSS transitions, `group-hover`,251 one-sided borders — see the catalog), say so plainly and offer the closest real substitute (e.g.252 a thin `Rectangle` for a left border). Don't silently drop it or fake a property.253254---255256## Hand off + read back results257258- **Send the entire file** (Studio needs a full-file paste), plus any isolated test snippets as259 clearly labeled separate blocks.260- When the user reports back, **classify the message first** using the Error-vs-warning table in the261 catalog: `PA1001` / `PA2108` = blocks the whole paste (must fix); `PA2105` / `PA2106` = non-blocking262 version warning (Studio auto-substitutes — don't panic-fix it).263- **If they send a screenshot,** compare it point-by-point to the mockup — position, color, size,264 font, border, radius — and name the specific remaining differences instead of saying "looks close."265- **Fix only what's broken.** A one-property fix doesn't justify rewriting the file.266267## Keep the catalog honest268269This skill is only as good as `references/confirmed-controls.md`. When a Studio test in a270conversation confirms or disconfirms something, update that file (add a row, move an item from271unverified to confirmed, or fix a wrong assumption) so the next conversion benefits. Don't let272hard-won test results live only in chat history.273274## Pre-send checklist (run through this before replying)275276- [ ] Full `Screens: -> name -> Children` wrapper present277- [ ] Every property value starts with `=`278- [ ] Properties are block-style (no `{ }` with commas)279- [ ] Every control type/version is from the catalog (or tagged `# UNVERIFIED` + given a test snippet)280- [ ] Nested controls sit inside a real `Children:` key281- [ ] Sidebar/nav items are `Classic/Button`, not `Label`282- [ ] Every `Navigate()` target is a screen that exists (or a commented placeholder)283- [ ] Unconfirmed items are flagged and, where impossible, called out with a substitute