Migration Guide
Migrate your existing app to Crystal Design System.
This guide covers migrating an existing ArtasceOne app to Crystal. The process involves three steps: adding tokens, mapping existing values, and adopting components.
Step 1: Add Crystal Packages
Install the packages:
pnpm --filter YOUR_APP add @artasce/crystal-tokens@workspace:* @artasce/crystal-ui@workspace:*Import tokens in your global CSS:
@import '@artasce/crystal-tokens/css';Step 2: Wrap with CrystalProvider
Add CrystalProvider to your root layout. This sets the data-crystal and data-crystallization attributes and provides theme context to all Crystal components.
import { CrystalProvider } from '@artasce/crystal-ui';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<CrystalProvider theme="onyx" crystallization="edge">
{children}
</CrystalProvider>
</body>
</html>
);
}CrystalProvider props:
| Prop | Type | Default | Description |
|---|---|---|---|
theme | CrystalTheme | — | Controlled theme (overrides internal state) |
defaultTheme | CrystalTheme | 'diamond' | Initial theme when uncontrolled |
crystallization | CrystalCrystallization | — | Controlled crystallization |
defaultCrystallization | CrystalCrystallization | 'clean' | Initial crystallization when uncontrolled |
To read or change the theme at runtime, use the useCrystalTheme hook:
import { useCrystalTheme } from '@artasce/crystal-ui';
function ThemeToggle() {
const { theme, setTheme, isDark, toggleDarkMode } = useCrystalTheme();
return <button onClick={toggleDarkMode}>{isDark ? 'Light' : 'Dark'}</button>;
}Step 3: Map Custom Tokens
Many apps define custom CSS variables. Map them to Crystal equivalents:
Common Mappings
| Custom Token | Crystal Equivalent |
|---|---|
--background, --bg | var(--crystal-bg-primary) |
--foreground, --text-color | var(--crystal-fg-primary) |
--card, --surface | var(--crystal-surface-primary) |
--border | var(--crystal-border-default) |
--primary | var(--crystal-accent-primary) |
--accent, --highlight | var(--crystal-accent-primary) |
--muted, --secondary-bg | var(--crystal-bg-tertiary) |
--muted-foreground | var(--crystal-fg-tertiary) |
App-Specific Prefixes
Some apps use branded prefixes. Replace them with Crystal tokens:
/* Before (mademoiselle) */
.hero { color: var(--mad-text); background: var(--mad-bg); }
/* After */
.hero { color: var(--crystal-fg-primary); background: var(--crystal-bg-primary); }/* Before (voyavacations) */
.card { border: 1px solid var(--voya-border); }
/* After */
.card { border: 1px solid var(--crystal-border-default); }Step 4: Adopt Components
Legacy Design System Migration
If your app imports from @artasce/design-system, replace with Crystal equivalents:
Legacy (@artasce/design-system) | Crystal (@artasce/crystal-ui) | Notes |
|---|---|---|
ThemeProvider | CrystalProvider | Set theme and crystallization props |
useTheme() | useCrystalTheme() | Returns { theme, setTheme, isDark, crystallization } |
Box | Native <div> with Tailwind | No wrapper needed |
Flex | Native <div className="flex"> | No wrapper needed |
Text | Native elements with Tailwind | Use text-crystal-text etc. |
Card | CrystalCard or CrystalTile | CrystalTile for metric displays |
Button | Button from @artasce/crystal-ui/ui | shadcn/ui Button with Crystal styling |
FadeIn | GSAP useGSAP hook | Direct GSAP for more control |
Header | CrystalNav | Full navigation system |
HorizontalScrollSection | GSAP ScrollTrigger | Direct implementation preferred |
colors, spacing, typography (JS tokens) | CSS custom properties (--crystal-*) | Tokens are CSS-first now |
Component Replacements
Cards
/* Before */
<div className="rounded-lg border bg-white p-4 shadow-sm">
<h3 className="font-semibold">Title</h3>
<p className="text-gray-600">Description</p>
</div>
/* After */
import { CrystalCard } from '@artasce/crystal-ui';
<CrystalCard title="Title" description="Description" />Grids
/* Before */
<div className="grid grid-cols-3 gap-4">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
/* After */
import { CrystalGrid, CrystalCard } from '@artasce/crystal-ui';
<CrystalGrid columns={3} gap="md">
{items.map(item => <CrystalCard key={item.id} {...item} />)}
</CrystalGrid>Surfaces
/* Before */
<div className="bg-gray-50 border rounded-lg p-6">Content</div>
/* After */
import { CrystalTile } from '@artasce/crystal-ui';
<CrystalTile variant="surface" padding="lg">Content</CrystalTile>Step 5: Remove Old Variables
After migrating, remove the custom token definitions from your CSS. Crystal tokens are now the source of truth.
Search for patterns like:
--YOUR_PREFIX-*custom properties- Hardcoded color values (
#09090b,#fafafa, etc.) - Static
border-radiusvalues (replace withvar(--crystal-radius)) - Static
box-shadowvalues (replace withvar(--crystal-shadow))
Incremental Migration
You don't need to migrate everything at once. Crystal tokens work alongside existing styles:
- Week 1: Add Crystal tokens, set theme, map backgrounds and text colors
- Week 2: Replace card and grid components
- Week 3: Adopt crystallization variables (radius, shadow, motion)
- Week 4: Remove old custom properties, clean up
Brand-Theme Mapping
Each portfolio brand maps to a Crystal theme:
| Brand | Theme | Crystallization |
|---|---|---|
| OpsCenter | Onyx | Edge |
| artasce.com | Diamond | Clean |
| Mademoiselle | Amethyst | Dream |
| ACModels | Ruby | Bold |
| VoyaVacations | Citrine | Clean |
| SyncFocus | Sapphire | Edge |
| Modolla | Emerald | Clean |
Troubleshooting
Tokens not applying
Check that data-crystal is set on the <html> element, not a child:
<!-- Correct -->
<html data-crystal="onyx">
<!-- Wrong (tokens won't cascade) -->
<div data-crystal="onyx">Dark mode conflicts
Crystal themes handle dark mode through theme selection, not prefers-color-scheme. If your app uses class="dark" or data-theme="dark", switch to a dark Crystal theme (onyx, garnet, sapphire, amethyst, ruby, emerald, tanzanite).
Tailwind conflicts
If Tailwind's default theme values conflict with Crystal tokens, ensure Crystal's CSS import comes after Tailwind's base:
@import 'tailwindcss';
/* Crystal must come after Tailwind */
@import '@artasce/crystal-tokens/css';