canonrs-server 0.1.0

CanonRS server-side rendering support
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
---
component: AlertDialog
layer: UI
status: Stable
since: v1.0
last_review: 2025-01-18
ownership: canonrs
keywords:
  - design system
  - dioxus
  - ssr
  - alert dialog
  - modal
  - enterprise ui
  - blocking modal
path_primitive: /opt/docker/monorepo/packages-rust/rs-design/src/primitives/alert_dialog.rs
path_ui: /opt/docker/monorepo/packages-rust/rs-design/src/ui/alert_dialog/
---

# AlertDialog

## 1. Conceptual Introduction

The AlertDialog is a **UI component** that provides a blocking modal interaction pattern for critical user decisions. It serves as the canonical enterprise solution for destructive actions, confirmations, warnings, and situations requiring explicit user acknowledgment before proceeding.

The AlertDialog exists in the **UI layer** because it provides:
- Blocking modal behavior (non-dismissible by default)
- Controlled state management via `RwSignal<bool>`
- Ergonomic API for critical decision flows
- Composition of primitives without business logic

**What it is NOT:**
- Not a primitive (it composes primitives)
- Not a dismissible dialog (use Dialog for that)
- Not a runtime behavior (no browser APIs)
- Not a notification (use Toast for non-blocking feedback)

---

## 2. Architectural Responsibility (Contract)

### Responsibility

The AlertDialog UI component:
- **Composes** `AlertDialogPrimitive`, `AlertDialogTrigger`, `AlertDialogPortal`, `AlertDialogOverlay`, `AlertDialogContent`, `AlertDialogHeader`, `AlertDialogFooter`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialogAction`, and `AlertDialogCancel`
- **Declares** non-dismissible modal behavior via `data-modal="true"` and `data-dismissible="false"`
- **Exposes** controlled state API with `open: RwSignal<bool>` and `default_open: bool`
- **Emits** SSR-safe HTML with data-attributes for runtime hydration
- **Guarantees** blocking interaction pattern (ESC and overlay clicks disabled)

### Non-Responsibility

The AlertDialog UI component explicitly does NOT:
- ❌ Execute browser APIs (focus trap, click handlers, ESC key)
- ❌ Manage state imperatively (state lives in `RwSignal` or DOM)
- ❌ Apply CSS classes or inline styles
- ❌ Register global event listeners
- ❌ Perform side effects or mutations
- ❌ Allow dismissal without explicit Action/Cancel

**Side effects are PROHIBITED.**  
All runtime behavior is delegated to the Shell Runtime JS layer.

---

## 3. Position in CanonRS Ecosystem

The AlertDialog participates in the canonical CanonRS flow:
```text
Page/Block (usage)
  ↓
UI Component (AlertDialog) — controlled state, blocking behavior
  ↓
Primitives (AlertDialogPrimitive, AlertDialogOverlay, etc.) — HTML + data-attributes
  ↓
SSR Render — static HTML emitted
  ↓
Shell Runtime JS — focus trap, event delegation, controlled state sync
  ↓
Browser API — DOM manipulation, ARIA, keyboard navigation
  ↓
CSS — styling via [data-alert-dialog-*] selectors
```

**SSR Context:**
- AlertDialog renders complete HTML structure on server
- Initial state determined by `default_open` prop
- No client-side JS required for initial render
- Runtime JS hydrates interactive behavior on client

**Hydration:**
- Shell Runtime reads `data-modal`, `data-dismissible`, and `data-default-open`
- Sets initial `data-open="true|false"` based on `default_open` or controlled `open` signal
- Attaches focus trap to content region
- Disables ESC and overlay click (non-dismissible contract)
- Syncs `RwSignal<bool>` with DOM state when Action/Cancel clicked

---

## 4. Tokens Applied

The AlertDialog UI component does **not directly apply tokens**—it delegates to CSS.  
The CSS layer (`style/ui/alert_dialog.css`) consumes the following token families:

### Overlay & Layering Tokens
- `--z-modal` (overlay and content z-index)
- `--overlay-backdrop-opacity` (overlay transparency)

### Layout Tokens
- `--space-lg` (content padding, max-width offset)
- `--space-md` (content gap)
- `--space-sm` (footer gap)
- `--space-xs` (header gap)
- `--size-modal-md` (content width)

### Typography Tokens
- `--font-size-lg` (title size)
- `--font-size-sm` (description size)
- `--font-weight-semibold` (title weight)
- `--line-height-tight` (title line height)
- `--line-height-normal` (description line height)

### Color Tokens
- `--color-bg-surface` (content background)
- `--color-fg-default` (title text)
- `--color-fg-muted` (description text)
- `--color-border-muted` (content border)

### Border & Radius Tokens
- `--border-width-hairline` (content border)
- `--radius-lg` (content border radius)

### Shadow Tokens
- `--shadow-lg` (content shadow)

**Token Resolution:**  
UI component emits data-attributes → CSS applies tokens → Browser renders.

---

## 5. Technical Structure (How It Works)

### SSR Render Phase

The AlertDialog component renders to static HTML:
```html
<div data-alert-dialog="" data-modal="true" data-dismissible="false" data-default-open="false">
  <button data-alert-dialog-trigger="" type="button">
    Delete Account
  </button>
  
  <div data-alert-dialog-portal="">
    <div data-alert-dialog-overlay=""></div>
    <div data-alert-dialog-content="">
      <div data-alert-dialog-header="">
        <div data-alert-dialog-title="">Are you absolutely sure?</div>
        <p data-alert-dialog-description="">
          This action cannot be undone. This will permanently delete your account.
        </p>
      </div>
      <div data-alert-dialog-footer="">
        <button data-alert-dialog-cancel="">Cancel</button>
        <button data-alert-dialog-action="">Delete</button>
      </div>
    </div>
  </div>
</div>
```

**Key contracts:**
- `data-alert-dialog` marks root container
- `data-modal="true"` enforces blocking behavior
- `data-dismissible="false"` disables ESC/overlay close
- `data-default-open="true|false"` specifies initial state
- `data-open="true|false"` reflects current open state (set by runtime or controlled signal)
- `data-alert-dialog-action` marks confirm button (closes dialog on click)
- `data-alert-dialog-cancel` marks cancel button (closes dialog on click)

### Runtime Hydration Phase

Shell Runtime JS:
1. Queries `[data-alert-dialog]` elements
2. Reads `data-modal`, `data-dismissible`, and `data-default-open` attributes
3. Sets initial `data-open="true|false"` based on `default_open` or controlled `open` signal
4. Attaches focus trap to `[data-alert-dialog-content]` when open
5. Disables ESC key handler (non-dismissible)
6. Disables overlay click handler (non-dismissible)
7. Attaches click handlers to `[data-alert-dialog-action]` and `[data-alert-dialog-cancel]`
8. On Action/Cancel click: sets `data-open="false"` and syncs controlled `RwSignal` if present

### CSS Styling Phase

CSS selectors target data-attributes:
```css
[data-alert-dialog-overlay] {
  opacity: 0;
  transition: opacity 200ms ease;
}

[data-alert-dialog][data-open="true"] [data-alert-dialog-overlay] {
  opacity: 1;
}

[data-alert-dialog-content] {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.95);
  transition: opacity 200ms ease, transform 200ms ease;
}

[data-alert-dialog][data-open="true"] [data-alert-dialog-content] {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
```

**No classes. No inline styles. Only data-attributes.**

---

## 6. Execution Flow
```text
1. SSR Render (Server)
      AlertDialog component executes
      Composes primitives with data-attributes
      Emits static HTML with data-modal="true", data-dismissible="false"

2. HTML Delivery (Network)
      Browser receives HTML
      Renders static structure (overlay/content hidden via opacity: 0)

3. Shell Runtime Hydration (Client)
      Runtime JS executes
      Reads data-alert-dialog attributes
      Sets initial data-open based on default_open or controlled signal
      Attaches focus trap and event delegation
      Disables ESC and overlay click (non-dismissible contract)

4. User Interaction (Trigger Click)
      User clicks [data-alert-dialog-trigger]
      Runtime sets data-open="true" on container
      CSS animates overlay (opacity 0→1) and content (scale 0.95→1)
      Focus trap activates, moves focus to content

5. User Decision (Action or Cancel)
      User clicks [data-alert-dialog-action] or [data-alert-dialog-cancel]
      Runtime sets data-open="false"
      If controlled: syncs RwSignal.set(false)
      CSS animates close (opacity 1→0, scale 1→0.95)
      Focus returns to trigger element
```

**Critical:** State lives in DOM (`data-open`) and optionally in `RwSignal<bool>`.  
Runtime synchronizes both, ensuring SSR compatibility and controlled state management.

---

## 7. Canonical Use Cases

### Destructive Action Confirmation
```rust
use dioxus::prelude::*;
use rs_design::ui::alert_dialog::*;

fn DeleteAccountDialog() -> Element {
    rsx! {
        AlertDialog {
            default_open: false,
            
            AlertDialogTrigger { "Delete Account" }
            
            AlertDialogPortal {
                AlertDialogOverlay {}
                AlertDialogContent {
                    AlertDialogHeader {
                        AlertDialogTitle { "Are you absolutely sure?" }
                        AlertDialogDescription {
                            "This action cannot be undone. This will permanently delete your account and remove your data from our servers."
                        }
                    }
                    AlertDialogFooter {
                        AlertDialogCancel { "Cancel" }
                        AlertDialogAction { "Delete Account" }
                    }
                }
            }
        }
    }
}
```

### Controlled State (External Trigger)
```rust
fn ControlledAlertDialog() -> Element {
    let mut is_open = use_signal(|| false);
    
    rsx! {
        button { onclick: move |_| is_open.set(true), "Open Alert" }
        
        AlertDialog {
            open: is_open,
            
            AlertDialogPortal {
                AlertDialogOverlay {}
                AlertDialogContent {
                    AlertDialogHeader {
                        AlertDialogTitle { "Confirm Logout" }
                        AlertDialogDescription { "Are you sure you want to log out?" }
                    }
                    AlertDialogFooter {
                        AlertDialogCancel { 
                            onclick: move |_| is_open.set(false),
                            "Cancel" 
                        }
                        AlertDialogAction { 
                            onclick: move |_| {
                                is_open.set(false);
                                // perform logout
                            },
                            "Logout" 
                        }
                    }
                }
            }
        }
    }
}
```

### Warning Dialog
```rust
AlertDialog {
    default_open: false,
    
    AlertDialogTrigger { "Submit Form" }
    
    AlertDialogPortal {
        AlertDialogOverlay {}
        AlertDialogContent {
            AlertDialogHeader {
                AlertDialogTitle { "Unsaved Changes" }
                AlertDialogDescription {
                    "You have unsaved changes. Submitting will discard them."
                }
            }
            AlertDialogFooter {
                AlertDialogCancel { "Go Back" }
                AlertDialogAction { "Submit Anyway" }
            }
        }
    }
}
```

---

## 8. Anti-Patterns (PROHIBITED)

### ❌ Anti-Pattern 1: Using Signal<bool> Instead of RwSignal<bool>
```rust
// WRONG — Signal is read-only, AlertDialog needs write access
let open = use_signal(|| false);

AlertDialog {
    open: open, // ❌ FORBIDDEN — cannot close dialog
}
```

**Why it breaks:**
- `Signal<bool>` is read-only
- Runtime cannot call `.set(false)` on Action/Cancel
- Dialog becomes unclosable without external hack
- Violates enterprise controllability contract

**Correct approach:**
```rust
let mut open = use_signal(|| false); // RwSignal via use_signal

AlertDialog {
    open: open, // ✅ Runtime can write
}
```

---

### ❌ Anti-Pattern 2: Making AlertDialog Dismissible
```rust
// WRONG — AlertDialog is non-dismissible by design
rsx! {
    div {
        "data-alert-dialog": "",
        "data-dismissible": "true", // ❌ FORBIDDEN
    }
}
```

**Why it breaks:**
- AlertDialog is for critical decisions
- Accidental dismissal (ESC, overlay click) is dangerous
- Violates enterprise UX pattern for blocking modals
- Use Dialog component if dismissible behavior needed

**Correct approach:**  
Use `Dialog` for dismissible modals. AlertDialog is always blocking.

---

### ❌ Anti-Pattern 3: Direct Browser API Usage
```rust
// WRONG — accessing window in UI component
#[component]
pub fn AlertDialog(...) -> Element {
    use_effect(move |_| {
        window().document().query_selector(...); // FORBIDDEN
    });
}
```

**Why it breaks:**
- UI components must be SSR-safe
- No `window()`, `document()`, or `web_sys` allowed
- Violates layer separation

**Correct approach:**  
Shell Runtime handles all browser APIs (focus trap, event delegation).

---

### ❌ Anti-Pattern 4: Inline Styles or Classes
```rust
// WRONG — applying classes or styles directly
rsx! {
    AlertDialogContent {
        class: "bg-white p-6 rounded-lg", // FORBIDDEN
        style: "z-index: 9999", // FORBIDDEN
    }
}
```

**Why it breaks:**
- Primitives emit data-attributes only
- CSS layer owns all styling
- Breaks token system and theming

**Correct approach:**  
Emit `data-alert-dialog-content` and let CSS handle styling.

---

### ❌ Anti-Pattern 5: Nesting AlertDialogs
```rust
// WRONG — nesting alert dialogs
AlertDialog {
    AlertDialogContent {
        AlertDialog { // ❌ FORBIDDEN — cognitive overload
            AlertDialogContent { ... }
        }
    }
}
```

**Why it breaks:**
- Cognitive overload for users
- Confusing focus trap stacking
- Violates enterprise UX patterns

**Correct approach:**  
Resolve to single decision point or use sequential dialogs.

---

## 9. SSR, Hydration, and Runtime

### SSR Impact

**Server-Side:**
- AlertDialog renders complete HTML structure
- All `data-*` attributes present in initial HTML
- Initial state determined by `default_open` prop
- No JavaScript required for structure
- Content hidden via CSS (`opacity: 0`)

**Benefits:**
- SEO-friendly (content visible to crawlers)
- Fast First Contentful Paint
- Works without JavaScript (graceful degradation)
- No layout shift during hydration

**Constraints:**
- Cannot use browser APIs during SSR
- Cannot access `window`, `document`, `localStorage`
- Must emit pure HTML

---

### Hydration Process

1. **HTML Delivery:** Browser receives static HTML with `data-alert-dialog` attributes
2. **CSS Application:** Browser applies `alert_dialog.css` styles (content hidden)
3. **Runtime Load:** Shell Runtime JS executes
4. **State Initialization:** Runtime reads `data-default-open` or controlled `open` signal, sets `data-open="true|false"`
5. **Focus Trap Setup:** Runtime prepares focus trap (inactive until opened)
6. **Event Delegation:** Runtime attaches delegated handlers for trigger, action, cancel
7. **Interactive:** Component is now fully interactive

**Hydration Contract:**
- Runtime never re-renders HTML
- Runtime only mutates `data-open` attribute
- Runtime syncs `RwSignal<bool>` if controlled
- CSS reacts to `data-open` state changes

---

### Runtime Global Constraints

**Focus Trap Behavior:**
- Focus trap activates when `data-open="true"`
- Traps focus within `[data-alert-dialog-content]`
- Returns focus to trigger on close
- Respects ARIA best practices

**Non-Dismissible Contract:**
- ESC key handler disabled
- Overlay click handler disabled
- Only Action/Cancel buttons close dialog
- Ensures critical decision completion

**AutoReload Behavior:**
- AutoReload during dev can break script order
- Shell Runtime must be inline in SSR (Canon Rule #103)
- External scripts may load out of order

**Mitigation:**
- Critical runtime JS inlined in `<head>`
- Non-critical behavior degrades gracefully
- Never depend on external script load timing

**Hot Reload:**
- AlertDialog preserves state across hot reloads
- `data-open` persists in DOM
- Controlled `RwSignal` re-syncs on component re-render
- No state loss during development

---

## 10. Conformance Checklist

- [x] SSR-safe (no browser APIs in component)
- [x] No imperative JS (no `use_effect`, `window()`, `document()`)
- [x] Uses tokens (indirectly via CSS)
- [x] All tokens documented in section 4
- [x] Anti-patterns documented with explanations
- [x] Canon Rules cited in section 11
- [x] Execution flow documented
- [x] Use cases provided
- [x] Hydration contract explicit
- [x] Runtime constraints documented
- [x] Controlled state uses `RwSignal<bool>`
- [x] Non-dismissible behavior enforced

---

## 11. Canon Rules Applied

### Canon Rules Applied

- **Canon Rule #102 — Runtime JS Is Shell Infrastructure**  
  AlertDialog delegates all browser interaction (focus trap, event delegation, ESC handling) to Shell Runtime, never executing imperative JS in the component layer.

- **Canon Rule #103 — Critical Runtime JS Must Be Inline in SSR**  
  AlertDialog hydration depends on Shell Runtime being available immediately; inline runtime ensures no race conditions for focus trap initialization.

- **Canon Rule #104 — AutoReload Breaks Script Order Guarantees**  
  AlertDialog state persists in DOM (`data-open`) and optionally in `RwSignal` to survive AutoReload script re-execution during development.

- **Canon Rule #107 — Primitives Are SSR-Safe Structural Components**  
  AlertDialog composes SSR-safe primitives that emit only HTML and data-attributes, never browser-dependent logic.

- **Canon Rule #108 — UI Components Provide Ergonomic Composition**  
  AlertDialog exists in the UI layer to provide blocking modal behavior (`data-modal`, `data-dismissible`) and controlled state API (`RwSignal<bool>`), reducing boilerplate in application code.

- **Canon Rule #109 — State Lives in DOM, Not Memory**  
  AlertDialog state is declared via `data-open` attribute and optionally controlled via `RwSignal<bool>`, making it inspectable, debuggable, and SSR-compatible.

- **Canon Rule #110 — CSS Targets Data-Attributes, Never Classes**  
  AlertDialog styling uses `[data-alert-dialog-*]` selectors exclusively, ensuring token-based theming and avoiding Tailwind/utility class pollution.

- **Canon Rule #111 — Controlled Components Use RwSignal, Not Signal**  
  AlertDialog enforces `RwSignal<bool>` for controlled state to enable runtime write access (Action/Cancel buttons must close dialog), rejecting read-only `Signal<bool>`.

---

**End of Documentation**