Migration Guide¶
Decoupling from Existing Projects¶
This guide helps you extract Concrete utilities from your existing project without breaking anything.
Strategy Overview¶
- Audit Phase: Identify which classes are utilities vs. components
- Extraction Phase: Move utilities to Concrete framework
- Replacement Phase: Update project to use Concrete
- Cleanup Phase: Remove duplicated code
Step 1: Audit Your CSS¶
Identify Utility Classes¶
Run this script to find all utility-like classes:
# Find classes that look like utilities
grep -r "class=\"[^\"]*\b(m|p|text|bg|flex|grid|border)-" src/
Categorize Classes¶
Create a spreadsheet:
| Class Name | Type | Used In | Migrate? |
|---|---|---|---|
.border-4 |
Utility | Global | ✅ Yes |
.hero-title |
Component | hero.php | ❌ No |
.text-center |
Utility | Multiple | ✅ Yes |
Utilities to migrate: Single-purpose, reusable classes
Components to keep: Multi-property, context-specific classes
Step 2: Extract Theme Values¶
From Your Current CSS¶
To Concrete Theme File¶
// themes/clinic-theme.scss
@use 'concrete-css' with (
$color-bg: #f5f5f5,
$color-bg-alt: #ffffff,
$color-accent: #000000,
$spacing: (
'gutter': 1rem, // Custom value
// ... other values
)
);
Step 3: Create Mapping Document¶
Map your old classes to new Concrete classes:
| Old Class | New Concrete Class | Notes |
|---|---|---|
.container |
.max-w-5xl mx-auto px-4 |
Multi-class |
.btn |
.px-4 py-2 border-4 uppercase |
Base button |
.btn--primary |
+ bg-black text-white |
Variant |
.top-strip |
Custom component | Keep as-is |
Step 4: Parallel Implementation¶
Don't replace everything at once. Run both systems in parallel:
<!-- OLD (still works) -->
<link rel="stylesheet" href="assets/css/utility.css">
<!-- NEW (added alongside) -->
<link rel="stylesheet" href="assets/css/concrete.css">
Add Namespace to Old Styles (Optional)¶
// utility.css - namespace your old utilities
.legacy {
.container { /* ... */ }
.btn { /* ... */ }
}
<!-- OLD -->
<div class="legacy">
<div class="container">...</div>
</div>
<!-- NEW -->
<div class="max-w-5xl mx-auto px-4">...</div>
Step 5: Incremental Migration¶
Page-by-Page Approach¶
// header.php - Feature flag
<?php $use_concrete = true; ?>
<?php if ($use_concrete): ?>
<link rel="stylesheet" href="/css/concrete.css">
<?php else: ?>
<link rel="stylesheet" href="/css/utility.css">
<?php endif; ?>
Component-by-Component¶
<!-- OLD button -->
<button class="btn btn--primary">Click</button>
<!-- NEW button (migrate incrementally) -->
<button class="px-6 py-3 bg-black text-white border-4 uppercase font-bold hover:bg-gray-800">
Click
</button>
Step 6: Handle Custom Components¶
Keep Component Classes¶
Not everything should be utilities:
// components/hero.scss
.hero {
// Use Concrete utilities via @apply (if using PostCSS)
// OR compose in HTML
}
<!-- Option 1: Pure utilities -->
<section class="border-b-4 bg-gray-100 py-8">
<div class="max-w-5xl mx-auto px-4">
<h1 class="text-4xl uppercase font-black">Title</h1>
</div>
</section>
<!-- Option 2: Component class + utilities -->
<section class="hero">
<div class="container">
<h1 class="hero__title text-4xl">Title</h1>
</div>
</section>
Wrapper Components (Recommended)¶
For frequently used patterns:
// components/button.php
function render_button($text, $variant = 'primary') {
$classes = 'px-6 py-3 border-4 uppercase font-bold';
if ($variant === 'primary') {
$classes .= ' bg-black text-white hover:bg-gray-800';
} else {
$classes .= ' bg-white text-black hover:bg-gray-100';
}
return "<button class=\"$classes\">$text</button>";
}
Step 7: Migration Checklist¶
Pre-Migration¶
- [ ] Audit all utility classes
- [ ] Document theme values
- [ ] Create mapping document
- [ ] Set up Concrete with custom theme
- [ ] Test Concrete build
During Migration¶
- [ ] Install Concrete alongside existing CSS
- [ ] Migrate one page/component at a time
- [ ] Test each migration thoroughly
- [ ] Update documentation
- [ ] Train team on new system
Post-Migration¶
- [ ] Remove old utility CSS
- [ ] Clean up unused classes
- [ ] Optimize with PurgeCSS
- [ ] Update build process
- [ ] Archive old CSS for reference
Migration Patterns¶
Pattern 1: Direct Replacement¶
Pattern 2: Multi-Class Composition¶
<!-- Before -->
<div class="container">...</div>
<!-- After -->
<div class="max-w-5xl mx-auto px-4 md:px-6 lg:px-8">...</div>
Pattern 3: Helper Functions (PHP)¶
// helpers.php
function container_classes() {
return 'max-w-5xl mx-auto px-4 md:px-6 lg:px-8';
}
function section_classes() {
return 'border-b-4 py-6 md:py-8';
}
<div class="<?= container_classes() ?>">...</div>
<section class="<?= section_classes() ?>">...</section>
Pattern 4: React Components¶
// components/Container.jsx
export function Container({ children, className = '' }) {
return (
<div className={`max-w-5xl mx-auto px-4 md:px-6 lg:px-8 ${className}`}>
{children}
</div>
);
}
Common Pitfalls¶
❌ Don't: Replace everything at once¶
<!-- Risky: Everything breaks if something goes wrong -->
<link rel="stylesheet" href="assets/css/concrete.css">
✅ Do: Migrate incrementally¶
<!-- Safe: Both systems work -->
<link rel="stylesheet" href="assets/css/utility.css">
<link rel="stylesheet" href="assets/css/concrete.css">
❌ Don't: Ignore component classes¶
✅ Do: Keep meaningful components¶
Testing Strategy¶
Visual Regression Testing¶
# Before migration
npm run screenshots -- --output baseline/
# After migration
npm run screenshots -- --output current/
# Compare
npm run compare-screenshots
Manual Testing Checklist¶
- [ ] Desktop layout (1920x1080)
- [ ] Tablet layout (768x1024)
- [ ] Mobile layout (375x667)
- [ ] All interactive states (hover, focus, active)
- [ ] All pages render correctly
- [ ] No console errors
- [ ] Forms still work
- [ ] Navigation functions
Rollback Plan¶
Always have a way back:
# Keep old CSS in version control
git branch migration-concrete
git checkout -b migration-backup
# If something breaks
git checkout main
git cherry-pick <safe-commits>
Framework-Specific Migrations¶
From Tailwind CSS¶
Most Tailwind classes work identically:
<!-- Tailwind -->
<div class="flex items-center justify-between p-4 bg-black text-white">
<!-- Concrete (same) -->
<div class="flex items-center justify-between p-4 bg-black text-white">
Differences:
- Tailwind: border (1px)
- Concrete: border-1 (explicit width)
From Bootstrap¶
<!-- Bootstrap -->
<div class="container">
<div class="row">
<div class="col-md-6">...</div>
</div>
</div>
<!-- Concrete -->
<div class="max-w-5xl mx-auto px-4">
<div class="grid md:grid-cols-2 gap-4">
<div>...</div>
</div>
</div>
From Custom CSS¶
See full examples in examples/migration/
Support¶
If you encounter issues:
- Check the FAQ
- Search GitHub Issues
- Read migration examples in
/examples
Success Stories¶
"We migrated our 50-page clinic management system in 2 weeks using the page-by-page approach. Zero downtime." - Hospital IT Team
"Created helper functions for common patterns. Made migration painless." - PHP Developer
Your success story here!