# Guia de Conversao HTML para Blazor

> **Scope:** blazor-azure
> **Layer:** 2 (on keyword)
> **Keywords:** blazor, html, conversion, prototype, migrate
> **Load When:** html conversion keywords detected

## Element Mapping

### Basic Elements

| HTML | Blazor (Fluent UI) |
|------|---------------------|
| `<button class="btn-primary">` | `<FluentButton Appearance="Appearance.Accent">` |
| `<button class="btn-secondary">` | `<FluentButton Appearance="Appearance.Neutral">` |
| `<input type="text">` | `<FluentTextField @bind-Value="Model.Field">` |
| `<input type="email">` | `<FluentTextField Type="TextFieldType.Email">` |
| `<input type="password">` | `<FluentTextField Type="TextFieldType.Password">` |
| `<textarea>` | `<FluentTextArea @bind-Value="Model.Field">` |
| `<select>` | `<FluentSelect @bind-Value="Model.Field">` |
| `<input type="checkbox">` | `<FluentCheckbox @bind-Value="Model.Flag">` |
| `<input type="radio">` | `<FluentRadioGroup @bind-Value="Model.Option">` |

### Containers & Layout

| HTML | Blazor |
|------|--------|
| `<div class="card">` | `<FluentCard>` |
| `<div class="flex">` | `<FluentStack Orientation="Horizontal">` |
| `<div class="flex-col">` | `<FluentStack Orientation="Vertical">` |
| `<div class="grid">` | `<FluentGrid>` |
| `<div class="modal">` | Custom HTML or `<FluentDialog>` |

### Feedback & Data

| HTML | Blazor |
|------|--------|
| `<div class="alert-success">` | `<FluentMessageBar Intent="MessageIntent.Success">` |
| `<div class="alert-error">` | `<FluentMessageBar Intent="MessageIntent.Error">` |
| `<div class="toast">` | `ToastService.ShowSuccess("msg")` |
| `<div class="spinner">` | `<FluentProgress />` |
| `<table>` | `<FluentDataGrid Items="@items">` |
| `<th>` | `<PropertyColumn Property="@(x => x.Prop)">` |
| `<td>` with actions | `<TemplateColumn>` |

---

## Page Structure

```razor
@page "/pagename"
@rendermode InteractiveServer
@inject IMyService _service

<PageTitle>App - Pagina</PageTitle>

<div class="page-layout">
    <header class="page-header-nav"><!-- Nav --></header>
    <div class="page-content container">
        <h1>Titulo</h1>
        <!-- Content -->
    </div>
</div>

@code {
    private bool _isLoading;
    private string? _error;
    protected override async Task OnInitializedAsync() => await LoadDataAsync();
    private async Task HandleClick() { }
    private string FormatValue(decimal value) => value.ToString("C");
}
```

---

## Event Conversion

| HTML Event | Blazor Pattern |
|------------|---------------|
| `onclick="fn()"` | `@onclick="HandleClick"` (sync) or `@onclick="HandleClickAsync"` (async) |
| `onchange="fn(val)"` | `@bind-Value="_val"` (two-way) or `@onchange="HandleChange"` (one-way) |
| `<form onsubmit>` | `<EditForm Model="@_model" OnValidSubmit="HandleSubmit">` with `<DataAnnotationsValidator />` |

### Form Example

```razor
<EditForm Model="@_model" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />
    <FluentTextField @bind-Value="_model.Email" Label="Email" Required />
    <FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">Enviar</FluentButton>
</EditForm>

@code {
    private MyModel _model = new();
    private async Task HandleSubmit() => await _service.SaveAsync(_model);
    public class MyModel { [Required][EmailAddress] public string Email { get; set; } = ""; }
}
```

---

## Modal Conversion

**Use custom HTML** when: custom design, backdrop blur, complex animations.
**Use `<FluentDialog>`** for simple confirmations.

### Custom Modal

```razor
@if (!_modalHidden)
{
    <div class="modal-overlay" @onclick="CloseModal">
        <div class="modal-container animate-scaleIn" @onclick:stopPropagation="true">
            <div class="modal-header">
                <h3>Titulo</h3>
                <button class="modal-close" @onclick="CloseModal">
                    <FluentIcon Value="@(new Icons.Regular.Size20.Dismiss())" />
                </button>
            </div>
            <div class="modal-body">Conteudo...</div>
            <div class="modal-footer">
                <FluentButton Appearance="Appearance.Neutral" @onclick="CloseModal">Cancelar</FluentButton>
                <FluentButton Appearance="Appearance.Accent" @onclick="Confirm">Confirmar</FluentButton>
            </div>
        </div>
    </div>
}
@code {
    private bool _modalHidden = true;
    private void OpenModal() => _modalHidden = false;
    private void CloseModal() => _modalHidden = true;
    private async Task Confirm() { /* logic */ CloseModal(); }
}
```

---

## Style Interpolation

```razor
<!-- Static: same as HTML -->
<div style="color: red; margin-top: 20px;">

<!-- Dynamic: ALWAYS use @($"...") -->
<div style="@($"color: {_color}; margin-top: {_margin}px;")">

<!-- WRONG: causes RZ9986 -->
<FluentStack Style="animation-delay: @(0.05 * index)s;">
<!-- CORRECT -->
<FluentStack Style="@($"animation-delay: {0.05 * index}s;")">
```

## Loops & Conditionals

```razor
<!-- Loop -->
@foreach (var item in _items) { <li>@item.Name</li> }

<!-- Loop with stagger animation -->
@for (int i = 0; i < _items.Count; i++)
{
    var index = i; // capture for closure
    <li class="animate-slideInUp" style="@($"animation-delay: {0.05 * index}s;")">@_items[index].Name</li>
}

<!-- Conditional rendering (replaces display:none toggle) -->
@if (_isLoading) { <FluentProgress /> }
else if (_error != null) { <FluentMessageBar Intent="MessageIntent.Error">@_error</FluentMessageBar> }
else { <FluentDataGrid Items="@_items"><!-- columns --></FluentDataGrid> }
```

## Checklist

- [ ] Page structure: `@page`, `@rendermode`, `@inject`
- [ ] HTML elements mapped to Fluent UI components
- [ ] Events: `onclick` → `@onclick`, forms → `EditForm`
- [ ] Modals: custom vs `FluentDialog` decided
- [ ] Style interpolation uses `@($"...")`
- [ ] Loops use `var index = i` for closures
- [ ] CSS classes validated with `morph-spec validate-css`

---

*MORPH-SPEC by Polymorphism Tech*
