The migration skill
skills/migrate-to-bedrock/SKILL.md is a Claude Code
skill that walks an agent through migrating a
Radix codebase. It is written for the agent, not for you — but it is worth
reading, because it is the shortest honest description of what a migration
actually involves.
Installing it
Into a project you are migrating:
mkdir -p .claude/skills/migrate-to-bedrock
curl -o .claude/skills/migrate-to-bedrock/SKILL.md \
https://bedrock.sams.land/skills/migrate-to-bedrock/SKILL.md
Then: "migrate this app off Radix". The description is written so the skill
triggers on that, and on the questions people ask afterwards — why a trigger
throws, why the dialog no longer closes on a backdrop click, why data-state
selectors stopped matching.
It also ships inside the package, at
node_modules/@apostel/bedrock/skills/migrate-to-bedrock/SKILL.md, so an agent
already working in the project can be pointed at it with no download.
What it makes the agent do
The order is the point.
Establish viability first. Before any code, it checks four things and
reports them: jsdom tests that click triggers, triggers that are not buttons,
data-state in shared CSS, and overlays with content in them. If a project has
hundreds of jsdom component tests and no Playwright setup, the skill says so and
stops rather than starting a refactor that ends badly.
Then the mechanical work — imports, deleting Portal and Overlay,
data-state → :open, the animation classes.
Then four judgement calls, surfaced rather than resolved: the button rule,
light dismiss, which roots need /controlled, and what depended on content
staying mounted. Each one changes behaviour a user will notice, so the skill is
explicit that the agent should not decide them quietly.
Then verification that catches the specific failure mode here: a bad trigger only throws when its component actually mounts, so type-checking is not enough and the app has to be opened in a browser.
Why a skill rather than a codemod
Most of the diff is mechanical, and a codemod could do it. The parts that matter are not: whether a trigger can become a button depends on what that element is for, and whether light dismiss should be restored depends on whether the dialog is a confirmation or a form.
A codemod would either refuse those or guess. The skill is structured so the agent does the boring 80% and brings you the other 20% with enough context to answer.