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.
| Doc | Use it when… |
|---|---|
| Getting started | Adoption path, Tailwind, ThemeProvider, registry |
| Installation | Exact install commands, @source, peers |
| Theming | Tokens, 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.
| Topic | Legacy (typical) | Target |
|---|---|---|
| Theme | MUI / LUI ThemeProvider, createTheme, sx, styled, makeStyles, LuiThemeProvider | ThemeProvider from @by/experience-system + tokens (theme.css) |
| Icons | @jda/lui-common-icon-library-mui5 | @by/icons (paired with @by/experience-system) |
| Portal | Theme hints from shell (historically MUI-shaped) | Bridge into ThemeProvider — see Portal theme consumption |
Expect three kinds of churn together:
| Churn type | What helps |
|---|---|
| Imports | by-es migrate scan (CLI) |
| Styling | Styling below + Theming |
| Composition | Getting started, primitives vs registry blocks |
Principles
| Principle | In practice |
|---|---|
| Accessibility & behavior | Focus rings, keyboard paths, ARIA are part of the component contract—you compose Dialog, triggers, etc., rather than one opaque widget per variation (Radix-style). |
| Composition | Prefer small primitives (Field, Label, Button) + registry blocks your platform owns over indefinite MUI wrappers. |
| Reviewable styling | Tailwind + semantic tokens make diffs easier than giant sx / styled() blobs (Theming). |
| Bundle surface | Named imports from @by/experience-system — shrink accidental MUI / CCL subgraphs as you migrate screens. |
Strategy
| Tactic | Why |
|---|---|
| Vertical slices | Stand up ThemeProvider, theme.css, @source per route/MFE—avoid a long-lived “migration branch” that blocks main. |
| Clear boundaries | Wrap new work with ThemeProvider at router/MFE edges; legacy keeps LuiThemeProvider / MUI ThemeProvider until touched. |
| Dependency hygiene | Confirm React, Tailwind, MUI combinations your org still supports alongside @by/experience-system peers — Installation. |
| Portal / shell | Bridge themeMessage.themeObject into ThemeProvider — Portal theme consumption, portal-theme-message-skill. |
| Parallel stacks | Expect 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 live | Theme object: palette, theme.spacing, typography, sx / styled / makeStyles | CSS: variables in theme.css, utilities, Button variant, ThemeProvider attributes |
| Dark / density | Often separate theme objects or flags | dark:, 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:
| Command | Status |
|---|---|
migrate scan | Supported — inventory legacy imports, summary aggregates, and per-import findings (see Scan below). |
migrate report | Supported — summary aggregates only (planning rollups, no per-import table) — from a scan or saved scan JSON (see Report below). |
migrate codemod | Work in progress — 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 scannpx 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 scanOptional version pin:
npx @by/experience-system@latest by-es migrate scan --dir ./src --format jsonDependency 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
--target | Package universe |
|---|---|
v4 (default) | v3 — CCL v3 / MUI v5 list below |
v3 | v2 — 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
| Flag | Purpose |
|---|---|
--dir, -D | Directory to scan (default: cwd). |
--gitignore, -G | Path to .gitignore (default: .gitignore). |
--verbose, -V | Verbose logging. |
--format, -f, -O | table (default), json, or markdown. |
--stdout | Print to the terminal only; do not write a file. |
--out, -o | Explicit output file path (relative to cwd); overrides docs/migration_scan.<ext>. |
--out-dir | Folder for the default migration_scan filename (default docs). |
--name | Default basename without extension (default migration_scan). |
--target, -T | v3 or v4. |
--top-files / --top-folders | Cap hotspot listings in summary (default 20 each). |
--help, -h | Help. |
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 v3Output
| Format | Typical file | Best for |
|---|---|---|
| Table | docs/migration_scan.txt | Plain-text summary plus ASCII tables grouped by module in the findings. |
json | docs/migration_scan.json | Tickets, dashboards, scripting; summary + findings tree. |
Markdown | docs/migration_scan.md | Readable 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).
| Property | Description |
|---|---|
summary | Aggregates for planning (totals, byPackage / category / confidence counts, hotspots, links/registry/skills, warnings) — identical to migrate report JSON/Markdown (MigrateReportResult). |
meta | schemaVersion is 2.0; target (v3 | v4) matches --target; scanRoot is the absolute --dir. |
findings | Ordered per-import rows (see findings[] below). |
Meta object fields
| Field | Description |
|---|---|
meta.schemaVersion | 2.0 for this contract; bump when the shape changes. |
meta.target | v3 or v4 scan preset (matches --target). |
meta.scanRoot | Absolute path passed via --dir. |
Findings entries
| Field | Description |
|---|---|
findings[].filePath | Path relative to scanRoot. |
findings[].line / column | 1-based position (UTF-16 columns per TypeScript). |
findings[].position | filePath:line:column for quick lookup. |
findings[].importKind | named, default, or namespace. |
findings[].isTypeOnly | true for import type or type-only named bindings. |
findings[].importedSymbol | Exported name (Button, default, \* for namespace). |
findings[].localName | Local binding (includes Foo as Bar aliases). |
findings[].migrationAction | Hint 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[].migrationCategory | Structured matrix bucket (direct-replacement, registry-recipe, compose-from-primitives, theme-token, icon-migration, styling-migration, theme-provider-migration, manual-review, unknown). |
findings[].confidence | high | medium | low | unknown heuristic from guide text. |
findings[].suggestedReplacement | Optional mirror of actionable hints when present. |
findings[].relatedDocs | Design-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).
| Flag | Purpose |
|---|---|
--output, -O | markdown (default) or json (summary JSON only). |
--from-scan-json | Path to migrate scan JSON; skip walking the tree. Match --target to the scan for correct package rollups. |
--stdout | Print to the terminal only — do not write a file. |
--out, -o / --out-dir / --name | Default docs/migration_report.md or docs/migration_report.json by format (same pattern as scan). |
--top-files / --top-folders | Caps 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.mdCodemod (work in progress)
by-es migrate codemod will apply high-confidence mechanical edits (imports, icon swaps, etc.). Not implemented in current releases—running codemod prints an error and exits non-zero until it ships.
Operating tips
| Tip | Detail |
|---|---|
| Train early | Getting started, Theming, by-es-llm-reference (Skills). |
| Preflight | Capture visual surprises when Tailwind first loads—screenshots or regression runs. |
| Imports | Prefer shallow @mui/material imports where possible — MUI minimizing bundle size. |
Resources
| Resource | Topic |
|---|---|
| Getting started | Adoption path and tools |
| Theming | ThemeProvider, tokens, density |
| Portal theme consumption | Shell themeMessage.themeObject (ThemeResponseMessage) |
| Installation | Peers, CSS, @source |
| Skills | setup-tailwind-theme-provider-skill, portal-theme-message-skill, LLM reference |
| Registry | components.json, REGISTRY_TOKEN |
| Components | Component index |
Related
MCP
Launch the published @by/experience-system-mcp package from Artifactory through an MCP client, and use its experience-system and governance tools in product projects.
Registry
Configure the Blue Yonder @by-es registry in components.json and add components with the shadcn CLI using npx, pnpx, or pnpm dlx.