Experience System

Migration

Move from CCL, MUI, LuiThemeProvider, and JDA icon libraries to @by/experience-system, @by/icons, and ThemeProvider—principles, strategy, styling, Migration CLI Tools (`by-es`), scan, and resources.

This guide is for teams replacing or narrowing CCL and Material UI with @by/experience-system, @by/icons, Tailwind-first styling, and optionally @by-es.

DocUse it when…
Getting startedAdoption path, Tailwind, ThemeProvider, registry
InstallationExact install commands, @source, peers
ThemingTokens, ThemeProvider, density

What migration means here

You are not swapping imports while keeping the same theme JSON in memory. You are changing where visual truth lives: from JS theme objects in React context to DOM attributes (data-ds-theme, data-ds-color, data-ds-spacing, dir) plus theme.css and Tailwind variants.

TopicLegacy (typical)Target
ThemeMUI / LUI ThemeProvider, createTheme, sx, styled, makeStyles, LuiThemeProviderThemeProvider from @by/experience-system + tokens (theme.css)
Icons@jda/lui-common-icon-library-mui5@by/icons (paired with @by/experience-system)
PortalTheme hints from shell (historically MUI-shaped)Bridge into ThemeProvider — see Portal theme consumption

Expect three kinds of churn together:

Churn typeWhat helps
Importsby-es migrate scan (CLI)
StylingStyling below + Theming
CompositionGetting started, primitives vs registry blocks

Principles

PrincipleIn practice
Accessibility & behaviorFocus rings, keyboard paths, ARIA are part of the component contract—you compose Dialog, triggers, etc., rather than one opaque widget per variation (Radix-style).
CompositionPrefer small primitives (Field, Label, Button) + registry blocks your platform owns over indefinite MUI wrappers.
Reviewable stylingTailwind + semantic tokens make diffs easier than giant sx / styled() blobs (Theming).
Bundle surfaceNamed imports from @by/experience-system — shrink accidental MUI / CCL subgraphs as you migrate screens.

Strategy

TacticWhy
Vertical slicesStand up ThemeProvider, theme.css, @source per route/MFE—avoid a long-lived “migration branch” that blocks main.
Clear boundariesWrap new work with ThemeProvider at router/MFE edges; legacy keeps LuiThemeProvider / MUI ThemeProvider until touched.
Dependency hygieneConfirm React, Tailwind, MUI combinations your org still supports alongside @by/experience-system peers — Installation.
Portal / shellBridge themeMessage.themeObject into ThemeProviderPortal theme consumption, portal-theme-message-skill.
Parallel stacksExpect two styling dialects for a while; make differences explicit — setup-tailwind-theme-provider-skill on Skills.

Styling

Scope: mental models for appearance and layout—not every API name. The Button example below illustrates patterns that apply across typography, surfaces, inputs, and layout.

MUI / CCL (leaving)BY (moving toward)
Where look & feel liveTheme object: palette, theme.spacing, typography, sx / styled / makeStylesCSS: variables in theme.css, utilities, Button variant, ThemeProvider attributes
Dark / densityOften separate theme objects or flagsdark:, compact:, variants tied to ThemeProvider
Review phrase“This sx uses theme.spacing(2)…”“This uses gap-scaled-4 + text-neutral-12 under ThemeProvider color=dark

Note: Enabling Tailwind runs preflight — legacy markup may shift (heading margins, lists, controls). BY assumes preflight on. Disabling it to “fix” legacy pages usually breaks new surfaces—plan a visual pass the first time Tailwind loads.

Example: MUI styled components to BY Button

Before

import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';

const CustomButton = styled(Button)(({ theme }) => ({
  backgroundColor: theme.palette.primary.main,
  color: 'white',
  padding: '8px 16px',
  borderRadius: '4px',
  '&:hover': {
    backgroundColor: theme.palette.primary.dark,
  },
  margin: theme.spacing(1),
}));

export const MyComponent = () => {
  return <CustomButton variant="contained">Submit</CustomButton>;
};

After

import { Button } from '@by/experience-system';

export const MyComponent = () => {
  return (
    <div className="m-4">
      <Button type="button" variant="fill">
        Submit
      </Button>
    </div>
  );
};

Prefer semantic colors from Theming and Semantic colors over hard-coded palette utilities.

During transition: Passing className into MUI you still own is common—that differs from ad-hoc utilities on @by/experience-system atoms; stick to documented props and variants on BY primitives.


Migration CLI Tools

The by-es binary ships with @by/experience-system. Commands:

CommandStatus
migrate scanSupported — inventory legacy imports, summary aggregates, and per-import findings (see Scan below).
migrate reportSupportedsummary aggregates only (planning rollups, no per-import table) — from a scan or saved scan JSON (see Report below).
migrate codemodSupported for conservative direct replacements — dry-run by default, writes with -w (see Codemod below).

Usage

Pick one pattern depending on whether @by/experience-system is already in package.json.

From the project dependency

Use pnpm exec / npm exec / yarn exec so CI resolves the by-es binary from node_modules / your lockfile:

pnpm exec by-es migrate scan
npm exec -- by-es migrate scan
yarn exec by-es migrate scan

npx by-es may resolve locally when the package is installed; for CI, prefer exec for deterministic paths.

Using npx

One-off audit without adding the dependency:

npx @by/experience-system by-es migrate scan

Optional version pin:

npx @by/experience-system@latest by-es migrate scan --dir ./src --format json

Dependency scan

Purpose: Find .ts, .tsx, .js, .jsx imports from legacy stacks; print migration hints where maintainers defined them. Icons should move to @by/icons—empty hints still show where legacy code lives. By default, results go to docs/migration_scan.<ext>; --stdout prints only to the terminal.

Targets

--targetPackage universe
v4 (default)v3 — CCL v3 / MUI v5 list below
v3v2 — CCL v2 / MUI v4 list below

v2 — CCL v2 / MUI v4

  • @jda/lui-common-component-library
  • @material-ui/core
  • @material-ui/icons
  • @material-ui/lab
  • @material-ui/pickers
  • @material-ui/styles
  • @material-ui/system
  • @material-ui/types
  • @material-ui/utils

v3 — CCL v3 / MUI v5

  • @jda/lui-common-component-library-mui5
  • @jda/lui-common-icon-library-mui5 → replace with @by/icons
  • @mui/icons-material
  • @mui/lab
  • @mui/material
  • @mui/x-date-pickers
  • @mui/styles
  • @mui/system
  • @mui/types
  • @mui/utils
FlagPurpose
--dir, -DDirectory to scan (default: cwd).
--gitignore, -GPath to .gitignore (default: .gitignore).
--verbose, -VVerbose logging.
--format, -f, -Otable (default), json, or markdown.
--stdoutPrint to the terminal only; do not write a file.
--out, -oExplicit output file path (relative to cwd); overrides docs/migration_scan.<ext>.
--out-dirFolder for the default migration_scan filename (default docs).
--nameDefault basename without extension (default migration_scan).
--target, -Tv3 or v4.
--top-files / --top-foldersCap hotspot listings in summary (default 20 each).
--help, -hHelp.
pnpm exec by-es migrate scan
pnpm exec by-es migrate scan --dir ./src --format json
pnpm exec by-es migrate scan --stdout
pnpm exec by-es migrate scan --format markdown --out ./migration_audit.md
npx @by/experience-system by-es migrate scan --target v3

Output

FormatTypical fileBest for
Tabledocs/migration_scan.txtPlain-text summary plus ASCII tables grouped by module in the findings.
jsondocs/migration_scan.jsonTickets, dashboards, scripting; summary + findings tree.
Markdowndocs/migration_scan.mdReadable summary plus Markdown tables (rollup plus per-import tables).

Scan JSON schema (version 2.0)

by-es migrate scan --format json writes a deterministic JSON tree with meta, rollup summary, and per-import findings (stable sort).

PropertyDescription
summaryAggregates for planning (totals, byPackage / category / confidence counts, hotspots, links/registry/skills, warnings) — identical to migrate report JSON/Markdown (MigrateReportResult).
metaschemaVersion is 2.0; target (v3 | v4) matches --target; scanRoot is the absolute --dir.
findingsOrdered per-import rows (see findings[] below).

Meta object fields

FieldDescription
meta.schemaVersion2.0 for this contract; bump when the shape changes.
meta.targetv3 or v4 scan preset (matches --target).
meta.scanRootAbsolute path passed via --dir.

Findings entries

FieldDescription
findings[].filePathPath relative to scanRoot.
findings[].line / column1-based position (UTF-16 columns per TypeScript).
findings[].positionfilePath:line:column for quick lookup.
findings[].importKindnamed, default, or namespace.
findings[].isTypeOnlytrue for import type or type-only named bindings.
findings[].importedSymbolExported name (Button, default, \* for namespace).
findings[].localNameLocal binding (includes Foo as Bar aliases).
findings[].migrationActionHint from structured migration matrix (each row exposes message; scan JSON still emits this flattened string plus category/docs fields). @mui/ / @material-ui/ imports with no symbol-specific row fall back to a default message linking /system/overview/migration.
findings[].migrationCategoryStructured matrix bucket (direct-replacement, registry-recipe, compose-from-primitives, theme-token, icon-migration, styling-migration, theme-provider-migration, manual-review, unknown).
findings[].confidencehigh | medium | low | unknown heuristic from guide text.
findings[].suggestedReplacementOptional mirror of actionable hints when present.
findings[].relatedDocsDesign-site paths under /system/... when suggested.
findings[].registry / skills@by-es registry item names / skill ids when referenced (may be empty).

Older tooling that expected legacy JSON keyed only by module should migrate to this shape (schemaVersion marks the cut).

Still on MUI v4 → v5? Follow Material UI migration and mui-replace codemod before assuming every hit maps to @by/experience-system.

Migration report

Purpose: Emit summary aggregates only (MigrateReportResult) — the same planning rollups as the summary object in migrate scan, without per-import findings. Runs a fresh scan (--dir, --gitignore, --target, --verbose same as scan) or --from-scan-json (reads meta + findings; any embedded summary is ignored and recomputed).

FlagPurpose
--output, -Omarkdown (default) or json (summary JSON only).
--from-scan-jsonPath to migrate scan JSON; skip walking the tree. Match --target to the scan for correct package rollups.
--stdoutPrint to the terminal only — do not write a file.
--out, -o / --out-dir / --nameDefault docs/migration_report.md or docs/migration_report.json by format (same pattern as scan).
--top-files / --top-foldersCaps for hotspot lists in the summary (default 20 each).
pnpm exec by-es migrate report --dir ./src
pnpm exec by-es migrate report --output json --stdout
pnpm exec by-es migrate report --from-scan-json ./docs/migration_scan.json --out ./migration-rollup.md

Codemod

Purpose: Apply conservative, component-level mechanical edits for the v4 migration target. It is a direct-replacement codemod, not a full migration engine: it changes imports, JSX tag identifiers, approved static prop names/values, and exact-match legacy LUI icon imports.

The command is dry-run by default. Add -w / --write to update files.

pnpm exec by-es migrate codemod --dir ./src --stdout
pnpm exec by-es migrate codemod --dir ./src --format json --out docs/migration_codemod.json
pnpm exec by-es migrate codemod --dir ./src -w
npx @by/experience-system by-es migrate codemod --dir ./src --stdout
Automated surfaceWhat changes
Direct component replacementsConfirmed one-to-one CCL/LUI and selected @mui/material components move to @by/experience-system. Imports are removed or split when only some specifiers move.
JSX component identifiersUnaliased JSX tags are renamed when the target component name differs. Aliased imports keep the local alias and only change the imported symbol.
Approved static prop mappingsMaintainer-approved prop renames and literal value maps are applied. Dynamic or unapproved values are reported as prop review items.
Legacy LUI icon importsExact-name icons from @jda/lui-common-icon-library-mui5 move to the matching @by/icons/<namespace> export.
Not automatedWhy
Material icon imports@mui/icons-material is scanned and reported, but the write codemod does not replace it.
Registry recipesRegistry components may require installing or inspecting @by-es/<item> and adapting local composition.
Compose-from-primitives/manual-reviewComponents such as dialogs, cards, lists, object status, drawers, selects, and pagination need behavior/API review before replacement.
Styling and provider migrationssx, styled, makeStyles, Tailwind setup, and ThemeProvider wiring are intentionally left to focused migration work.
FlagPurpose
--dir, -DRoot directory of source files to codemod (default: cwd).
--gitignore, -GPath to .gitignore (default: .gitignore).
--write, -wWrite file edits. Omit for dry-run mode.
--verbose, -VInclude unchanged files in the report.
--format, -f, -Otable (default plain text) or json.
--stdoutPrint to the terminal only — do not write a report file.
--out, -o / --out-dir / --nameDefault docs/migration_codemod.txt or docs/migration_codemod.json by format (same pattern as scan).

Recommended workflow:

  1. Run migrate scan --format json first so the team can see all work, including manual review.
  2. Run migrate codemod --stdout and review changed, skipped, and prop-review items.
  3. Run migrate codemod -w on a focused slice.
  4. Type-check, lint, test, and visually inspect the slice.
  5. Rerun migrate scan to confirm the expected findings were removed.

Operating tips

TipDetail
Train earlyGetting started, Theming, by-es-llm-reference (Skills).
PreflightCapture visual surprises when Tailwind first loads—screenshots or regression runs.
ImportsPrefer shallow @mui/material imports where possible — MUI minimizing bundle size.

Resources

ResourceTopic
Getting startedAdoption path and tools
ThemingThemeProvider, tokens, density
Portal theme consumptionShell themeMessage.themeObject (ThemeResponseMessage)
InstallationPeers, CSS, @source
Skillssetup-tailwind-theme-provider-skill, portal-theme-message-skill, LLM reference
Registrycomponents.json, REGISTRY_TOKEN
ComponentsComponent index