Skip to content
Development
Skill

/emotion-migrate

Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports from components.

From plugin
metabase
49k23 skills11 agents23 commands
Install
$ npx -y skills add metabase/metabase --skill emotion-migrate --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/emotion-migrate

Context preview

The summary Claude sees to decide when to auto-load this skill.

Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports from components.

SKILL.md

emotion-migrate.SKILL.md
name: emotion-migrate
description: Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports from components.

Emotion → Mantine + CSS Modules Migration Skill

Migrate Emotion styled-components (`@emotion/styled`, `@emotion/react`) to Mantine layout components with style props and CSS modules. The goal is zero Emotion imports, zero inline styles, and maximum use of design system tokens.

Priority Order (Strict)

1. **Mantine components + style props** — `Box`, `Flex`, `Stack`, `Group`, `Text`, `Title`, `Card`. This is the DEFAULT. Every CSS property must be checked against style props FIRST. 2. **CSS modules** (`.module.css`) — ONLY for properties that Mantine style props genuinely cannot express: pseudo-selectors (`:hover`, `:focus`, `::before`), `box-shadow`, `border` shorthand, `animation`/`@keyframes`, complex selectors, `cursor`, `pointer-events`, `overflow`, `text-overflow`, `white-space`, `transition`. 3. **Inline styles ONLY for dynamic values** — `style={{ }}` is allowed only for truly dynamic runtime values (e.g., computed widths, positions, data-driven colors). All static styles must use Mantine props or CSS modules.

Mantine-First Decision Gate (CRITICAL)

For EACH styled component, go through every CSS property and ask: "Can this be a Mantine style prop?" If yes → style prop. If no → CSS module. Do NOT dump an entire component into a CSS module just because one property needs it — split them.

**Properties that ARE style props** (use these, not CSS modules):

  • `display` → `display` prop
  • `color` → `c` prop (`c="core-brand"`, `c="text-primary"`)
  • `background-color` → `bg` prop (`bg="background_page-primary"`)
  • `font-size` → `fz` prop (`fz="md"`)
  • `font-weight` → `fw` prop (`fw="bold"`)
  • `line-height` → `lh` prop (`lh="md"`)
  • `text-align` → `ta` prop (`ta="center"`)
  • `padding` (all variants) → `p`, `px`, `py`, `pt`, `pb`, `pl`, `pr`
  • `margin` (all variants) → `m`, `mx`, `my`, `mt`, `mb`, `ml`, `mr`
  • `width` → `w`, `min-width` → `miw`, `max-width` → `maw`
  • `height` → `h`, `min-height` → `mih`, `max-height` → `mah`
  • `flex` → `flex` prop (`flex="0 0 auto"`, `flex={1}`)
  • `gap` → `gap` prop (on Flex/Stack/Group)
  • `align-items` → `align` prop (on Flex/Stack/Group)
  • `justify-content` → `justify` prop (on Flex/Stack/Group)
  • `flex-direction` → `direction` prop (on Flex)
  • `flex-wrap` → `wrap` prop (on Flex)
  • `position` → `pos` prop
  • `top/right/bottom/left` → `top`, `right`, `bottom`, `left` props
  • `opacity` → `opacity` prop

**Properties that NEED CSS modules** (no style prop equivalent):

  • `:hover`, `:focus`, `:active`, `::before`, `::after` (pseudo-selectors)
  • `box-shadow`, `border` (shorthand with color), `outline`
  • `cursor`, `pointer-events`
  • `overflow`, `text-overflow`, `white-space`
  • `animation`, `transition`, `transform`
  • `@media` queries (UNLESS it's simple responsive spacing/sizing — then use responsive syntax: `p={{ base: "md", lg: "xl" }}`)

**Hybrid approach** — when a component needs BOTH, put style props on the Mantine component AND add a CSS module class for the rest:

<Flex
  className={S.root}       /* for :hover, box-shadow, border */
  align="center"            /* style prop */
  gap="sm"                  /* style prop */
  p="md"                    /* style prop */
  bg="background_page-primary"   /* style prop */
>

CSS Module Conventions (Strict)

Class Naming: camelCase

All CSS module class names MUST use **camelCase**. This is the dominant convention across the codebase (~830 camelCase vs ~620 PascalCase classes), used consistently in Mantine UI components, and matches standard CSS module conventions.

/* CORRECT */
.root {
}
.settingsSection {
}
.dragHandle {
}
.closeIcon {
}

/* WRONG — do not use PascalCase or kebab-case */
.ItemRoot {
}
.settings-section {
}

Modifier/state classes also use camelCase:

.selected {
}
.disabled {
}
.interactive {
}
.draggable {
}

No Cascading — Direct Class Assignment

Cascading selectors are **discouraged**. Instead of styling through parent-child relationships, assign a class directly to the element that needs styling.

/* WRONG — cascading/descendant selectors */
.root > input {
}
.root .label {
}
.container > div > span {
}

/* CORRECT — direct class on the target element */
.input {
}
.label {
}
.title {
}

The only acceptable nesting patterns are:

  • **Pseudo-selectors on the same element**: `.item { &:hover { } }`
  • **Modifier composition**: `.item { &.selected { } }`
  • **Hover-reveal patterns** where a parent hover affects a child: `.root:hover .showOnHover { opacity: 1; }` — but only when structurally necessary (the child has no way to know about the parent's hover state)

Import Alias

Always import the CSS module as `S`:

import S from "./ComponentName.module.css";

Step-by-Step Migration Process

Step 1: Read and Understand

Read the `.styled.tsx` file AND every component that imports from it. Understand:

  • Which styled components are used and where
  • Which props drive dynamic styles
  • Which styles are static vs conditional
  • Which styles can map directly to Mantine style props

Step 2: Classify Each Styled Component

For each styled component, apply the Mantine-First Decision Gate above. Then determine the migration target:

| Emotion Pattern | Migration Target | | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `styled.div` with only layout/spacing/color | `Box`, `Flex`, `Stack`, or `

Read more
Ships withmetabase

Metabase is the easy, open-source way for everyone in your company to ask questions and learn from data.

Get the whole plugin