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
62 changes: 62 additions & 0 deletions packages/remix/skills/remix-component-styling/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
`<remix-root>/lib/src/components/<name>/` 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 <changed files>
```
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`.
47 changes: 47 additions & 0 deletions packages/remix/skills/remix-material-migration/MAPPING.md
Original file line number Diff line number Diff line change
@@ -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.
53 changes: 53 additions & 0 deletions packages/remix/skills/remix-material-migration/SKILL.md
Original file line number Diff line number Diff line change
@@ -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): -> <RemixTarget>; <what differs>` 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 <changed files>
```
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.
68 changes: 68 additions & 0 deletions packages/remix/skills/remix-theming/SKILL.md
Original file line number Diff line number Diff line change
@@ -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 `<remix-root>/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`.
106 changes: 106 additions & 0 deletions packages/remix/skills/shared/find-remix-source.md
Original file line number Diff line number Diff line change
@@ -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. `<remix-root>/lib` below
means that resolved directory.

## 2. Source layout (stable structure)

```
<remix-root>/lib/
remix.dart # public exports — the API allowlist
src/
components/<name>/ # one dir per component
<name>.dart # library file (part-of hub)
<name>_widget.dart # the RemixX widget + its constructor args
<name>_style.dart # hand-written chainable style helpers
<name>.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' \
<remix-root>/lib/src/components/*/*.g.dart \
<remix-root>/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 `<name>_style.dart` (hand-written) and
`<name>.g.dart` (generated). To list what a component actually supports:

```bash
grep -rhoE 'Remix[A-Za-z]+Styler? [a-zA-Z]+\(' \
<remix-root>/lib/src/components/<name>/*.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]' \
<remix-root>/lib/src/components/<name>/<name>_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.
Loading