# Skill: Write Waltzing Template

You are helping a user write a Waltzing template (.wtz file).

## CRITICAL: Common Mistakes to Avoid

Before writing any template code, review these frequent errors:

| Mistake | Wrong | Correct |
|---------|-------|---------|
| Quoted expressions | `class="@var"` | `class=@var` |
| Alpine.js x-data | `x-data="{ count: 0 }"` | `x-data=@```json { count: 0 } ```@` |
| Full enum path | `variant=@Variant::Primary` | `variant=@::Primary` (inferred) |

**Note:** Binary operators in `@let` now work directly: `@let sum = a + b` ✓

**Tolerant parsing:** `@` is optional in expression context - `@let x = @foo` and `[@a, @b]` now work but the idiomatic style omits `@`. Special keywords still require `@`: `@out`, `@default`, `@{ }`, `@()`.

**The key insight**: `@` enters expression mode. After `@let x =`, you're already IN expression mode - use plain Rust syntax, no more `@` prefixes.

## Template Structure

A typical Waltzing template has this structure:

```waltzing
@* Imports *@
@use crate::models::User
@import "layouts/base.wtz" as layout

@* Optional struct/enum definitions *@
@struct PageData {
    title: String,
    users: Vec<User>,
}

@* Main template function *@
@fn apply(data: PageData) {
    <@layout title=@data.title>
        <h1>@data.title</h1>

        @for user in data.users {
            <div class="user">
                <h2>@user.name</h2>
                @if let Some(email) = user.email {
                    <a href="mailto:@email">@email</a>
                }
            </div>
        }
    </@>
}
```

## Key Rules

1. **Dynamic attribute values**: Do NOT use quotes around expressions
   ```waltzing
   @* ❌ WRONG - quotes make it literal text *@
   <div class="@cn(&[base, extra])" />
   @* Output: class="@cn(&[base, extra])" *@

   @* ✅ CORRECT - no quotes, expression evaluated *@
   <div class=@cn(&[base, extra]) />
   @* Output: class="btn primary" *@
   ```

2. **Variables**: Use `@` prefix for variables and expressions
   - Simple: `@name`
   - Complex (operators): `@(count + 1)`

3. **Let bindings**: Operators work directly (greedy parsing)
   - `@let sum = a + b` (operators work without parentheses)
   - `@let valid = x > 0 && y < 10` (boolean operators too)

4. **Array literals**: Use `[...]` syntax (compiles to `vec![]`)
   ```waltzing
   @let classes = ["btn", "btn-primary", extra_class]
   <div class=@cn(&classes)>
   ```

5. **Struct literals**: Use standard Rust syntax with any expression as field values
   ```waltzing
   @let point = Point { x: 10, y: 20 }
   @let user = User {
       name: input.trim().to_string(),
       active: items.len() > 0,
       role: if is_admin { Role::Admin } else { Role::User }
   }
   @let config = Config { debug: true, ..Default::default() }
   ```
   For JS-style objects, use embedded JSON: `@```json { "key": @value } ```@`

6. **Statement blocks**: Use `{ ... }` for Rust statements in expressions
   ```waltzing
   @let result = { let x = 1; x * 2 }
   ```

7. **Template blocks**: Use `@{ ... }` for template content in expressions
   ```waltzing
   @let content = @{ <div>Hello</div> }
   ```

8. **Inline callbacks vs Content**: Don't confuse these!
   - `@() { ... }` - Inline render callback (callable)
   - `@{ ... }` - Template block producing Content (NOT callable)
   ```waltzing
   @let cb = @() { <div>Hello</div> }  @* Callback - can call with @cb() *@
   @let content = @{ <div>Hello</div> }  @* Content - just use @content *@
   ```

9. **Control flow**: Use `@if`, `@for`, `@match` with braces. Both `else` and `@else` work.
   ```waltzing
   @if condition {
       content
   } else {
       other
   }
   @* Or with @ prefix: } @else { *@
   ```

10. **Components**: Use `<@module::function />` syntax
    ```waltzing
    <@button::primary label="Click" />
    ```

11. **Shorthand**: `<@template>` equals `<@template::apply>`
    ```waltzing
    @* These are equivalent: *@
    <@layout::apply title="Home">content</@layout::apply>
    <@layout title="Home">content</@layout>
    ```

12. **Comments**: Use `@* comment *@` (not in output) or `<!-- -->` (in output)
    - Comments work in `<script>` and `<style>` tags too!

13. **Safe output**: Use `@safe(html)` for trusted HTML content

14. **Higher-order functions**: Use `@render(T)` type and `@out` reference
    ```waltzing
    @fn render(callback: @render()) {
        @callback(@out)
    }
    ```
    Note: `out` is reserved - use `@(out)` for variables named "out"

## Common Patterns

### List with conditional classes
```waltzing
<ul>
    @for (i, item) in items.iter().enumerate() {
        <li class="@if i == 0 { first }">@item</li>
    }
</ul>
```

### Form with errors
```waltzing
<input
    type="email"
    value="@form.email"
    @if errors.contains_key("email") { class="error" }
/>
```

### Alpine.js integration
```waltzing
<div x-data=@```json {
    count: @initial,
    increment() { this.count++ }
} ```@>
    <button @click="increment">Count: <span x-text="count"></span></button>
</div>
```

### Dark mode initialization with comment
```waltzing
<script>
    @* Initialize theme based on user preference *@
    if (localStorage.theme === 'dark' || window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.documentElement.classList.add('dark');
    }
</script>
```

### Dynamic CSS with theme
```waltzing
<style>
@```css
@* Theme variables *@
.theme-@theme_name {
    --primary: @theme.primary;
    --secondary: @theme.secondary;
}

@if dark_mode {
    .dark { background: #1a1a1a; color: #fff; }
}
```@
</style>
```

### Table with custom row renderer
```waltzing
@fn data_table(items: Vec<Item>, row_renderer: @render(&Item)) {
    <table>
        <tbody>
            @for item in items.iter() {
                <tr>@row_renderer(item, @out)</tr>
            }
        </tbody>
    </table>
}
```

### Layout with pluggable sections
```waltzing
@fn custom_layout(header: @render(), footer: @render(), content: Content) {
    <header>@header(@out)</header>
    <main>@safe(content.render())</main>
    <footer>@footer(@out)</footer>
}
```

## Built-in Helpers

Waltzing provides 40+ tree-shaken helper functions. Use them directly - they're auto-imported.

### Most Useful Helpers

```waltzing
@* Classes (like shadcn's cn()) *@
<div class="@cn(&["btn", "btn-primary", if active { "active" } else { "" }])">

@* Text formatting *@
@truncate(&description, 100)        @* "Long text..." *@
@slugify(&title)                    @* "hello-world" *@
@highlight(&text, search_query)     @* <mark>matched</mark> *@

@* Numbers & currency *@
@number(count)                      @* "1,234,567" *@
@currency(price, "USD")             @* "$1,234.50" *@
@percent(ratio)                     @* "15.6%" *@
@bytes(file_size)                   @* "1.5 MB" *@

@* Pluralization *@
@pluralize(count, "item")           @* "1 item" or "5 items" *@

@* Conditional classes/attributes *@
@class_if(is_selected, "selected")
@class_toggle(is_dark, "dark", "light")
@aria_if(expanded, "expanded", "true")

@* Time *@
@relative_time(seconds_ago)         @* "2 hours ago" *@

@* Unique IDs for form labels *@
@let input_id = unique_id("email")
<label for="@input_id">Email</label>
<input id="@input_id" />
```

### All Categories

- **Security:** `safe_url`, `json`
- **Text:** `capitalize`, `titlecase`, `truncate`, `slugify`, `kebab_case`, `camel_case`, `snake_case`, `initials`, `strip_tags`, `excerpt`, `highlight`, `mask`, `linkify`, `nl2br`
- **Numbers:** `number`, `number_with`, `ordinal`, `bytes`, `clamp`, `percentage`
- **Formatting:** `currency`, `percent`, `format_list`, `format_date`, `format_time`
- **Pluralization:** `pluralize`, `pluralize_with`, `plural_word`
- **HTML/CSS:** `cn`, `class_if`, `class_toggle`, `classes`, `attr_if`, `aria_if`, `data_attrs`, `sr_only_class`, `unique_id`
- **Defaults:** `default`, `default_if_empty`
- **Time:** `relative_time`, `duration`

**Import shadowing:** `@use my_utils::cn` makes your import take precedence.

## File Naming

Templates use the `.wtz` extension with optional format hint:
- `template.wtz` - Generic template
- `template.html.wtz` - HTML template
- `_partial.wtz` - Partial (underscore prefix convention)

## CLAUDE.md Integration

For project-wide Waltzing support, add this to your `CLAUDE.md`:

```markdown
## Waltzing Templates

**BEFORE writing or modifying any `.wtz` files, you MUST:**

1. Read the Waltzing syntax reference at https://waltzing.awesomike.com/raw/syntax.md
2. If `waltzing-mcp` is available, use `syntax_help` for specific constructs

**Critical syntax rules:**

- `attr=@expression` — NO quotes around evaluated expressions
- `attr="literal"` — quotes only for literal strings
- `x-data=@```json { key: @var } ```@` — embedded JSON for Alpine.js with variables
- `@let x = a + b` — operators work directly (greedy parsing)
```
