diff --git a/packages/remix/skills/remix-component-styling/SKILL.md b/packages/remix/skills/remix-component-styling/SKILL.md new file mode 100644 index 00000000..3dbf46f7 --- /dev/null +++ b/packages/remix/skills/remix-component-styling/SKILL.md @@ -0,0 +1,62 @@ +--- +name: remix-component-styling +description: Build the chainable style for a single Remix Flutter component — the RemixX styler chain (color, padding, border radius), state variants (onHovered/onPressed/onFocused/onDisabled), and animations, referencing the app's existing theme tokens instead of hardcoding values. Use when a developer wants to style or restyle one Remix component instance, make a RemixButton/RemixCard/etc. look a certain way, add hover/pressed/focused/disabled states, or add animation to a component. For app-wide theming and tokens use remix-theming; for converting Material widgets to Remix use remix-material-migration. +--- + +# Remix component styling + +Builds the style chain for **one component instance**. Scope: the per-component +styler class and its chainable methods. App-wide theme setup is out of scope — +that belongs to `remix-theming`. + +## Before anything: read the real API + +Styler class names are inconsistent (`RemixButtonStyler` vs `RemixCardStyle` vs +the three `RemixSelect*Style` classes) and methods vary per component and version. +Read [../shared/find-remix-source.md](../shared/find-remix-source.md) and confirm +the exact class name and available methods for THIS component from +`/lib/src/components//` before writing code. + +## Workflow + +1. **Identify the component and confirm its styler class** + (find-remix-source §3) and the methods it actually exposes (§4). + +2. **Prefer theme tokens over raw values.** Check for an existing theme scope: + ```bash + grep -rnE 'FortalScope|RemixTheme|FortalTokens' lib/ + ``` + - If a theme exists → reference its tokens rather than hardcoding colors / + spacing / radii. + - If no theme exists → say so and point the dev to the `remix-theming` skill. + Only hardcode values if the dev explicitly wants a standalone one-off. + +3. **Build the chain.** Start from the confirmed empty styler and chain the + confirmed methods. State variants take **another styler instance**, not a + callback: + ```dart + final style = RemixButtonStyler() // confirm class name from source + .paddingX(16) + .paddingY(10) + .color(/* theme token */) + .borderRadiusAll(const Radius.circular(8)) + .onHovered(RemixButtonStyler().color(/* darker token */)) + .animate(AnimationConfig.spring(300.ms)); + ``` + +4. **Attach to the widget** via its `style:` parameter (confirm the widget's + constructor args, find-remix-source §5). + +5. **Verify (recommended).** Run analysis on the touched files and fix every + error before finishing: + ```bash + dart analyze + ``` + A failure here usually means a hallucinated method — reconfirm from source. + +## Notes + +- Only use methods you confirmed exist for that specific component; the button's + helpers are not guaranteed to exist on other components. +- Mix primitives (`AnimationConfig`, `EdgeInsetsGeometryMix`, …) come through + `package:remix/remix.dart`. diff --git a/packages/remix/skills/remix-material-migration/MAPPING.md b/packages/remix/skills/remix-material-migration/MAPPING.md new file mode 100644 index 00000000..93020ad7 --- /dev/null +++ b/packages/remix/skills/remix-material-migration/MAPPING.md @@ -0,0 +1,47 @@ +# Material → Remix mapping table + +Tiered by confidence. **Always confirm the Remix class/widget still exists and its +constructor args** against the resolved source (see +`../shared/find-remix-source.md`) before converting — names change between versions. + +## Tier 1 — confident 1:1 (auto-convert + translate style) + +| Material widget | Remix widget | Notes | +|---|---|---| +| `ElevatedButton`, `TextButton`, `OutlinedButton`, `FilledButton` | `RemixButton` | Map `onPressed`, `child`→`label`. Translate `style: ButtonStyle`. | +| `IconButton` | `RemixIconButton` | | +| `Checkbox` | `RemixCheckbox` | | +| `Switch` | `RemixSwitch` | | +| `Radio` | `RemixRadio` | | +| `Card` | `RemixCard` | | +| `TextField`, `TextFormField` | `RemixTextField` | Form validation semantics differ — verify. | +| `Tooltip` | `RemixTooltip` | | +| `Divider`, `VerticalDivider` | `RemixDivider` | | +| `CircularProgressIndicator` | `RemixSpinner` | Indeterminate spinner. | +| `LinearProgressIndicator` | `RemixProgress` | Determinate/bar progress. | +| `CircleAvatar` | `RemixAvatar` | | +| `AlertDialog`, `Dialog`, `showDialog` | `RemixDialog` | Confirm the show/builder API. | + +## Tier 2 — near-match (do NOT auto-convert; emit a flagged TODO) + +These change semantics or API shape. Leave the Material code in place and add a +`// TODO(remix-migration):` comment with the suggested target and the concrete +differences the dev must resolve. + +| Material widget | Suggested Remix | Why manual | +|---|---|---| +| `DropdownButton`, `DropdownMenu` | `RemixSelect` (+ trigger/menu-item stylers) | Item/trigger model differs substantially. | +| `ExpansionTile`, `ExpansionPanelList` | `RemixAccordion` | Expansion state model differs. | +| `PopupMenuButton` | `RemixMenu` | Trigger + item composition differs. | +| `ToggleButtons` | `RemixToggle` | Single toggle vs button group. | +| `Chip`, `Badge` | `RemixBadge` | Interaction/affordance differs. | +| `TabBar` + `TabBarView` | `RemixTabs` / `RemixTabBar` / `RemixTabView` | Controller wiring differs. | +| `SnackBar`, `MaterialBanner` | `RemixCallout` | Inline vs transient overlay — not equivalent. | + +## Tier 3 — no Remix equivalent (leave untouched, never convert) + +Remix is a component library, not a layout/navigation framework. Do NOT touch: +`Scaffold`, `AppBar`, `Drawer`, `BottomNavigationBar`, `NavigationRail`, +`ListView`, `GridView`, `Column`, `Row`, `Stack`, `Padding`, `Container`, +`SizedBox`, `Expanded`, `Flexible`, navigation/routing, `SnackBar` *scheduling*, +gesture/scroll widgets, and any Material widget not listed in Tier 1/2. diff --git a/packages/remix/skills/remix-material-migration/SKILL.md b/packages/remix/skills/remix-material-migration/SKILL.md new file mode 100644 index 00000000..96dad374 --- /dev/null +++ b/packages/remix/skills/remix-material-migration/SKILL.md @@ -0,0 +1,53 @@ +--- +name: remix-material-migration +description: Convert existing Flutter Material widgets in an app to their Remix component-library equivalents — swap the widget AND translate its Material styling (ButtonStyle, decoration, theme) into the equivalent Remix styler chain, auto-converting only confident 1:1 matches and flagging near-matches as TODOs. Use ONLY when the developer explicitly asks to migrate/convert/replace Material widgets with Remix (e.g. "migrate this screen to Remix", "replace ElevatedButton with RemixButton", "convert Material to Remix"). Requires existing Material code to rewrite. For app-wide theming use remix-theming; to style an already-Remix component use remix-component-styling. +--- + +# Material → Remix migration + +Rewrites existing **Material** widgets into **Remix** components. Precondition: +there is real Material code to convert. This skill both swaps the widget and +translates its visual style. + +## Before anything: read the real API + +Read [../shared/find-remix-source.md](../shared/find-remix-source.md) and confirm +every target Remix class/widget name and its constructor args from the resolved +version before rewriting. Confirm styler names too — they are inconsistent. + +## Workflow + +1. **Scope the migration.** Identify the file(s)/widget(s) the dev named. Never + migrate beyond what was asked. + +2. **Classify each Material widget** using + [MAPPING.md](MAPPING.md): + - **Tier 1 (confident 1:1)** → auto-convert (steps 3–4). + - **Tier 2 (near-match)** → do NOT rewrite. Insert a + `// TODO(remix-migration): -> ; ` comment and + leave the Material code intact. + - **Tier 3 (no equivalent — layout/navigation)** → leave untouched. + +3. **Swap the widget** (Tier 1). Map constructor args (`onPressed`, `child`→ + `label`, etc. per MAPPING.md), preserving behavior. Add the Remix import. + +4. **Translate the styling** (Tier 1). This is required — an unstyled migration + looks broken. Read the Material `style: ButtonStyle`/`ThemeData`, wrapping + `Container`/`Padding` decoration, colors and radii, and produce the equivalent + Remix styler chain. Delegate the chain-building to the same approach as the + `remix-component-styling` skill (confirm methods from source; reference theme + tokens if a theme scope exists). Where a Material style property has no faithful + Remix equivalent, drop a `// TODO(remix-migration):` rather than guessing. + +5. **Report.** Summarize: converted (Tier 1), flagged for manual review (Tier 2, + with reasons), and left as-is (Tier 3). Silent semantic changes are forbidden. + +6. **Verify (REQUIRED — non-negotiable).** Migration rewrites code that already + compiled, so it must still compile: + ```bash + dart analyze + ``` + Fix every error before declaring done. Iterate until clean. A failure usually + means a hallucinated name — reconfirm against source. If a conversion cannot be + made to compile faithfully, revert it to the original Material widget and flag + it as Tier 2 instead. diff --git a/packages/remix/skills/remix-theming/SKILL.md b/packages/remix/skills/remix-theming/SKILL.md new file mode 100644 index 00000000..95c5fa38 --- /dev/null +++ b/packages/remix/skills/remix-theming/SKILL.md @@ -0,0 +1,68 @@ +--- +name: remix-theming +description: Set up and configure app-wide theming for the Remix Flutter component library — install a RemixTheme / Fortal (Radix) preset scope, define and wire design tokens (colors, spacing, radii, typography), and handle light/dark mode. Use when a developer wants to set up Remix theming, configure global tokens, apply the Fortal or Radix preset, wrap their app in a theme scope, or asks how Remix theming/tokens/dark mode work app-wide. This is the authority on tokens; for styling one individual component use remix-component-styling, and for converting Material widgets use remix-material-migration. +--- + +# Remix theming + +Configures **app-wide** theming for Remix. Scope: the theme scope that wraps the +app and the design tokens every component reads. Individual component styling is +out of scope — that belongs to `remix-component-styling`. + +## Before anything: read the real API + +Remix theme class and token names differ between versions. Read +[../shared/find-remix-source.md](../shared/find-remix-source.md) and confirm the +resolved names from `/lib/src/fortal/` and `theme/remix_theme.dart` +before writing code. Do not trust the identifiers below without checking. + +## Workflow + +1. **Detect existing theming.** Search the app for an existing theme scope: + ```bash + grep -rnE 'FortalScope|RemixTheme|FortalTokens' lib/ + ``` + - If one exists → report it and only adjust what the dev asked for. Do not + scaffold a second scope. + - If none exists → proceed to scaffold. + +2. **Choose the foundation.** Ask (or infer from the request): + - **Fortal preset** (Radix-based, batteries included) — recommended default. + Wrap the app *inside* `MaterialApp` with the scope builder confirmed from + `fortal_theme.dart` (e.g. `FortalScope(accent:, gray:, child:)`), picking + `accent`/`gray` from the confirmed `FortalAccentColor` / `FortalGrayColor` + enums. + - **Custom tokens** — only if the dev has an existing design system to map. + +3. **Wire the scope** at the app root so every Remix component inherits it: + ```dart + MaterialApp( + home: FortalScope( // confirm builder name + params from source + accent: FortalAccentColor.indigo, + gray: FortalGrayColor.slate, + child: const HomeScreen(), + ), + ); + ``` + +4. **Map the dev's tokens** (custom path only). Read `FortalTokens` (or the + confirmed token class) to see the token surface — colors, spacing, radii, + typography — and populate it from the dev's existing constants. Prefer token + references over raw values. + +5. **Light/dark mode.** Theme resolution reads brightness from + `MediaQuery` / `Theme.of(context)` (see `theme/remix_theme.dart`). Wire the + app's `themeMode`/brightness rather than hardcoding a single mode. + +6. **Verify (required).** Run analysis on the files you touched and fix every + error before declaring done: + ```bash + dart analyze lib/main.dart # or the files changed + ``` + Analysis failures usually mean a hallucinated name — go back to step 0 and + confirm against source. + +## Handoff + +Once a scope exists, tell the dev that per-component styling (referencing these +tokens) is handled by `remix-component-styling`. diff --git a/packages/remix/skills/shared/find-remix-source.md b/packages/remix/skills/shared/find-remix-source.md new file mode 100644 index 00000000..a8626aa7 --- /dev/null +++ b/packages/remix/skills/shared/find-remix-source.md @@ -0,0 +1,106 @@ +# Finding & reading Remix source (shared reference) + +All three Remix skills (`remix-theming`, `remix-component-styling`, +`remix-material-migration`) share this file. It is the single source of truth for +**how to locate the installed Remix package and read its real API** so we never +hallucinate class or method names. + +> **Golden rule:** Remix's public names are *not* uniform and change between +> releases. NEVER trust names from memory, the README, or another skill's +> examples. Confirm every class and method against the source in the consuming +> repo's resolved version before emitting code. + +## 1. Locate the installed package + +The consuming app depends on `remix` via pub. Resolve the exact version and path: + +```bash +# The resolved version the app actually uses: +grep -A3 '^ remix:' pubspec.lock + +# Path to the source in the pub cache (hosted dep): +ls "$HOME/.pub-cache/hosted/pub.dev/" | grep '^remix-' +# -> e.g. remix-4.2.0 => $HOME/.pub-cache/hosted/pub.dev/remix-4.2.0/lib +``` + +For a git or path dependency, read the `packages:` / `packageConfig` entry: + +```bash +cat .dart_tool/package_config.json | grep -A2 '"name": "remix"' +``` + +Use the `rootUri` from `package_config.json` as the authoritative source location — +it is correct for hosted, git, and path dependencies. `/lib` below +means that resolved directory. + +## 2. Source layout (stable structure) + +``` +/lib/ + remix.dart # public exports — the API allowlist + src/ + components// # one dir per component + .dart # library file (part-of hub) + _widget.dart # the RemixX widget + its constructor args + _style.dart # hand-written chainable style helpers + .g.dart # generated styler class + .animate() etc. + theme/remix_theme.dart # brightness / theme resolution + fortal/ # Fortal design-system preset (Radix-based) + fortal.dart # entrypoint export + fortal_theme.dart # FortalTokens + scope builder + radix/colors/ # Radix color swatches +``` + +`remix.dart` re-exports `package:mix/mix.dart` and part of `naked_ui`, so Mix +styling primitives (`EdgeInsetsGeometryMix`, `AnimationConfig`, `Prop`, …) are +available through `package:remix/remix.dart`. + +## 3. Confirming the class name for a component + +Styler class names are **inconsistent** — do not assume `RemixXStyle`: + +```bash +# List every real styler class name: +grep -rhoE 'class Remix[A-Za-z]+(Styler|Style)\b' \ + /lib/src/components/*/*.g.dart \ + /lib/src/components/*/*_style.dart | sed 's/class //' | sort -u +``` + +Known inconsistencies at time of writing (verify against the resolved version): +`RemixButtonStyler` (the `-Styler` suffix), but `RemixCardStyle`, +`RemixCheckboxStyle`, … (the `-Style` suffix); `select` splits into +`RemixSelectStyle`, `RemixSelectTriggerStyle`, `RemixSelectMenuItemStyle`. + +## 4. Confirming available chainable methods + +The style API is spread across `_style.dart` (hand-written) and +`.g.dart` (generated). To list what a component actually supports: + +```bash +grep -rhoE 'Remix[A-Za-z]+Styler? [a-zA-Z]+\(' \ + /lib/src/components//*.dart | sort -u +``` + +Durable API shapes (still confirm per version): +- Layout/paint: `.color(Color)`, `.paddingX/Y/All(double)`, `.borderRadiusAll(Radius)`. +- Animation: `.animate(AnimationConfig)` (e.g. `AnimationConfig.spring(300.ms)`). +- **State variants take another styler instance**, not a callback: + `.onHovered(RemixButtonStyler().color(...))`, plus `.onPressed`, `.onFocused`, + `.onDisabled`. Confirm which states a given component exposes. +- Construct an empty styler with the class's default constructor + (`RemixButtonStyler()`); some expose `.create(...)` and a `styleFrom` static. + +## 5. Confirming widget constructor args + +```bash +grep -nE 'const Remix[A-Za-z]+\(|required this|this\.[a-z]' \ + /lib/src/components//_widget.dart +``` + +The widget takes the style via a `style:` parameter typed as its styler class. + +## 6. Version check + +Compare this skill bundle's version against the app's resolved `remix` version +(`pubspec.lock`, step 1). If they differ, rely entirely on the steps above — +do not trust any concrete name written in the skill bodies.