Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6715189
feat: add MagicStarterTokens semantic alias layer
anilcancakir Jun 25, 2026
44055c4
refactor: migrate MagicStarterCard to WindRecipe atomic component
anilcancakir Jun 25, 2026
103a551
feat: add structure components (FormField, PageHeader, EmptyState, Er…
anilcancakir Jun 25, 2026
3d2b652
feat: build the generic design-system component library (atomic folders)
anilcancakir Jun 25, 2026
3c260df
refactor: rewrite views + layouts onto the design-system components
anilcancakir Jun 25, 2026
41d8baa
feat: add design.md.stub for consumer DESIGN.md scaffolding
anilcancakir Jun 25, 2026
782ea41
docs: complete post-change sync for the design-system work
anilcancakir Jun 25, 2026
992127a
fix(switch): position thumb via flex justify, drop no-op translate-x
anilcancakir Jun 26, 2026
b78a5b4
fix(button): default Button shrinks to content instead of full-width
anilcancakir Jun 26, 2026
3fe9902
feat(previews): dev-only previews.dart barrel for all 30 components
anilcancakir Jun 26, 2026
bc8b62f
fix(teams): switch to the new team on create (server-side current-team)
anilcancakir Jun 26, 2026
29f7b5f
fix(components): address PR #78 review (release boundary, semantic to…
anilcancakir Jun 26, 2026
7952e72
feat(settings): add SettingsSection/Row/NavRow atomic components
anilcancakir Jun 26, 2026
650149c
feat(page-header): add unified automatic back navigation
anilcancakir Jun 26, 2026
7c453a4
fix(layout): guest pages own their implicit scroll controller
anilcancakir Jun 26, 2026
7788cb6
feat(settings): add SettingsScaffold sub-page wrapper + barrel exports
anilcancakir Jun 26, 2026
d4905c1
feat(settings): add hub + Profile sub-page views, route getters
anilcancakir Jun 26, 2026
1b5f08c
feat(settings): add Security sub-pages (2FA, Password, Sessions)
anilcancakir Jun 26, 2026
80b4c62
feat(settings): add Preferences sub-pages (Appearance, Language, Time…
anilcancakir Jun 26, 2026
74b633a
feat(settings): wire iOS settings routes + registry, retarget nav to hub
anilcancakir Jun 26, 2026
be6557a
feat(settings): adopt unified back header on teams/notifications sub-…
anilcancakir Jun 26, 2026
73bacc5
feat(settings): register the 4 settings component previews (dev-only)
anilcancakir Jun 26, 2026
0e4d4bf
style: dart format settings components/views
anilcancakir Jun 26, 2026
d4d6dea
fix(settings): make hub/sub-pages resolve + back work at runtime
anilcancakir Jun 26, 2026
534fce7
style(settings): larger iOS rows + wider desktop column
anilcancakir Jun 26, 2026
4734bd0
feat(settings): ship settings i18n keys in the install lang stub
anilcancakir Jun 26, 2026
aac6a50
fix(settings): uniform page surface + de-duplicate timezone/language
anilcancakir Jun 26, 2026
51ebae8
fix(settings): fill desktop width + move Save below the card
anilcancakir Jun 26, 2026
b22a2a8
fix(layout): key guest page subtree by route to stop transition crash
anilcancakir Jun 26, 2026
c5a29b5
feat(settings): add Settings hub link to user dropdown; move Delete A…
anilcancakir Jun 26, 2026
245523d
fix(segmented-control): render segments horizontally
anilcancakir Jun 27, 2026
f58f2e5
fix(ui): PageHeader titleSuffix hugs the title (flex-initial title co…
anilcancakir Jun 30, 2026
f758a54
fix(ui): present BottomSheet on the root navigator so it anchors to t…
anilcancakir Jul 1, 2026
9aa7806
fix(cli): windTheme injection tolerates multi-arg MagicApplication
anilcancakir Jul 3, 2026
1905aba
fix(ui): caller className appends onto component recipe instead of re…
anilcancakir Jul 7, 2026
284387e
fix(ui): lift dropdown_menu + tooltip default classNames into recipes…
anilcancakir Jul 7, 2026
5bb7746
feat(ui): add first-class fullWidth prop to Button, Input, Textarea (…
anilcancakir Jul 7, 2026
b5064f0
feat(testing): defensive manager fallback + setUpMagicStarterForTests…
anilcancakir Jul 7, 2026
89fa5eb
feat(theme): one-call MagicStarter.useWindTheme(WindThemeData) adopti…
anilcancakir Jul 7, 2026
91001fd
refactor(ui)!: rename flat component exports to MS-prefixed names (MS…
anilcancakir Jul 7, 2026
35288cb
test(testing): make MS-6 fallback statics resettable for order-indepe…
anilcancakir Jul 7, 2026
0a9671b
fix(ui): settings scaffold uses min-h-full inside the app scroll (not…
anilcancakir Jul 7, 2026
fbbfda4
test(ui): scroll-wrap the SettingsNavRow preview test (tall preview o…
anilcancakir Jul 7, 2026
bbdbc3c
chore(deps): realign magic ^0.0.4 + magic_notifications ^0.0.1 (0.0.x…
anilcancakir Jul 7, 2026
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
65 changes: 65 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

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
94 changes: 92 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 (MSButton, MSInput, MSBadge, MSDialog, MSToast, MSTabs, MSAccordion, 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,84 @@ 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(),
)
```

> **`MS` prefix (breaking)**: every design-system component now carries an `MS` prefix (`MSButton`, `MSDialog`, `MSSwitch`, ...). This removes the Material collision entirely, so importing both `package:flutter/material.dart` and `package:magic_starter/magic_starter.dart` no longer needs a `hide` clause. See the [migration table](#migration-ms-prefix) below to update from the pre-`MS` names.

### Component families

| Family | Components |
|--------|-----------|
| Form controls | `MSButton`, `MSInput`, `MSTextarea`, `MSCheckbox`, `MSSwitch`, `MSRadio`, `MSSelect`, `MSCombobox` |
| Display | `MSBadge`, `MSTypography`, `MSSkeleton`, `MSToast`, `MSTooltip`, `MSEmptyState`, `MSErrorState` |
| Selection / navigation | `MSSegmentedControl`, `MSTabs`, `MSAccordion`, `MSNavbar`, `MSDropdownMenu` |
| Overlay | `MSDialog`, `MSBottomSheet` |
| Composition | `MSFormField`, `MSCard`, `MSPageHeader`, `MSSocialDivider` |
| App chrome | `MSNotificationDropdown`, `MSUserProfileDropdown`, `MSTeamSelector` |

All components accept Wind `className` strings and resolve colors through the semantic alias layer when configured. `MSButton`, `MSInput`, and `MSTextarea` also accept a `bool fullWidth = false` prop; setting it to `true` fills the parent width (wraps the rendered widget in a `SizedBox(width: double.infinity)` rather than a `className` token, since Material widgets ignore cross-axis stretch — see [flutter/flutter#19399](https://github.com/flutter/flutter/issues/19399)).

<a name="migration-ms-prefix"></a>
### Migration: the `MS` prefix

Earlier previews exported these components under bare names. They now carry an
`MS` prefix (and the old names are gone, no compat shim). Replace each old name
with its `MS` counterpart and drop any `hide` clause you added for the Material
collision:

| Old | New | Old | New |
|-----|-----|-----|-----|
| `Button` | `MSButton` | `DropdownMenu` | `MSDropdownMenu` |
| `Input` | `MSInput` | `DropdownMenuItem` | `MSDropdownMenuItem` |
| `Textarea` | `MSTextarea` | `MagicFormField` | `MSFormField` |
| `Checkbox` | `MSCheckbox` | `Navbar` | `MSNavbar` |
| `Switch` | `MSSwitch` | `EmptyState` | `MSEmptyState` |
| `Radio` | `MSRadio` | `ErrorState` | `MSErrorState` |
| `Badge` | `MSBadge` | `SettingsSection` | `MSSettingsSection` |
| `Typography` | `MSTypography` | `SettingsRow` | `MSSettingsRow` |
| `Skeleton` | `MSSkeleton` | `SettingsNavRow` | `MSSettingsNavRow` |
| `Select` | `MSSelect` | `SettingsScaffold` | `MSSettingsScaffold` |
| `Combobox` | `MSCombobox` | `Card` | `MSCard` |
| `SegmentedControl` | `MSSegmentedControl` | `PageHeader` | `MSPageHeader` |
| `Tabs` | `MSTabs` | `SocialDivider` | `MSSocialDivider` |
| `Accordion` | `MSAccordion` | `NotificationDropdown` | `MSNotificationDropdown` |
| `AccordionItem` | `MSAccordionItem` | `UserProfileDropdown` | `MSUserProfileDropdown` |
| `Dialog` | `MSDialog` | `TeamSelector` | `MSTeamSelector` |
| `BottomSheet` | `MSBottomSheet` | `ConfirmDialog` | `MSConfirmDialog` |
| `Toast` | `MSToast` | `Tooltip` | `MSTooltip` |

The `MagicStarter*` public widgets (`MagicStarterCard`, `MagicStarterPageHeader`, ...), the per-axis enums (`ButtonIntent`, `InputState`, ...), and the recipe functions are unchanged.

---

## 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 Expand Up @@ -379,6 +454,21 @@ MagicStarter.useTheme(

`MagicStarterTheme` has 7 sub-themes: `form`, `card`, `navigation`, `modal`, `layout`, `pageHeader`, `auth`. All fields are optional.

#### One-call adoption from a `WindThemeData`

If your app already defines a Wind semantic palette (the 17 alias roles, e.g. via `MagicStarterTokens.defaultAliases` or a `design:sync`-generated map), skip building sub-theme structs entirely and adopt the whole palette in one call:

```dart
MagicStarter.useWindTheme(
WindThemeData(
colors: {'primary': myBrandColor},
aliases: MagicStarterTokens.defaultAliases, // or a design:sync map
),
);
```

`useWindTheme()` derives all 7 sub-themes from the theme's semantic roles (`bg-surface`, `text-fg`, `bg-primary`, `border-color-border`, `bg-destructive`, ...) and delegates to `useTheme()`. Each alias carries its own `dark:` pair, so one token re-skins both light and dark. It is additive: `useTheme()` and any `use*Theme()` setter still override afterward. A role you do not define keeps the shipped default (never an invisible surface). See [doc/guides/wind-theme-adoption.md](doc/guides/wind-theme-adoption.md) for the full alias-to-property mapping.

### Level 3: Builder Slots

Override specific sections of a screen without replacing the full view. Use `MagicStarter.view.slot()` to register a partial builder for a named slot:
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.]
30 changes: 29 additions & 1 deletion assets/stubs/install/en.stub
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
"cancel": "Cancel",
"confirm": "Confirm",
"go_to_dashboard": "Go to Dashboard",
"off": "Off",
"on": "On",
"remove": "Remove",
"save": "Save",
"toggle_theme": "Toggle theme",
Expand Down Expand Up @@ -106,8 +108,12 @@
"description": "Create a permanent account to keep your data and unlock all features.",
"title": "Upgrade Your Guest Account"
},
"nav": {
"settings": "Settings"
},
"notifications": {
"fetch_error": "Failed to load notification preferences."
"fetch_error": "Failed to load notification preferences.",
"preferences_title": "Notification Preferences"
},
"newsletter": {
"fetch_error": "Failed to load newsletter preferences.",
Expand All @@ -133,6 +139,20 @@
"verify_button": "Verify Code",
"verify_error": "Failed to verify code. Please try again."
},
"appearance": {
"dark": "Dark",
"dark_description": "Always use the dark theme.",
"light": "Light",
"light_description": "Always use the light theme.",
"section_footer": "Choosing System follows your device display settings.",
"subtitle": "Choose your preferred color scheme.",
"system": "System",
"system_description": "Follow the system display setting.",
"title": "Appearance"
},
"language": {
"subtitle": "Choose the language used throughout the app."
},
"profile": {
"delete_account": {
"button": "Delete Account",
Expand All @@ -142,6 +162,14 @@
"password_label": "Confirm your password",
"title": "Delete Account"
}
},
"settings": {
"account_section": "Account",
"preferences_section": "Preferences",
"security_section": "Security"
},
"timezone": {
"subtitle": "Set the timezone used for displaying dates and times."
}
},
"nav": {
Expand Down
Loading