# Powerapps YAML

> Genera código YAML pasteable en Power Apps Studio usando el schema pa.yaml v3. Incluye controles modernos, patrones de caché, Gallery con colecciones, y todas las lecciones aprendidas para vibe codear sin errores. Trigger: Cuando el usuario pida crear pantallas, controles o código para Power Apps en formato YAML.

- Skill: `rohbunny01/powerapps-yaml-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add rohbunny01/powerapps-yaml-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rohbunny01/powerapps-yaml-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: rohbunny01 (https://skillmd.com/u/rohbunny01)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/rohbunny01/powerapps-yaml-2

---


# Power Apps YAML — Guía Completa para Vibe Coding

## Cuándo usar

- Al generar código YAML para pegar en Power Apps Studio
- Al crear o modificar pantallas de Power Apps
- Al convertir diseños a controles Power Apps modernos
- Al trabajar con SharePoint como backend

---

## 🔑 Estructura Base (pa.yaml Schema v3)

La estructura **OBLIGATORIA** para que Power Apps Studio acepte el YAML via "Paste code":

```yaml
Screens:
    NombrePantalla:
        Properties:
            Fill: =SurfaceAlt
            OnVisible: |-
                =Set(varEjemplo, "hola")
        Children:
            - NombreControl:
                Control: TipoControl
                Variant: VarianteOpcional
                Properties:
                    X: =0
                    Y: =0
                    Width: =Parent.Width
                    Height: =100
                Children:
                    - ControlHijo:
                        Control: Text
                        Properties:
                            Text: ="Hola mundo"
```

### Reglas de estructura

1. **Top-level**: Siempre empieza con `Screens:`
2. **Screen name**: Debe coincidir EXACTAMENTE con el nombre de la pantalla en Power Apps Studio
3. **No usar `Control: Screen`**: Las pantallas se definen directamente bajo `Screens:`, SIN propiedad `Control`
4. **Children es un array**: Cada hijo comienza con `- NombreControl:`
5. **Control y Variant**: Se definen dentro de cada hijo, NO en la pantalla
6. **Valores con `=`**: TODAS las propiedades llevan `=` al inicio: `Text: ="Hola"`, `X: =0`

---

## 🚨 Propiedades de Controles Modernos (CRÍTICO)

Power Apps usa controles **modernos** que tienen nombres DIFERENTES a los clásicos. Usar los nombres incorrectos produce error `PA2108: Unknown property`.

### Control `Text` (Label)

| ❌ Clásico (NO usar) | ✅ Moderno (USAR) |
|----------------------|-------------------|
| `Color` | `FontColor` |
| `FontWeight` | `Weight` |

```yaml
- lblEjemplo:
    Control: Text
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =40
        Text: ="Hola"
        FontColor: =RGBA(0, 0, 0, 1)
        Size: =14
        Weight: =FontWeight.Bold
        Align: =Align.Center
        VerticalAlign: =VerticalAlign.Middle
```

### Control `Button`

| ❌ Clásico (NO usar) | ✅ Moderno (USAR) |
|----------------------|-------------------|
| `Fill` | `BasePaletteColor` |
| `Color` | `FontColor` |
| `Size` | `FontSize` |
| `RadiusTopLeft/Right/etc` | _(no existe, usa Fluent theme)_ |

```yaml
- btnEjemplo:
    Control: Button
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =44
        Text: ="Click"
        BasePaletteColor: =BrandAccent
        FontColor: =TextOnAccent
        FontSize: =14
        FontWeight: =FontWeight.Semibold
        Appearance: ='ButtonCanvas.Appearance'.Primary
        OnSelect: =Notify("Clickeado")
```

**Valores de Appearance:**
- `'ButtonCanvas.Appearance'.Primary` — botón principal
- `'ButtonCanvas.Appearance'.Secondary` — botón secundario
- `'ButtonCanvas.Appearance'.Transparent` — transparente (ideal para overlays y back buttons)

### Control `TextInput`

| ❌ Clásico (NO usar) | ✅ Moderno (USAR) |
|----------------------|-------------------|
| `Default` | `Value` |
| `HintText` / `PlaceholderText` | _(no existe en moderno)_ |
| `Format` | _(no existe en moderno)_ |

```yaml
- txtEjemplo:
    Control: TextInput
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =44
        Value: =""
        Mode: =TextMode.MultiLine  # Opcional
```

### Control `DatePicker`

| ❌ Clásico (NO usar) | ✅ Moderno (USAR) |
|----------------------|-------------------|
| `DefaultDate` | `SelectedDate` |

```yaml
- dpEjemplo:
    Control: DatePicker
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =44
        SelectedDate: =Today()
```

### Control `Dropdown`

```yaml
- ddEjemplo:
    Control: Dropdown
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =44
        Items: =["Opción 1", "Opción 2", "Opción 3"]
        DefaultSelectedItems: =["Opción 1"]
        OnChange: =Set(varSeleccion, Self.Selected.Value)
```

**Para items numéricos:**
```yaml
Items: =Sequence(24, 0)              # 0 a 23
Items: =[0, 5, 10, 15, 20, 25, 30]   # Lista explícita
```

### Control `GroupContainer`

| ❌ NO soportado | ✅ Alternativa |
|-----------------|---------------|
| `OnSelect` | Usar un Button overlay transparente dentro del container |
| `RadiusTopLeft/Right/etc` | _(no existe en modernos)_ |

```yaml
- containerEjemplo:
    Control: GroupContainer
    Variant: ManualLayout
    Properties:
        X: =0
        Y: =0
        Width: =200
        Height: =100
        Fill: =Surface
        DropShadow: =DropShadow.Light
```

**Para hacer un container clickeable:**
```yaml
Children:
    - lblContenido:
        Control: Text
        Properties:
            Text: ="Mi contenido"
    - btnOverlay:
        Control: Button
        Properties:
            X: =0
            Y: =0
            Width: =Parent.Width
            Height: =Parent.Height
            Text: =""
            Appearance: ='ButtonCanvas.Appearance'.Transparent
            OnSelect: =Navigate(OtraPantalla, ScreenTransition.None)
```

---

## 📊 Gallery — Reglas Críticas

### Regla #1: Variant es OBLIGATORIO
```yaml
- galEjemplo:
    Control: Gallery
    Variant: BrowseLayout_Flexible_SocialFeed_ver5.0  # OBLIGATORIO
```

**Error si falta:** `PA1011: The keyword 'Variant' is required but is missing or empty`

### Regla #2: Items de SharePoint directo → funciona bien
```yaml
Items: =MiListaSP
```

### Regla #3: Items de colección → usar ForAll con esquema explícito

⚠️ **PROBLEMA**: Cuando `Items` apunta a una colección creada con `GroupBy + AddColumns`, los controles dentro de la gallery muestran errores ❌ porque Power Apps no puede inferir el esquema.

✅ **SOLUCIÓN**: Usar `ForAll` con registros de forma explícita `{campo: valor}`:

```yaml
OnVisible: |-
    =ClearCollect(
        colRanking,
        ForAll(
            GroupBy(colRegistros, Title, Email, Registros),
            {
                Nombre: ThisRecord.Title,
                EmailUsuario: ThisRecord.Email,
                Puntos: Sum(Registros, Puntos),
                Sesiones: CountRows(Registros)
            }
        )
    )
```

### Regla #4: GroupBy — NO usar comillas en columnas
```
# ❌ MAL — "Registros" se interpreta como texto
GroupBy(tabla, "Title", "Email", "Registros")

# ✅ BIEN — identificadores sin comillas
GroupBy(tabla, Title, Email, Registros)
```

### Regla #5: No agrupar por columnas que cambian
Si un usuario puede cambiar de equipo, NO agrupar por `Equipo` porque aparecerá duplicado. Tomar el equipo del último registro:
```
EquipoUsuario: Last(Registros).Equipo
```

### Regla #6: Acceder a tabla agrupada dentro de ForAll
```
# ❌ MAL — ThisRecord.Registros no resuelve
Sum(ThisRecord.Registros, Puntos)

# ✅ BIEN — acceso directo a la columna de grupo
Sum(Registros, Puntos)
```

### Template completo para Gallery con colección:
```yaml
- galRanking:
    Control: Gallery
    Variant: BrowseLayout_Flexible_SocialFeed_ver5.0
    Properties:
        X: =16
        Y: =188
        Width: =Parent.Width - 32
        Height: =Parent.Height - 204
        Items: =SortByColumns(colRanking, "Puntos", SortOrder.Descending)
        TemplatePadding: =6
        TemplateSize: =80
        Fill: =RGBA(0, 0, 0, 0)
    Children:
        - CardItem:
            Control: GroupContainer
            Variant: ManualLayout
            Properties:
                Width: =Parent.Width
                Height: =74
                Fill: =Surface
                DropShadow: =DropShadow.Light
            Children:
                - lblNombre:
                    Control: Text
                    Properties:
                        X: =16
                        Y: =12
                        Width: =Parent.Width - 140
                        Height: =24
                        Text: =ThisItem.Nombre
```

---

## 🚀 Patrón de Caché Local (Optimización)

### Problema
Cada pantalla hace queries independientes a SharePoint → lento y redundante.

### Solución: Centro de caché en Screen_Inicio

```yaml
# Screen_Inicio.OnVisible — carga datos UNA VEZ
OnVisible: |-
    =Set(varUsuarioActual, User().FullName);
    Set(varEmailActual, User().Email);
    ClearCollect(
        colRegistrosMes,
        Filter(
            MiListaSP,
            Month(Fecha) = Month(Today()),
            Year(Fecha) = Year(Today())
        )
    );
    Set(varMisRegistros, Filter(colRegistrosMes, Email = varEmailActual));
    Set(varMiEquipo, LookUp(Miembros, Email = varEmailActual, Equipo))
```

### Reutilizar en otras pantallas

```yaml
# Screen_Ranking — usa colRegistrosMes, NO re-consulta SharePoint
OnVisible: |-
    =ClearCollect(
        colRanking,
        ForAll(
            GroupBy(colRegistrosMes, Title, Email, Registros),
            { Nombre: ThisRecord.Title, Puntos: Sum(Registros, Puntos) }
        )
    )

# Screen_Equipos — usa varMiEquipo ya cacheado
OnVisible: |-
    =Set(varVistaEquipos, "Lista")
    # varMiEquipo ya está cargado desde Screen_Inicio
```

### Después de guardar un registro — actualizar caché local

```yaml
# En btnGuardar.OnSelect, después del Patch:
Patch(MiListaSP, Defaults(MiListaSP), { ... });
Collect(colRegistrosMes, { ...mismo registro... });
Navigate(Screen_Inicio, ScreenTransition.None)
```

**Resultado**: ~7 queries SP → **2 queries** (1 ClearCollect + 1 LookUp)

---

## 🎨 Colores del Tema (App.Formulas)

Definir paleta global en `App > Formulas`:
```
BrandPrimary = RGBA(30, 41, 59, 1);
BrandAccent = RGBA(255, 107, 53, 1);
BrandAccentLight = RGBA(255, 107, 53, 0.08);
Surface = RGBA(255, 255, 255, 1);
SurfaceAlt = RGBA(245, 247, 252, 1);
TextPrimary = RGBA(15, 23, 42, 1);
TextSecondary = RGBA(100, 116, 139, 1);
TextOnAccent = RGBA(255, 255, 255, 1);
StatusSuccess = RGBA(16, 185, 129, 1);
StatusWarning = RGBA(234, 179, 8, 1);
StatusError = RGBA(239, 68, 68, 1);
```

---

## ⏰ Patrón: Selector de Hora con Dropdowns

El `DatePicker` solo selecciona fecha. Para hora, usar pares de Dropdowns:

```yaml
# Horas (0-23)
- ddHoraH:
    Control: Dropdown
    Properties:
        Items: =Sequence(24, 0)
        DefaultSelectedItems: =[8]

# Separador ":"
- lblSep:
    Control: Text
    Properties:
        Text: =":"
        Weight: =FontWeight.Bold

# Minutos (intervalos de 5)
- ddHoraM:
    Control: Dropdown
    Properties:
        Items: =[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
        DefaultSelectedItems: =[0]
```

### Construir DateTime desde dropdowns:
```
DateAdd(
    DateAdd(Today(), Value(ddHoraH.Selected.Value), TimeUnit.Hours),
    Value(ddHoraM.Selected.Value),
    TimeUnit.Minutes
)
```

### Calcular duración:
```
With(
    {
        mins: (Value(ddFinH.Selected.Value) * 60 + Value(ddFinM.Selected.Value)) -
              (Value(ddInicioH.Selected.Value) * 60 + Value(ddInicioM.Selected.Value))
    },
    If(mins > 0, Text(RoundDown(mins / 60, 0)) & "h " & Text(Mod(mins, 60)) & "min", "⚠️ Error")
)
```

---

## 📋 Proceso de Paste Code

1. **Crear la pantalla** manualmente en Power Apps Studio
2. **Asegurar que el nombre** coincide con el definido en el YAML
3. **Copiar TODO** el contenido del archivo YAML (incluyendo `Screens:`)
4. **Seleccionar la pantalla** en el Tree View
5. **Pegar** con Ctrl+V o Click derecho → Paste code
6. Si hay errores de design-time por colecciones en `OnVisible`, ejecutar (Play ▶️) para que se creen

---

## ⚠️ Tabla de errores comunes

| Error | Causa | Solución |
|-------|-------|----------|
| `PA2108: Unknown property 'Color'` | Propiedad clásica | Usar `FontColor` |
| `PA2108: Unknown property 'FontWeight'` en Text | Propiedad clásica | Usar `Weight` |
| `PA2108: Unknown property 'Fill'` en Button | Propiedad clásica | Usar `BasePaletteColor` |
| `PA2108: Unknown property 'Default'` en TextInput | Propiedad clásica | Usar `Value` |
| `PA2108: Unknown property 'DefaultDate'` | Propiedad clásica | Usar `SelectedDate` |
| `PA2108: Unknown property 'OnSelect'` en GroupContainer | No soportado | Button overlay transparente |
| `PA1011: 'Variant' is required` en Gallery | Falta Variant | Agregar `Variant: BrowseLayout_Flexible_SocialFeed_ver5.0` |
| `PA2109: Unknown variant 'X'` en Gallery | Variant inválido | Usar `BrowseLayout_Flexible_SocialFeed_ver5.0` |
| `YamlInvalidSyntax: PaModule` | Estructura incorrecta | Usar `Screens:` como top-level |
| Gallery con ❌ en controles | Colección sin esquema inferible | Usar `ForAll` + registros explícitos |
| Nombre duplicado en ranking | GroupBy por columna variable | No agrupar por campos que cambian |
| `"Registros" error: text` | Comillas en GroupBy | Quitar comillas: `GroupBy(t, Col, Grupo)` |

---

## 🧩 Mapeo rápido: Clásico → Moderno

| Control | Propiedad Clásica | Propiedad Moderna |
|---------|-------------------|-------------------|
| Text | `Color` | `FontColor` |
| Text | `FontWeight` | `Weight` |
| Button | `Fill` | `BasePaletteColor` |
| Button | `Color` | `FontColor` |
| Button | `Size` | `FontSize` |
| TextInput | `Default` | `Value` |
| TextInput | `HintText` | _(no existe)_ |
| DatePicker | `DefaultDate` | `SelectedDate` |
| GroupContainer | `OnSelect` | _(no existe)_ |
| Todos | `RadiusTopLeft` etc. | _(no existe)_ |

---

## 💡 Tips Power Fx

- **Separadores**: Usar **comas** (`,`) como separadores — la config regional del entorno define si Power Apps espera `,` o `;`
- **Strings multilínea**: Usar `|-` en YAML seguido de la fórmula con `=` al inicio
- **SharePoint Choice columns**: Al hacer Patch, usar `{Value: "texto"}` para columnas de tipo Choice
- **SharePoint list names**: Verificar el nombre EXACTO de la lista (con números si hay duplicados, ej: `MiLista1`)
- **Colecciones tipadas**: Siempre usar `ForAll({campo: valor})` en vez de `AddColumns` para colecciones que alimentan Gallery
- **Variables globales**: Cachear datos compartidos en `Screen_Inicio.OnVisible` y reutilizar en otras pantallas

