Template Site: Astro Components Conversion Roadmap
Section titled “Template Site: Astro Components Conversion Roadmap”Approved: 2025-11-06
Vision: Transform from Svelte-heavy architecture to Astro-first with AlpineJS for interactivity
Target Completion: 2-3 weeks
Build Status: Already stable (0 errors) after Lesson 10 conversions
Strategic Overview
Section titled “Strategic Overview”Current State
Section titled “Current State”- ✅ Header.astro - Converted, working with vanilla JS
- ✅ Footer.astro - Converted, working
- ✅ ThemeSwitcher.astro - Converted, working with vanilla JS
- ✅ Build succeeds -
pnpm buildcompletes without errors - ⚠️ Limited functionality - Pages exist but limited content/features
- ⚠️ Mobile menu - Works but could be enhanced with Alpine
- ⚠️ Theme switching - Functional but basic implementation
Vision
Section titled “Vision”90% Astro Components + 10% AlpineJS for simple, maintainable, FOSS-aligned web development
Phase 1: Foundation & Infrastructure (Week 1)
Section titled “Phase 1: Foundation & Infrastructure (Week 1)”1.1 Install & Configure AlpineJS
Section titled “1.1 Install & Configure AlpineJS”Effort: 1-2 hours
Owner: AI Assistant
Tasks:
- Install AlpineJS via package manager or CDN
- Verify it loads in dev and build
- Create Alpine utility components directory:
src/components/alpine/ - Document Alpine patterns used in this project
Checklist:
- [ ] pnpm add alpinejs (or verify existing)- [ ] Add to BaseLayout.astro scripts if not present- [ ] Test basic Alpine directive (x-data, @click) in Header- [ ] Build succeeds with Alpine codeResources:
1.2 Create Component Library Documentation
Section titled “1.2 Create Component Library Documentation”Effort: 2-3 hours
Owner: AI Assistant + User Review
Create: docs/COMPONENT_LIBRARY.md
Contents:
- Component naming conventions
- When to use Astro vs Alpine vs Svelte
- File structure and organization
- Code examples for each tier
- TypeScript/props patterns for Astro components
Example section structure:
## Component Decision Tree
Does component need to be interactive?├─ NO → Use Astro component├─ YES → Change after page load? ├─ NO (only on user interaction) → AlpineJS └─ YES (real-time updates) → Consider Svelte/React1.3 Create Alpine Component Wrapper Pattern
Section titled “1.3 Create Alpine Component Wrapper Pattern”Effort: 1-2 hours
Owner: AI Assistant
Create template: src/components/alpine/AlpineComponentBase.astro
---/** * Reusable pattern for Alpine-powered Astro components * * Usage: * <AlpineDropdown items={items} /> */
interface Props { id?: string; class?: string;}
const { id = '', class: className = '' } = Astro.props;---
<div id={id} class={className} x-data="componentName()" x-init="init()"> <slot /></div>
<script define:vars={{ /* any vars from frontmatter */ }}> window.componentName = function() { return { // Alpine state here init() { // Initialize } } }</script>
<style scoped> /* Component styles */</style>Phase 2: Convert Existing Svelte Components (Week 1-2)
Section titled “Phase 2: Convert Existing Svelte Components (Week 1-2)”2.1 Audit Remaining Svelte Components
Section titled “2.1 Audit Remaining Svelte Components”Effort: 1 hour
Owner: AI Assistant
List all .svelte files currently in use:
find src/components -name "*.svelte" -type fExpected list:
- List generated and categorized by purpose
Categorize by:
- Tier 1 - Pure Astro (no interactivity needed)
- Tier 2 - Alpine candidates (simple interactivity)
- Tier 3 - Keep as Svelte (genuinely complex)
2.2 Convert Tier 1 Components (Pure Astro)
Section titled “2.2 Convert Tier 1 Components (Pure Astro)”Effort: 3-4 hours
Owner: AI Assistant
Target Components:
- Button.svelte → Button.astro
- Card.svelte → Card.astro
- Hero.svelte → Hero.astro
- Feature.svelte → Feature.astro
- Price.svelte → Price.astro
- Stats.svelte → Stats.astro
- Grid.svelte → Grid.astro
- Container.svelte → Container.astro
- Section.svelte → Section.astro
- Badge.svelte → Badge.astro
- Alert.svelte → Alert.astro
Pattern for conversion:
<script lang="ts"> interface Props { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; } const { variant = 'primary', size = 'md' } = $props();</script>
<button class={`btn btn-${variant} btn-${size}`}> <slot /></button>
<style> .btn { /* styles */ }</style>---interface Props { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; class?: string;}const { variant = 'primary', size = 'md', class: className = '' } = Astro.props;---
<button class={`btn btn-${variant} btn-${size} ${className}`} {...Astro.props}> <slot /></button>
<style> .btn { /* styles */ }</style>For each component:
- Create
.astroversion - Copy props interface and styling
- Remove Svelte-specific code (script, runes, etc.)
- Test in dev with
pnpm dev - Verify build passes with
pnpm build - Delete original
.sveltefile
2.3 Convert Tier 2 Components (Alpine Enhancement)
Section titled “2.3 Convert Tier 2 Components (Alpine Enhancement)”Effort: 4-5 hours
Owner: AI Assistant
Target Components:
- Dropdown.svelte → Dropdown.astro (with Alpine)
- Modal.svelte → Modal.astro (with Alpine)
- Tabs.svelte → Tabs.astro (with Alpine)
- Accordion.svelte → Accordion.astro (with Alpine)
- Navbar toggle → Use Alpine for mobile menu
- Form validation → Alpine directives
Pattern for Alpine conversion:
---interface Props { trigger: string; items: string[]; class?: string;}const { trigger, items, class: className = '' } = Astro.props;---
<div class={`dropdown ${className}`} x-data="dropdown()" @click.away="open = false"> <button @click="open = !open" class="dropdown-trigger"> {trigger} </button>
<ul x-show="open" class="dropdown-menu"> {items.map(item => <li><a href="#">{item}</a></li>)} </ul></div>
<script> window.dropdown = function() { return { open: false } }</script>
<style> .dropdown { position: relative; } .dropdown-menu { position: absolute; top: 100%; left: 0; } [x-show] { display: none; } [x-show][style*="display"] { display: block; }</style>For each component:
- Create
.astroversion with Alpine - Copy HTML structure
- Replace Svelte reactivity with Alpine directives
- Add Alpine state object (x-data)
- Test interactivity in dev
- Verify build passes
- Delete original
.sveltefile
2.4 Decide on Tier 3 Components (Keep or Assess)
Section titled “2.4 Decide on Tier 3 Components (Keep or Assess)”Effort: 1-2 hours
Owner: User Review + AI Assessment
Decision for each:
- If functionality can be achieved with Alpine → Convert to Astro + Alpine
- If functionality truly requires complex state → Keep as Svelte (but isolate)
- If functionality not used → Delete
Criteria:
- Does it need real-time reactivity?
- Does it have complex state dependencies?
- Is ecosystem support critical?
Expected outcome:
- Minimal or no Svelte components remain
- All build friction eliminated
Phase 3: Enhance Template Pages (Week 2)
Section titled “Phase 3: Enhance Template Pages (Week 2)”3.1 Recreate Documentation Pages
Section titled “3.1 Recreate Documentation Pages”Effort: 2-3 hours
Owner: AI Assistant
Pages to create:
-
/docs- Overview.md content (text container: 25rem-80rem) -
/components- Component showcase with examples -
/blocks- Block showcase with categories -
/pages- Page templates showcase
Each page should:
- Use Astro components exclusively
- Implement proper text container widths (25rem-80rem min/max)
- Include example usage and code snippets
- Support theme switching (via ThemeSwitcher Alpine component)
3.2 Implement Enhanced Theme Switcher with AlpineJS
Section titled “3.2 Implement Enhanced Theme Switcher with AlpineJS”Effort: 2-3 hours
Owner: AI Assistant
Current: Basic theme cycling (Light/Dark/System)
Target: Dropdown with brand colors (Blue, Hunter Green, Gold)
Features:
- Dropdown menu with color options
- Visual indicator of current theme
- Keyboard navigation (Arrow keys, Enter)
- Persists to localStorage
- Smooth transitions
- Accessible (ARIA labels, focus states)
Implementation:
---// components/ThemeSwitcher.astro - Enhanced version---
<div class="theme-switcher" x-data="themeSwitcher()" @click.away="dropdownOpen = false"> <button @click="dropdownOpen = !dropdownOpen" class="theme-button" :aria-label="`Current theme: ${currentTheme}`" > <span x-text="themeLabel"></span> <span class="dropdown-arrow">▼</span> </button>
<div x-show="dropdownOpen" class="theme-dropdown"> <button @click="setTheme('light')" :class="{ active: currentTheme === 'light' }" > ☀️ Light </button> <button @click="setTheme('dark')" :class="{ active: currentTheme === 'dark' }" > 🌙 Dark </button> <button @click="setTheme('system')" :class="{ active: currentTheme === 'system' }" > 🔄 System </button>
<hr class="theme-divider" />
<button @click="setColor('blue')" :class="{ active: currentColor === 'blue' }" > 🔵 Blue (Primary) </button> <button @click="setColor('green')" :class="{ active: currentColor === 'green' }" > 💚 Hunter Green </button> <button @click="setColor('gold')" :class="{ active: currentColor === 'gold' }" > 🟡 Gold </button> </div></div>
<script define:vars={{ colors: { blue: '#3B82F6', green: '#355E3B', gold: '#E6BA1D' } }}> window.themeSwitcher = function() { return { dropdownOpen: false, currentTheme: 'system', currentColor: 'blue', themeLabel: 'System',
init() { this.currentTheme = localStorage.getItem('theme-preference') || 'system'; this.currentColor = localStorage.getItem('color-preference') || 'blue'; this.updateLabel(); },
setTheme(theme) { this.currentTheme = theme; localStorage.setItem('theme-preference', theme); this.applyTheme(); this.updateLabel(); },
setColor(color) { this.currentColor = color; localStorage.setItem('color-preference', color); this.applyColor(); },
updateLabel() { const labels = { light: '☀️ Light', dark: '🌙 Dark', system: '🔄 System' }; this.themeLabel = labels[this.currentTheme] || 'System'; },
applyTheme() { const actual = this.currentTheme === 'system' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : this.currentTheme; document.documentElement.setAttribute('data-theme', actual); },
applyColor() { document.documentElement.setAttribute('data-color', this.currentColor); } } }</script>
<style> .theme-switcher { position: relative; display: inline-block; }
.theme-button { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: hsl(var(--background)); border: 1px solid hsl(var(--border)); border-radius: var(--radius); color: hsl(var(--foreground)); cursor: pointer; transition: all 0.2s; }
.theme-button:hover { background: hsl(var(--accent)); }
.dropdown-arrow { font-size: 0.75em; transition: transform 0.2s; }
.theme-dropdown { position: absolute; top: 100%; left: 0; margin-top: 0.5rem; background: hsl(var(--background)); border: 1px solid hsl(var(--border)); border-radius: var(--radius); box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 1000; min-width: 150px; }
.theme-dropdown button { display: block; width: 100%; padding: 0.75rem 1rem; text-align: left; background: none; border: none; cursor: pointer; transition: background 0.2s; }
.theme-dropdown button:hover { background: hsl(var(--accent)); }
.theme-dropdown button.active { background: hsl(var(--accent)); font-weight: 500; }
.theme-divider { margin: 0.5rem 0; border: none; border-top: 1px solid hsl(var(--border)); }</style>3.3 Improve Mobile Menu with Alpine
Section titled “3.3 Improve Mobile Menu with Alpine”Effort: 2-3 hours
Owner: AI Assistant
Current: Vanilla JS works but basic
Target: Smooth Alpine-powered experience with keyboard support
Features:
- Smooth open/close animation with Alpine
- Keyboard navigation (Escape to close)
- Focus trap in open menu
- Chevron animation
- Submenu handling
- Touch/click outside to close
Phase 4: Content & Features (Week 2-3)
Section titled “Phase 4: Content & Features (Week 2-3)”4.1 Create Block Showcase Pages
Section titled “4.1 Create Block Showcase Pages”Effort: 3-4 hours
Owner: User + AI
Pages to create:
/blocks/hero- Hero variants with hr separators/blocks/feature- Feature block variants/blocks/cta- Call-to-action variants/blocks/stats- Statistics block variants/blocks/testimonials- Testimonial variants/blocks/utility- Utility/misc blocks
Each page:
- Display 3-5 variants of each block
- Clear visual separation (use
<hr>as per requirements) - Live examples with copy-able code
- Responsive preview
4.2 Create Page Template Showcase
Section titled “4.2 Create Page Template Showcase”Effort: 3-4 hours
Owner: User + AI
Pages to create:
/pages/homepage- Homepage template/pages/about- About page template/pages/contact- Contact page template/pages/services- Services page template/pages/error- 404 error page template
Each page:
- Complete template with realistic content
- All required components
- Responsive design
- Accessibility features
4.3 Implement Text Container Widths
Section titled “4.3 Implement Text Container Widths”Effort: 1-2 hours
Owner: AI Assistant
Apply to all pages:
- Min width:
25rem(400px) - Max width:
80rem(1280px) - Already defined in
design-tokens.css - Use CSS variable:
var(--container-text)
Implementation pattern:
<div class="container" style="max-width: var(--container-text)"> <!-- content --></div>Verification:
- Test at 320px viewport
- Test at 1600px viewport
- Content remains readable
- Line length appropriate
Phase 5: Testing & Optimization (Week 3)
Section titled “Phase 5: Testing & Optimization (Week 3)”5.1 Comprehensive Testing
Section titled “5.1 Comprehensive Testing”Effort: 2-3 hours
Owner: User Review
Checklist:
- Dev server works:
pnpm dev - Build succeeds:
pnpm build - All pages load without errors
- No console errors
- Theme switching works (all 6 options)
- Mobile menu works (all sizes)
- Responsive design (320px, 768px, 1024px, 1400px)
- Accessibility (keyboard nav, screen reader compatible)
- Performance (Lighthouse score > 90)
5.2 Performance Optimization
Section titled “5.2 Performance Optimization”Effort: 1-2 hours
Owner: AI Assistant
Check:
- Bundle size
- Load time
- Unused CSS removal
- Image optimization
- Alpine code splitting (if needed)
Tools:
- Astro’s built-in performance metrics
- Lighthouse
- WebPageTest
5.3 Documentation
Section titled “5.3 Documentation”Effort: 1-2 hours
Owner: AI Assistant
Update:
- COMPONENT_LIBRARY.md - Add all new components
- README - Update tech stack section
- Component storybook/catalog
- Migration guide for future developers
Success Criteria
Section titled “Success Criteria”Build Health
Section titled “Build Health”- ✅ Build succeeds with zero errors
- ✅ Dev server runs smoothly
- Dev build time < 5 seconds
- Production build time < 15 seconds
Architecture
Section titled “Architecture”- 90%+ of components are Astro
- Alpine used only for necessary interactivity
- Zero Svelte components (unless justified)
- Clean file organization
Functionality
Section titled “Functionality”- All pages render correctly
- Theme switching works (6 options)
- Mobile menu functions smoothly
- Text containers properly constrained (25rem-80rem)
- No build warnings
Performance
Section titled “Performance”- Bundle size < 50KB (excluding images)
- Largest Contentful Paint < 2.5s
- Cumulative Layout Shift < 0.1
- Lighthouse score > 90 (all metrics)
User Experience
Section titled “User Experience”- Responsive at all breakpoints
- Accessible (WCAG AA compliant)
- Keyboard navigable
- Screen reader compatible
Implementation Notes
Section titled “Implementation Notes”Best Practices
Section titled “Best Practices”- Test after each component: Don’t batch all conversions
- Verify build: Run
pnpm buildfrequently - Document decisions: Add comments explaining why Astro vs Alpine
- Keep it simple: Don’t over-engineer components
Potential Challenges
Section titled “Potential Challenges”- Alpine learning curve - But very quick to learn
- Component state management - Use simple Alpine objects
- CSS specificity - Use Astro’s scoped styles
Rollback Strategy
Section titled “Rollback Strategy”- Keep git history clean
- Commit after each phase completes
- Tag stable versions
Timeline Summary
Section titled “Timeline Summary”| Phase | Duration | Status |
|---|---|---|
| Phase 1: Foundation | 1 week | Pending |
| Phase 2: Component Conversion | 1 week | Pending |
| Phase 3: Enhance Pages | 1 week | Pending |
| Phase 4: Content & Features | 1 week | Pending |
| Phase 5: Testing & Optimization | 3-5 days | Pending |
| Total | 2-3 weeks | Not Started |
Decision Framework
Section titled “Decision Framework”When choosing between Astro/Alpine/Svelte:
Section titled “When choosing between Astro/Alpine/Svelte:”Component needs to change?├─ NO → Use Astro└─ YES → Change only from user interaction? ├─ YES → Alpine └─ NO (real-time/complex) → Svelte (last resort)
Alpine sufficient? ├─ YES → Use Alpine └─ NO (complex state) → SvelteAppendix: AlpineJS Quick Reference
Section titled “Appendix: AlpineJS Quick Reference”<!-- State --><div x-data="{ open: false }"> <button @click="open = !open">Toggle</button></div>
<!-- Conditional Rendering --><div x-show="open">Shown when open is true</div><template x-if="open"><div>DOM not created if false</div></template>
<!-- Text Content --><div x-text="message">Default text</div>
<!-- Attributes --><div :aria-label="`Item ${count}`"></div>
<!-- Classes --><div :class="{ active: isActive }"></div>
<!-- Listeners --><button @click="doSomething()" @focus="onFocus()"></button>
<!-- Initialization --><div x-init="() => console.log('Ready')"></div>
<!-- Watch --><div x-data="{ count: 0 }" x-init="$watch('count', () => console.log('Changed'))"></div>Approval Checklist
Section titled “Approval Checklist”- User reviews and approves roadmap
- Timeline is realistic
- Success criteria are clear
- Team understands philosophy
- Begin Phase 1
Date Approved: _______________
Approved By: _______________