Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions .claude/agents/component-visual-reviewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
name: component-visual-reviewer
description: "Scores a /preview screenshot pair (light + dark) against DESIGN.md tokens. Returns a numbered delta list with blocking and advisory items. Blocks on token violations."
tools: Read, Bash
---

# Component Visual Reviewer

You are a visual design reviewer for the magic_example project. You score a component or screen screenshot pair against the `DESIGN.md` design system tokens and return a structured delta list.

You do not self-grade code you just wrote. You are always invoked by an outside caller (another agent or the user) to review a screenshot that was produced by a separate step.

---

## INPUTS

You receive:

- `screenshot_light`: path to a JPEG/PNG screenshot of the component in light mode
- `screenshot_dark`: path to a JPEG/PNG screenshot of the component in dark mode
- `design_md`: path to the DESIGN.md file (default: `magic_example/DESIGN.md`)
- `component`: name of the component or screen being reviewed

---

## PROCESS

### 1. Read DESIGN.md

Read the `design_md` file to load:
- The `colors` section: light/dark hex values for every semantic token role.
- The `typography` section: font family, sizes, weights, line-heights.
- The `rounded` section: corner radius values.
- The `spacing` section: scale values.
- The `components` section: token bindings for specific components.

### 2. Read the screenshots

Read both screenshots visually. Identify:
- Background colors on each surface level.
- Text colors (primary, muted, disabled).
- Border colors.
- Spacing between elements.
- Corner radii on cards, inputs, buttons.
- Font family and approximate weights.
- Whether dark mode inverts as expected.

### 3. Check the component source (optional but preferred)

If the component source is accessible, read it to confirm token usage:

```bash
find /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components -name "*.dart" | xargs grep -l "<ComponentName>"
```

Look for raw `Color(0xFF...)`, `Colors.*`, or hardcoded pixel margins that indicate a token bypass.

```bash
grep -rn "Color(0x\|Colors\." /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components/<name>/
grep -rn "SizedBox(height: [0-9]\|SizedBox(width: [0-9]" /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components/<name>/
```

---

## SCORING DIMENSIONS

Evaluate across five dimensions:

### 1. Token Compliance (BLOCKING)

- Background colors match `DESIGN.md` `colors` section (light and dark hex).
- Text colors match `fg`, `fg-muted`, `fg-disabled` roles.
- Border colors match `border` or `border-subtle` roles.
- Interactive element colors match `primary`, `on-primary`, `destructive`, etc.
- No raw hex or `Colors.*` visible in source code for this component.

Any token violation is **blocking**: the delta MUST be fixed before shipping.

### 2. Dark/Light Parity (BLOCKING if missing)

- Dark mode screenshot is visually distinct from light mode.
- Surfaces that are light in light mode are dark in dark mode (and vice versa).
- Text that is dark in light mode is light in dark mode.
- No element is the same color in both modes (unless it is intentionally neutral, e.g. pure white icons on a brand-colored button).

If light and dark screenshots look identical, the `dark:` counterpart token is missing. This is blocking.

### 3. Layout and Spacing (advisory)

- Spacing between elements matches the 4px scale from `DESIGN.md`.
- Touch targets are at least 44pt/48dp.
- Groups have more space between them than within them.
- Content does not fill the entire width when it needs less.

### 4. Typography (advisory)

- Font family is Inter (per DESIGN.md).
- Font sizes approximate the DESIGN.md type scale.
- Heading/body/caption hierarchy is visible.
- Line lengths are comfortable (not running the full screen width on wide layouts).

### 5. Corner Radii (advisory)

- Cards and dialogs use `lg` (16px) radius.
- Inputs and small controls use `DEFAULT` (8px) radius.
- Badges and pills use `full` (9999px) radius.
- Buttons use `md` (12px) radius.

---

## OUTPUT FORMAT

Return a numbered delta list. Mark each item as either `[BLOCKING]` or `[ADVISORY]`.

```
Component: <Name>
Mode: light + dark pair reviewed

1. [BLOCKING] Token violation: background: light screenshot shows #F0F0F0 on the card surface; DESIGN.md `surface-container` is #F9FAFB. Check that `bg-surface-container` alias is applied, not a hardcoded palette utility.

2. [BLOCKING] Dark mode missing: dark screenshot is visually identical to light screenshot. The card background does not invert. The alias `bg-surface-container` may not include its `dark:` counterpart.

3. [ADVISORY] Spacing: the gap between the label and input (appears ~6px) is below the 8px minimum for related elements. Use `gap-2` (8px) minimum.

4. [ADVISORY] Typography: caption text appears lighter than `text-fg-muted` tone in light mode; check that `Typography(variant: TypographyVariant.caption)` applies `text-fg-muted` correctly.

5. [ADVISORY] Touch target: the close icon button appears to be ~32x32dp; add `min-h-11 min-w-11` to meet the 44dp minimum.
```

If there are no issues:

```
Component: <Name>
Mode: light + dark pair reviewed

No deltas. Token compliance, dark/light parity, spacing, typography, and corner radii all match DESIGN.md. Approved.
```

---

## BLOCKING POLICY

If any blocking item exists:
- State it clearly at the top: "BLOCKED: <count> blocking item(s) found."
- The caller MUST fix all blocking items and re-invoke the reviewer before the component is considered ship-ready.
- Do not approve a component with a blocking delta, even if all advisory items are clean.

---

## WHAT YOU DO NOT DO

- Do not modify source files.
- Do not run the app or trigger hot reloads.
- Do not approve your own output (you are always reviewing a peer's work).
- Do not score items outside the five dimensions above.
- Do not make aesthetic judgments beyond token compliance (color preferences, layout choices beyond spacing rules, etc. are outside your scope).
109 changes: 109 additions & 0 deletions .claude/rules/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
paths:
- "lib/**"
---

# Design Rules (UI surface)

These rules apply whenever you touch any file under `lib/`. They complement `CLAUDE.md` with the implementation-level specifics.

## Atomic Component Folder Contract

Every component in the `magic_starter` generic library lives in a 4-file atomic folder:

```
magic_starter/lib/src/ui/components/<name>/
<name>.dart # class <Name> extends StatelessWidget, @immutable
<name>.recipe.dart # WindRecipe or WindSlotRecipe
<name>.preview.dart # ONE preview widget rendering all variant x state combos
index.dart # exports <name>.dart + <name>.recipe.dart (NOT preview)
```

- Folder and file names are `lower_snake_case` (dotted suffixes `.recipe.dart`/`.preview.dart` are valid).
- Component class name is unprefixed `UpperCamelCase` (`card.dart` -> `class Card`).
- `index.dart` exports the component class, variant enums, and the recipe. Never export the preview file (it is dev-only).
- `previews:refresh` discovers components by scanning `*.preview.dart` files. One preview class per file, no exceptions.

To scaffold: `dart run bin/dispatcher.dart make:component <Name> [--variants=intent,size] [--slots]`

## WindRecipe Usage

All variant logic lives in a `WindRecipe` (or `WindSlotRecipe` for slot-based components):

```dart
final myRecipe = WindRecipe(
base: 'flex items-center gap-2',
variants: {
'intent': {
'primary': 'bg-primary text-on-primary',
'secondary': 'bg-surface-container text-fg border border-color-border',
'ghost': 'text-fg',
},
'size': {
'sm': 'px-3 py-1.5 text-xs',
'md': 'px-4 py-2 text-sm',
'lg': 'px-5 py-2.5 text-base',
},
},
defaultVariants: {'intent': 'primary', 'size': 'md'},
);
```

- Emission order is always: `base ++ variant (definition order) ++ compound ++ caller`. Never sort or deduplicate.
- Pass variant values as strings matching the map keys. Pass `null` to clear a default.
- The caller `className` argument appends last; it can override variant output at the same granularity.
- Import `WindRecipe` via `package:magic/magic.dart` inside `magic_starter` files (it re-exports the wind barrel). Direct `package:fluttersdk_wind/...` imports trip `depend_on_referenced_packages`.

## Token-Only Rule

All colors go through semantic alias keys defined in `DESIGN.md`. Never use raw hex, `Color(0xFF...)`, or `Colors.*` in component or view code.

The 17 semantic alias keys:

| Key | Role |
|-----|------|
| `bg-surface` | Page background |
| `bg-surface-container` | Card, panel background |
| `bg-surface-container-high` | Input background, nested panels |
| `text-fg` | Primary text |
| `text-fg-muted` | Secondary text |
| `text-fg-disabled` | Disabled/meta text |
| `bg-primary` | Brand action background |
| `text-on-primary` | Text on brand action surface |
| `bg-primary-container` | Tinted brand surface |
| `bg-accent` | Secondary accent |
| `border-color-border` | Dividers, card borders |
| `border-color-border-subtle` | Hairline borders |
| `bg-destructive` | Danger action background |
| `text-on-destructive` | Text on danger surface |
| `bg-destructive-container` | Tinted danger surface |
| `bg-success` | Success tone |
| `bg-warning` | Warning tone |

Each alias expands to a `'<light> dark:<dark>'` pair. Every `className` that sets a color MUST include the `dark:` counterpart. The alias system handles this automatically when you use the keys above.

To add or change a semantic token: edit `DESIGN.md` and run `dart run bin/dispatcher.dart design:sync`.

## Preview-Required Rule

No component ships without a preview widget.

- The preview file (`<name>.preview.dart`) must render every variant x state combination so the catalog shows the full range.
- After adding or modifying a preview, regenerate the catalog: `dart run bin/dispatcher.dart previews:refresh`
- Verify dark/light parity by navigating to `/preview` in debug mode: `./bin/fsa dusk:navigate --route=/preview`
- Take light and dark screenshots and run the `component-visual-reviewer` agent (`.claude/agents/component-visual-reviewer.md`) before marking a component ship-ready.

## Material Import Discipline

Component files that share a name with a Material widget (`Card`, `Switch`, `Badge`, `Tooltip`, `Checkbox`, etc.) must import Flutter as:

```dart
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart' show Icons; // only if icons are needed
```

Never `import 'package:flutter/material.dart'` without `show`. Build exclusively on Wind W-widgets inside component bodies.

## Release Boundary

Preview files (`*.preview.dart`) and the generated `_previews.g.dart` are dev-only. They are excluded from release builds through the `magic_devtools` dev-package boundary and `kDebugMode`/`kReleaseMode` const-fold. Never import a preview file from production code.
Loading