Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added
- **`design.md.stub`**: a `DESIGN.md` template shipped at `assets/stubs/design.md.stub` covering all 17 semantic roles (`surface`, `fg`, `primary`, `border`, `destructive`, `success`, `warning`, and their variants), typography on the 4px logical scale, rounded/spacing scales, and key component entries with `{{ placeholder }}` tokens. Consumers copy it into their project root, fill in brand hex values and fonts, then run `design:lint` to validate and `design:sync` to generate the Wind theme. Pairs with `MagicStarterTokens.defaultAliases` as the stable key contract.
- **Wave 4 design-system component library**: 23 new generic UI components are now part of the public barrel (`package:magic_starter/magic_starter.dart`). Each component lives in the canonical atomic-component folder shape (`<name>.dart`, `<name>.recipe.dart`, `<name>.preview.dart`, `index.dart`) under `lib/src/ui/components/`.
- **Form controls**: `Button` (with `ButtonIntent`, `ButtonSize`, `buttonRecipe`), `Input` (with `InputState`, `inputRecipe`), `Textarea` (with `TextareaState`, `textareaRecipe`), `Checkbox`, `Switch`, `Radio`, `Select` (with `selectRecipe`), `Combobox` (with `comboboxRecipe`).
- **Feedback and display**: `Badge` (with `BadgeTone`), `Typography` (with `TypographyVariant`), `Skeleton` (with `SkeletonShape`), `Toast` (with `ToastVariant`), `Tooltip`, `EmptyState`, `ErrorState`.
- **Layout and navigation**: `Accordion` (with `AccordionItem`, `accordionRecipe`), `SegmentedControl` (with `SegmentedControlSize`, `segmentedControlRecipe`), `Tabs` (with `tabsRecipe`), `Navbar`, `Dropdown` menu (`DropdownMenu`, `DropdownMenuItem`).
- **Overlay**: `Dialog`, `BottomSheet`.
- **Composition**: `MagicFormField` (label, hint, error wrapper).
- Previously migrated components (`Card`, `PageHeader`, `SocialDivider`, `NotificationDropdown`, `UserProfileDropdown`, `TeamSelector`, `ConfirmDialog`) were already barrel-reachable through their existing alias exports and are unchanged.
- **Breaking collision note**: the barrel now exports `Switch`, `Dialog`, `Checkbox`, `Radio`, `Badge`, `Typography`, `BottomSheet`, `Tooltip`, `DropdownMenu`, and `DropdownMenuItem`. Tests and app code that import both `package:flutter/material.dart` and `package:magic_starter/magic_starter.dart` must add a `hide` clause on the material import (or the barrel import) to resolve the ambiguity.

### Changed
- **Wave 5 view rewrite**: the auth (login, register, forgot, reset, two-factor-challenge, otp-verify), profile, notifications (list + preferences matrix), and teams (create, settings, invitation-accept) views plus both layouts (`MagicStarterAppLayout`, `MagicStarterGuestLayout`) now compose the new design-system components (`Button`, `Card`, `Switch`, `PageHeader`, `SocialDivider`, etc.) instead of inline W-widgets. Views are now Wind-exclusive: bare `package:flutter/material.dart` imports were replaced with `widgets.dart` + `material show Icons` (and the few genuinely-needed Material shells via `show`), so the new component names no longer collide. Behavior, registry keys (`auth.*`, `profile.*`, `notifications.*`, `teams.*`, `layout.app`, `layout.guest`), controller contracts, gate abilities, `refreshNotifier`, and notification polling are all preserved; only the presentation layer changed.
- **Card migrated to the atomic-component folder + `WindRecipe`**: the card now lives at `lib/src/ui/components/card/` in the canonical 4-file shape (`card.dart` → `class Card`, `card.recipe.dart`, `card.preview.dart`, `index.dart`) and resolves its root className through a theme-driven `WindRecipe` instead of inline string interpolation. The recipe output is byte-identical to the previous `_defaultClassName` for every `CardVariant` x `noPadding` combination (gated by an explicit equivalence test). `MagicStarterCard` is retained as a thin re-export alias of `Card`, and `CardVariant` plus the barrel export path (`package:magic_starter/magic_starter.dart`) are unchanged, so existing callers and the widget-test suite are untouched. This establishes the verbatim template for the Wave 4 component migration.

### Added
- **`MagicStarterTokens.defaultAliases`**: semantic token alias map with 17 roles (`surface`, `surface-container`, `surface-container-high`, `fg`, `fg-muted`, `fg-disabled`, `primary`, `on-primary`, `primary-container`, `accent`, `border`, `border-subtle`, `destructive`, `on-destructive`, `destructive-container`, `success`, `warning`). Each role maps to a light+dark wind className pair (`'bg-... dark:bg-...'` / `'text-... dark:text-...'`). Pass as `WindThemeData(aliases: MagicStarterTokens.defaultAliases)` so components resolve against semantic roles rather than palette utilities directly. This map is the stable key contract that `design:sync` (Steps 20-21) will later regenerate from `DESIGN.md`.

### Fixed
- **Create team opens the new team's settings**: `MagicStarterTeamController.activeTeamName` now matches the local `currentTeamId` (when set) against the resolver's `allTeams()` by id, falling back to the resolver's `currentTeam()` when no match exists. Previously `activeTeamId` preferred the local `currentTeamId` notifier (set on create/switch) while `activeTeamName` read only the resolver's `currentTeam()`, so after creating a team the settings view pre-filled the OLD team's name and effectively opened the old team ([#14](https://github.com/fluttersdk/magic_starter/issues/14)).

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Flutter starter kit for the Magic Framework. Pre-built Auth, Profile, Teams & No

| Command | Description |
|---------|-------------|
| `flutter test --coverage` | Run all tests (~52 files, ~752 cases) with coverage |
| `flutter test --coverage` | Run all tests (~62 files, ~996 cases) with coverage |
| `flutter test test/http/controllers/` | Run controller tests only |
| `flutter test --name "pattern"` | Run tests matching pattern |
| `flutter analyze --no-fatal-infos` | Static analysis (flutter_lints ^6.0) |
Expand Down
48 changes: 46 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Stop rebuilding authentication, profile management, and team features from scrat
| :bell: | **Notifications** | Real-time polling, mark read/unread, preference matrix |
| :iphone: | **OTP Login** | Phone-based guest authentication with send/verify flow |
| :art: | **Wind UI** | Tailwind-like className system — no Material widgets, dark mode built-in |
| :package: | **Reusable Widgets** | PageHeader, Card (3 variants), ConfirmDialog (3 variants), PasswordConfirmDialog, TwoFactorModal — all standalone |
| :package: | **Design-System Components** | 29 atomic components (Button, Input, Badge, Dialog, Toast, Tabs, Accordion, and more) plus `MagicStarterTokens` semantic alias layer |
| :gear: | **13 Feature Toggles** | All opt-in, configure only what you need |
| :jigsaw: | **View Registry** | Override any screen or layout from the host app |
| :hammer_and_wrench: | **CLI Tools** | install, configure, doctor, publish, uninstall |
Expand Down Expand Up @@ -191,9 +191,53 @@ MagicStarter.useSidebarFooter((context) {

---

## Design-System Components

Magic Starter ships a full atomic design-system component library exported from `package:magic_starter/magic_starter.dart`. Every component lives in a canonical 4-file atomic folder (`<name>.dart`, `<name>.recipe.dart`, `<name>.preview.dart`, `index.dart`) under `lib/src/ui/components/` and is driven by a `WindRecipe` that reads from `MagicStarterTokens.defaultAliases`.

### MagicStarterTokens

`MagicStarterTokens.defaultAliases` is a map of 17 semantic roles to light+dark Wind className pairs:

| Role | Example className pair |
|------|----------------------|
| `surface` | `bg-white dark:bg-gray-950` |
| `fg` | `text-gray-900 dark:text-gray-50` |
| `primary` | `bg-primary-600 dark:bg-primary-500` |
| `destructive` | `bg-red-600 dark:bg-red-500` |
| `border` | `border-gray-200 dark:border-gray-800` |

Pass the map when configuring your Wind theme so all components resolve against semantic roles rather than raw palette utilities:

```dart
WindApp(
theme: WindThemeData(
aliases: MagicStarterTokens.defaultAliases,
),
child: const MyApp(),
)
```

> **Breaking import note**: the barrel now exports `Switch`, `Dialog`, `Checkbox`, `Radio`, `Badge`, `Typography`, `BottomSheet`, `Tooltip`, `DropdownMenu`, and `DropdownMenuItem`. If you import both `package:flutter/material.dart` and `package:magic_starter/magic_starter.dart`, add a `hide` clause on the conflicting names.

### Component families

| Family | Components |
|--------|-----------|
| Form controls | `Button`, `Input`, `Textarea`, `Checkbox`, `Switch`, `Radio`, `Select`, `Combobox` |
| Display | `Badge`, `Typography`, `Skeleton`, `Toast`, `Tooltip`, `EmptyState`, `ErrorState` |
| Selection / navigation | `SegmentedControl`, `Tabs`, `Accordion`, `Navbar`, `DropdownMenu` |
| Overlay | `Dialog`, `BottomSheet` |
| Composition | `MagicFormField`, `Card`, `PageHeader`, `SocialDivider` |
| App chrome | `NotificationDropdown`, `UserProfileDropdown`, `TeamSelector` |

All components accept Wind `className` strings and resolve colors through the semantic alias layer when configured.

---

## Reusable Widgets

Magic Starter exports a set of standalone UI widgets that consumer apps can use directly — no internal controller coupling required.
Magic Starter also exports a set of standalone UI widgets that consumer apps can use directly. No internal controller coupling required.

### MagicStarterPageHeader

Expand Down
176 changes: 176 additions & 0 deletions assets/stubs/design.md.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
name: {{ app_name }}
description: >
Design system for {{ app_name }}. Single-brand, Wind semantic tokens,
M3-role palette. Generate the Wind theme with:
dart run {{ app_package }}:artisan design:sync
colors:
surface:
light: "#FFFFFF"
dark: "#030712"
surface-container:
light: "#F9FAFB"
dark: "#111827"
surface-container-high:
light: "#F3F4F6"
dark: "#1F2937"
fg:
light: "#111827"
dark: "#F9FAFB"
fg-muted:
light: "#6B7280"
dark: "#9CA3AF"
fg-disabled:
light: "#D1D5DB"
dark: "#4B5563"
primary:
light: "#{{ primary_hex_light }}"
dark: "#{{ primary_hex_dark }}"
on-primary:
light: "#FFFFFF"
dark: "#FFFFFF"
primary-container:
light: "#{{ primary_container_light }}"
dark: "#{{ primary_container_dark }}"
accent:
light: "#{{ accent_hex_light }}"
dark: "#{{ accent_hex_dark }}"
border:
light: "#E5E7EB"
dark: "#374151"
border-subtle:
light: "#F3F4F6"
dark: "#1F2937"
destructive:
light: "#DC2626"
dark: "#EF4444"
on-destructive:
light: "#FFFFFF"
dark: "#FFFFFF"
destructive-container:
light: "#FEE2E2"
dark: "#7F1D1D"
success:
light: "#15803D"
dark: "#16A34A"
warning:
light: "#D97706"
dark: "#B45309"
typography:
display:
fontFamily: {{ font_family }}
fontSize: 36px
fontWeight: "700"
lineHeight: 44px
letterSpacing: -0.02em
headline-lg:
fontFamily: {{ font_family }}
fontSize: 28px
fontWeight: "700"
lineHeight: 36px
letterSpacing: -0.01em
headline-md:
fontFamily: {{ font_family }}
fontSize: 22px
fontWeight: "600"
lineHeight: 30px
title-lg:
fontFamily: {{ font_family }}
fontSize: 18px
fontWeight: "600"
lineHeight: 26px
body-lg:
fontFamily: {{ font_family }}
fontSize: 16px
fontWeight: "400"
lineHeight: 26px
body-md:
fontFamily: {{ font_family }}
fontSize: 14px
fontWeight: "400"
lineHeight: 22px
label-md:
fontFamily: {{ font_family }}
fontSize: 14px
fontWeight: "600"
lineHeight: 20px
letterSpacing: 0.01em
label-sm:
fontFamily: {{ font_family }}
fontSize: 12px
fontWeight: "500"
lineHeight: 16px
rounded:
sm: 4px
DEFAULT: 8px
md: 12px
lg: 16px
xl: 24px
full: 9999px
spacing:
xs: 4px
sm: 8px
md: 16px
lg: 24px
xl: 40px
gutter: 16px
section: 32px
components:
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
rounded: "{rounded.md}"
padding: "{spacing.md}"
button-destructive:
backgroundColor: "{colors.destructive}"
textColor: "{colors.on-destructive}"
rounded: "{rounded.md}"
padding: "{spacing.md}"
card-surface:
backgroundColor: "{colors.surface-container}"
rounded: "{rounded.lg}"
padding: "{spacing.lg}"
input-field:
backgroundColor: "{colors.surface-container-high}"
rounded: "{rounded.DEFAULT}"
padding: "{spacing.md}"
---

## Overview

[Describe the brand personality, product purpose, and style direction for
{{ app_name }}. What does this design system represent?]

For responsive and accessible usage patterns, see
[docs/design-culture/](docs/design-culture/).

## Colors

[Explain the palette rationale: why these colors, what roles they serve, how
light and dark modes differ. Reference WCAG contrast requirements for
on-X / X pairs.]

## Typography

[Describe the chosen font and why it fits the brand. List the scale levels and
their intended usage contexts (display, body, label).]

## Layout

[Describe the responsive grid: breakpoints, column model, gutter and section
spacing. Reference the wind-responsive doc for breakpoint-specific guidance.]

## Elevation & Depth

[Describe the elevation model: tonal backgrounds, shadows (if any), and how
depth signals hierarchy to the user.]

## Shapes

[Map corner-radius values to use cases: inputs, buttons, cards, dialogs,
badges.]

## Components

[List the key components and their token bindings. Variant matrices are visible
at `/preview` in debug builds. Run `design:lint` to validate token usage.]
Loading
Loading