<!-- Generated by ac:claude-md-rules-creator on 2026-05-22. Block-level HTML comment; stripped before injection, visible when humans Read the file. -->

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working inside `example/`. The parent project at `../CLAUDE.md` and its `.claude/rules/*.md` files load first; this file adds example-app-specific rules on top.

## What this directory is

`example/` is a standalone Flutter app (own `pubspec.yaml`, `analysis_options.yaml`, web/android/ios/desktop targets) that boots `lib/main.dart` into a navigable showcase gallery. It serves as the canonical visual reference embedded at `fluttersdk.com/wind` via per-page iframes.

Wind itself is consumed via `pubspec.yaml: fluttersdk_wind: { path: ../ }`. A code change in `../lib/` is visible here on the next hot-reload; the example app is the manual smoke test for the parent package.

The standalone JSON playground formerly hosted here (`lib/play.dart`, `lib/playground.dart`) has been migrated to the `wind_playground_web` package in the `fluttersdk.com` repository. The wind package only ships the demo gallery now.

## Adding a new example page (three edits, manual)

There is NO auto-discovery. Pages are wired by hand in `lib/routes.dart`. To add one:

1. Create the page file at `lib/pages/<category>/<feature>_basic.dart`. File name is **snake_case**; class name is `<Feature>BasicExamplePage` (PascalCase, ends in `ExamplePage`). Page shape (root `WDiv` with `scrollPrimary: true`, `_buildHeader` + `_buildSection` helpers, dark-mode pairs on every color token) is governed by `../.claude/rules/example-pages.md` — read it before writing the page body.
2. Add `import 'pages/<category>/<feature>_basic.dart';` to the top of `lib/routes.dart` (alphabetically inside its category block).
3. Add `'/<category>/<feature>_basic': const <Feature>BasicExamplePage(),` to the `appRoutes` map at the bottom of `lib/routes.dart`.

Verify by running `flutter run -d chrome` and navigating to `/<category>/<feature>_basic`. The route key MUST exactly match the file basename (sans `.dart`) — the docs website uses this URL to iframe the page.

## The `<x-preview>` contract (docs ↔ example binding)

A doc file at `../doc/<section>/<page>.md` embeds an example page via:

```html
<x-preview path="<category>/<feature>_basic" size="md" source="example/lib/pages/<category>/<feature>_basic.dart"></x-preview>
```

The docs website resolves this by iframing the deployed web build at `/<category>/<feature>_basic`. Three things MUST hold for the iframe to render:

- The `path` attribute matches a key in `appRoutes` exactly. Case-sensitive. **Snake_case only** — kebab-case caused 8 doc 404s in commit `a2dee86` and was reverted globally.
- The `source` attribute is the repo-root-relative path to the actual `.dart` file.
- The route exists in `lib/routes.dart` AND the page widget compiles cleanly under `flutter build web --target lib/main.dart`.

If you rename, move, or delete a page, grep `../doc/` for the old `path=` / `source=` value FIRST. Update or remove every matching x-preview in the same change set. A stale x-preview is a broken doc.

Empty `path=""` is the documented special case for the root `/` route → `installation_basic.dart`.

The `size="sm|md|lg"` attribute is interpreted by the docs website's iframe wrapper CSS only; the example page itself renders to whatever viewport it gets. Do not branch on size inside Dart.

## Iframe rendering invariants

The docs website iframes a deployed web build of `lib/main.dart` and navigates to `/<route>` per `<x-preview>`. Pages must work inside an iframe with unpredictable height:

- Root widget of every page is `WDiv(className: 'w-full h-full overflow-y-auto p-4', scrollPrimary: true, child: ...)`. Skipping `scrollPrimary: true` breaks iOS Safari tap-to-top inside the iframe.
- No hard-coded width or height that exceeds typical iframe dimensions (1024 wide is safe; > 1280 risks horizontal scroll in narrow embed contexts).
- No `Navigator.of(context).pop()`, `SystemNavigator.pop()`, `dart:js` window manipulation, or anything that escapes the iframe sandbox.
- No real network calls (HTTP 400 in the test binding, hang in the production iframe). Use static fixtures.

## Theme setup

`lib/main.dart` wraps the gallery in `WindTheme(data: windTheme, builder: (ctx, controller) => MaterialApp(theme: controller.toThemeData(), routes: appRoutes.map(...), ...))`. The floating theme-toggle FAB in `AppLayout` calls `context.windTheme.toggleTheme()`.

## Dependencies

- `fluttersdk_wind: { path: ../ }` — local path to the parent. Do not bump to a pub.dev version; the example tests pre-release parent code.
- `google_fonts: ^6.1.0` — pulled by some pages for non-system font demos.

`pubspec.lock` is committed (this is an application, not a library). Do not delete it.

## Off-limits

- `lib/routes.dart` route keys — renaming a route silently breaks every doc page that links to it. Run a grep across `../doc/` BEFORE renaming.
- `web/index.html`, `web/manifest.json` — standard Flutter web scaffolding. Editing them changes the iframe-host page chrome; coordinate with the docs site before touching.

## Coverage

The 90% coverage gate enforced by `../tool/coverage.sh 90` measures the PARENT package only (`../lib/src/`). The example app contributes nothing to coverage. Tests for new parent features go to `../test/`.

## Related rules (loaded automatically by the parent project)

- `../CLAUDE.md` — Wind-wide stack, commands, post-change sync discipline (which lists example/lib/pages/ as one of the 5 surfaces every code change updates), coverage policy.
- `../.claude/rules/example-pages.md` (paths: example/lib/pages/**) — per-page file shape, helpers (`_buildHeader`, `_buildSection`), dark-mode pair rule, naming convention.
- `../.claude/rules/docs.md` (paths: doc/**) — the doc-side half of the `<x-preview>` contract.
- `../.claude/rules/dynamic.md` (paths: {lib/src/dynamic,test/dynamic}/**) — `WDynamic` schema and security model.
