Skip to main content
apps/mobile is replacing shadcn_flutter with the app-owned design system in lib/core/ui/ (see Mobile design system). The migration runs in reviewable batches. A CI ratchet stops new imports, and the dependency is removed once nothing imports it.

Ratchet

scripts/check-mobile-shadcn-imports.js scans apps/mobile/lib and apps/mobile/test. It treats a direct package:shadcn_flutter import, or an import of an in-repo shim, as a shadcn import. The shims are shadcn_flutter_compat.dart, shadcn_material_bridge.dart, shadcn_localizations_fallback.dart and mobile_shad_theme.dart. Each such file must appear in apps/mobile/tool/shadcn_import_allowlist.txt.
  • A new importer fails with a pointer to package:mobile/core/ui/ui.dart.
  • Every URI of every import/export directive is checked. A small Dart tokenizer (scripts/dart-directive-uris.js) skips comments and strings, so comments inside a directive, several directives on one line, conditional if (dart.library.x) alternatives and adjacent string literals are all read. Dart escapes (\uXXXX, \u{...}, \xHH, simple escapes) are decoded, and a URI with a malformed escape or interpolation fails closed. Relative and package:mobile/ paths are normalised before matching a shim.
  • An allowlisted file that no longer imports shadcn also fails. Run node scripts/check-mobile-shadcn-imports.js --prune to remove migrated entries. The flag never adds entries.
  • Entries missing from the allowlist at the base revision fail, so a PR cannot add an importer together with an allowlist line. CI passes the PR base (or the previous main commit, else main) with --base. Locally the base defaults to the merge base with origin/main. When no base resolves, the check fails closed and asks for --base.
  • When the list is empty, the check fails until flutter pub remove shadcn_flutter has run.
The check runs in the Mobile Analysis CI job, in bun check:changed whenever mobile Dart or the allowlist changes, and as bun check:mobile-shadcn. Part files (part of) inherit their library’s import. A library is migrated only when neither it nor any of its parts uses the prefix. The initial allowlist (October 2026) has 325 files: 253 under lib/ and 72 under test/. Counting the 139 part files, 464 files use shad. identifiers.

Component mapping

Codemod

scripts/mobile-shadcn-codemod.js performs the mechanical renames on whole libraries, including their parts:
It rewrites <prefix>.Gap( → AppGap(, <prefix>.Theme.of( → AppTokens.of(, and <prefix>.LucideIcons.* → LucideIcons.* or AppIcons.*. It adds package:mobile/core/ui/ui.dart and drops the shadcn import once neither the library nor its parts use the prefix. The codemod covers 1,705 icons, 1,282 gaps and 770 theme reads, about 3,750 of the 5,270 prefixed uses. A dry run over the whole app touches 322 files. Afterwards, 263 files still import shadcn (62 freed). After running it, fix values passed into APIs typed shad.ThemeData, then run flutter analyze and the feature’s tests on the remote validation lane. Replace the remaining ~1,500 widget uses by hand with the table above.

Batch order

Each batch is one PR, or one layer of a native stack, owned by one lane. Run the codemod for its folders, migrate the remaining widgets, prune the allowlist, and attach renders for changed screens. LOC and counts cover every file that uses shad., parts included. Phases 2–7 can run in parallel after phase 1 merges, because they touch disjoint folders. Phase 8 waits for 2–7. Large features (tasks_boards, time_tracker, finance) should be split by sub-folder into stack layers of about 10–15 files.

Final removal

  1. Allowlist empty (--prune leaves only the header).
  2. Delete core/widgets/shadcn_flutter_compat.dart, shadcn_material_bridge.dart, shadcn_localizations_fallback.dart and core/theme/mobile_shad_theme.dart. Fold AppearanceTheme.colorScheme into AppColorTokens directly.
  3. flutter pub remove shadcn_flutter, then flutter pub get, then analyze and test on the validation lane.
  4. Remove the ratchet step from .github/workflows/mobile.yaml, the check-changed plan entry, the script, its test and the allowlist file.
  5. Update this page and the mobile skill reference to mark the migration complete.