Building Flexible Modules
Introduction
Section titled “Introduction”Now let’s compose our CardTest component into a reusable flexible module. Flexible modules are section-level layouts that combine components, grid systems, and spacing to create complete page sections.
Module Structure
Section titled “Module Structure”Create: src/flexible/modules/ModuleExample.astro
---import CardTest from "@components/card/CardTest.astro";import { spacingConverter, getLine } from "@utilities/astro";
const { payload: { spacing, line, key }, language,} = Astro.props;
const spaces = spacingConverter(spacing);const lineClass = getLine(line);---
<section class:list={[spaces, lineClass]} id={key}> <div class="f--container"> <div class="f--row f--gap-a"> <!-- Header Section --> <div class="f--col-12"> <h2 class="f--font-c u--font-semibold f--sp-d"> Welcome to Our Services </h2> <p class="f--font-g"> Discover how we can help transform your business with our innovative solutions. </p> </div>
<!-- Card 1 --> <div class="f--col-6 f--col-tablets-12"> <CardTest payload={{ language, title: "Cloud Security", subtitle: "Protect your data with enterprise-grade security solutions.", media: { type: "Image", url: "/images/img-desktop_1.webp", alt: "Cloud Security Icon", }, link: { label: "Learn More", url: "/services/cloud-security", target: "_self", }, }} /> </div>
<!-- Card 2 --> <div class="f--col-6 f--col-tablets-12"> <CardTest payload={{ language, title: "Data Analytics", subtitle: "Make informed decisions with powerful data insights.", media: { type: "Image", url: "/images/img-desktop_2.webp", alt: "Data Analytics Icon", }, link: { label: "Explore", url: "/services/data-analytics", target: "_self", }, }} /> </div> </div> </div></section>Understanding the Module
Section titled “Understanding the Module”Section Structure
Section titled “Section Structure”<section class:list={[spaces, lineClass]} id={key}> <div class="f--container"> <div class="f--row f--gap-a"> <!-- content --> </div> </div></section>Hierarchy:
<section>- Module wrapper with spacing and line styling.f--container- Max-width container (centers content).f--row- Grid row (flex container).f--col-*- Grid columns (responsive width)
📚 Learn more about Grid System in Chapter 2-4
Spacing System
Section titled “Spacing System”const spaces = spacingConverter(spacing);const lineClass = getLine(line);spacingConverter generates spacing classes:
spacing: { top: 10, bottom: 7 }→"u--pt-10 u--pb-7"- Uses the measure-based spacing scale (0, 1, 2, 3, 4, 5, 7, 8, 10, 15)
getLine generates divider line classes:
line: "top"→"u--line-top"- Options:
"top","bottom","both",false
Spacing Example:
<!-- Module with top spacing of 80px and bottom spacing of 56px -->payload={{ spacing: { top: 10, bottom: 7 }, line: "bottom", key: "services-section"}}Grid Classes
Section titled “Grid Classes”<div class="f--col-6 f--col-tablets-12">Responsive Column Pattern:
.f--col-6- 50% width on desktop (6 of 12 columns).f--col-tablets-12- 100% width on tablets and below
Common Patterns:
Two Columns:
<div class="f--col-6 f--col-tablets-12">Three Columns:
<div class="f--col-4 f--col-tablets-6 f--col-mobile-12">Four Columns:
<div class="f--col-3 f--col-tabletl-6 f--col-tablets-12">Full Width:
<div class="f--col-12">Gap Utilities
Section titled “Gap Utilities”<div class="f--row f--gap-a">Gap Scale:
.f--gap-a-$measure * 5(40px).f--gap-b-$measure * 4(32px).f--gap-c-$measure * 3(24px).f--gap-d-$measure * 2(16px)
When to Use:
.f--gap-a- Large spacing between elements (default for sections).f--gap-b- Medium spacing.f--gap-c- Small spacing.f--gap-d- Minimal spacing
Module Best Practices
Section titled “Module Best Practices”✅ Do’s
Section titled “✅ Do’s”1. Use semantic section IDs
<section id={key}> <!-- Dynamic from CMS --><section id="services-overview"> <!-- Static/hardcoded -->2. Keep modules flexible
- Accept dynamic content via props
- Support multiple configurations
- Handle edge cases (0 items, 1 item, etc.)
3. Use grid for responsive layout
<div class="f--col-6 f--col-tablets-12">4. Apply consistent spacing
<h2 class="f--sp-d">Title</h2> <!-- Spacing bottom -->5. Maintain typography hierarchy
<h2 class="f--font-c u--font-semibold">Main Heading</h2><p class="f--font-g">Description text</p>❌ Don’ts
Section titled “❌ Don’ts”1. Don’t hardcode spacing - use utilities
<!-- ❌ Bad --><section style="padding: 80px 0 56px 0;">
<!-- ✅ Good --><section class:list={[spaces, lineClass]}>2. Don’t skip responsive classes
<!-- ❌ Bad - Won't stack on mobile --><div class="f--col-6">
<!-- ✅ Good --><div class="f--col-6 f--col-tablets-12">3. Don’t nest sections within sections
<!-- ❌ Bad --><section> <section> Content </section></section>
<!-- ✅ Good --><section> <div> Content </div></section>4. Don’t forget semantic HTML
<!-- ❌ Bad --><div> <div>Title</div> <div>Description</div></div>
<!-- ✅ Good --><section> <h2>Title</h2> <p>Description</p></section>Dynamic Data Pattern
Section titled “Dynamic Data Pattern”For production modules receiving data from CMS, use this pattern:
---const { payload: { spacing, line, key, title, // ← Dynamic from CMS subtitle, // ← Dynamic from CMS cards, // ← Array of card data from CMS }, language,} = Astro.props;
const spaces = spacingConverter(spacing);const lineClass = getLine(line);---
<section class:list={[spaces, lineClass]} id={key}> <div class="f--container"> <div class="f--row f--gap-a"> {title && ( <div class="f--col-12"> <h2 class="f--font-c u--font-semibold f--sp-d">{title}</h2> {subtitle && <p class="f--font-g">{subtitle}</p>} </div> )}
{cards?.map((card) => ( <div class="f--col-6 f--col-tablets-12"> <CardTest payload={{ language, ...card, // Spread card data from CMS }} /> </div> ))} </div> </div></section>Advanced Grid Patterns
Section titled “Advanced Grid Patterns”Asymmetric Layouts
Section titled “Asymmetric Layouts”<div class="f--row f--gap-a"> <!-- Large featured item --> <div class="f--col-8 f--col-tablets-12"> <CardTest payload={featuredCard} /> </div>
<!-- Smaller sidebar --> <div class="f--col-4 f--col-tablets-12"> <CardTest payload={sidebarCard} /> </div></div>Mixed Column Widths
Section titled “Mixed Column Widths”<div class="f--row f--gap-a"> <!-- Header --> <div class="f--col-12"> <h2>Title</h2> </div>
<!-- Three equal columns --> <div class="f--col-4 f--col-tablets-12"> <CardTest payload={card1} /> </div> <div class="f--col-4 f--col-tablets-12"> <CardTest payload={card2} /> </div> <div class="f--col-4 f--col-tablets-12"> <CardTest payload={card3} /> </div></div>Nested Grids
Section titled “Nested Grids”<div class="f--row f--gap-a"> <div class="f--col-6 f--col-tablets-12"> <!-- Nested row inside column --> <div class="f--row f--gap-c"> <div class="f--col-6"> <CardTest payload={card1} /> </div> <div class="f--col-6"> <CardTest payload={card2} /> </div> </div> </div></div>Summary & Workflow
Section titled “Summary & Workflow”What You Accomplished
Section titled “What You Accomplished”✅ Created CardTest Component
- Learned Astro component structure
- Used Asset and Btn custom components
- Followed BEM naming conventions
- Implemented conditional rendering
✅ Styled with SCSS
- Used Foundation tokens (colors, typography, spacing)
- Applied utility classes via @extend
- Implemented responsive design
- Added proper hover states
✅ Added JavaScript Behavior
- Built CustomAnimation class following Terra’s pattern
- Used GSAP for smooth animations
- Integrated ScrollTrigger for viewport detection
- Created Handler for lifecycle management
- Registered everything in the framework
✅ Built ModuleExample
- Composed components into a flexible module
- Used the grid system for responsive layout
- Applied spacing and gap utilities
- Structured semantic HTML
The Complete Workflow
Section titled “The Complete Workflow”1. Create Component (CardTest.astro) ↓2. Add SCSS Styles (_c--card-test.scss) ↓3. Register SCSS (style.scss) ↓4. Create Animation Class (CustomAnimation.js) ↓5. Create Handler (CustomAnimationHandler.js) ↓6. Register Animation Asset (extraAssets.js) ↓7. Register Handler (Main.js) ↓8. Allocate Instances (Project.js) ↓9. Build Module (ModuleExample.astro) ↓10. Test & IterateKey Concepts Review
Section titled “Key Concepts Review”Component Development:
- Props destructuring from
payload - BEM naming convention (
c--block__element) - Conditional rendering patterns
- Component composition
SCSS Architecture:
- Foundation token system
- Utility class patterns
- Responsive breakpoints
- Hover state best practices
JavaScript Framework:
- Terra’s four-pillar class structure
- GSAP timeline management
- ScrollTrigger integration
- Handler lifecycle management
- Manager instance tracking
Module Composition:
- Grid system usage
- Spacing utilities
- Semantic HTML structure
- Dynamic vs static data patterns
Next Steps
Section titled “Next Steps”Beginner Path
Section titled “Beginner Path”- 📖 Review Style Foundations in depth
- 🎨 Experiment with different color and typography combinations
- ✨ Try creating variations of CardTest with modifiers
- 📱 Test responsive behavior on different devices
Intermediate Path
Section titled “Intermediate Path”- 🧩 Create more complex components (sliders, modals, accordions)
- 🎬 Build custom GSAP animations and transitions
- 🔄 Understand SWUP lifecycle in depth
- 📦 Explore existing modules for patterns
Advanced Path
Section titled “Advanced Path”- ⚡ Optimize performance with Boostify
- 🏗️ Architect complex multi-component modules
- 🎯 Create reusable animation systems
- 🚀 Contribute to the framework architecture
Testing Checklist
Section titled “Testing Checklist”Manual Testing:
- Component renders on initial page load
- Styles match design specifications
- Responsive behavior works on all breakpoints
- Animations trigger at correct scroll position
- Hover states work correctly on desktop
- No hover issues on touch devices
- Component survives SWUP page transition
- No console errors
- No memory leaks (check Performance tab)
- Accessibility (keyboard navigation, ARIA)
- Works in all supported browsers
Performance Checklist:
- Images optimized and lazy loaded
- Animations run at 60fps
- No layout shifts (CLS)
- Fast initial render (LCP)
- Proper code splitting implemented
Quick Reference
Section titled “Quick Reference”File Locations
Section titled “File Locations”Component: src/components/card/CardTest.astroSCSS: src/scss/framework/components/card/_c--card-test.scssSCSS Registration: src/scss/style.scssAnimation Class: src/scripts/module/CustomAnimation.jsHandler: src/scripts/handler/customAnimation/CustomAnimationHandler.jsAsset Definition: src/scripts/preload/extraAssets.jsHandler Registry: src/scripts/Main.jsInstance Pool: src/scripts/Project.jsModule: src/flexible/modules/ModuleExample.astroCommon Classes
Section titled “Common Classes”// Layout.f--container // Max-width container.f--row // Flex row.f--col-6 // 6 columns (50%).f--col-tablets-12 // 12 columns on tablets
// Spacing.u--pt-10 // Padding top 80px.u--pb-7 // Padding bottom 56px.f--sp-d // Spacing bottom (gap)
// Typography.f--font-c // Font size C (~32px).f--font-d // Font size D (~24px).f--font-g // Font size G (~16px).u--font-semibold // Font weight 600
// Colors.f--color-a // Navy (primary).f--color-h // Gray (text)Conclusion
Section titled “Conclusion”You now have a complete understanding of the NETWRIX component-to-module workflow! 🎉
Remember the key principles:
- Always use Foundation tokens before writing custom CSS
- Follow Terra’s four-pillar class structure
- Implement destroy methods for all interactive components
- Test responsive behavior across all breakpoints
- Use the grid system for consistent layouts
Keep building! The best way to master these patterns is through practice.
Happy coding! 🚀