PageSectionComponent - Consistent Page Section Wrapper
Overview
The Www::PageSectionComponent provides a standardized way to create page sections with consistent styling across all static pages. It handles:
- Container sizing (container-lg, container-fluid, etc.)
- Vertical padding (py-2, py-4, py-5)
- Border styles (top, bottom, both, none)
- Background styles (white, light, mint-green, gradients)
- Motion/reveal animations via the motion Stimulus controller
Usage
Using the ViewComponent directly
<%= render Www::PageSectionComponent.new(
id: 'features',
border: :bottom,
padding: 'py-4'
) do %>
<h2>Features</h2>
<p>Your content here...</p>
<% end %>
Using the helper method (recommended)
<%= page_section(id: 'features', border: :bottom) do %>
<h2>Features</h2>
<p>Your content here...</p>
<% end %>
Options
| Option | Type | Default | Description |
|---|---|---|---|
id |
String | nil | Optional section ID for anchor links |
container |
String | 'container-lg' | Container class. Use nil for no container |
padding |
String | 'py-4' | Vertical padding class |
border |
Symbol | :bottom | Border style: :none, :top, :bottom, :both |
background |
Symbol/String | :default | Background style or custom class |
motion |
Symbol/Hash | nil | Motion animation preset or custom options |
extra_classes |
String | nil | Additional CSS classes for the section |
data |
Hash | {} | Additional data attributes |
Note: The default border: :bottom provides consistent visual separation between sections. Override with border: :none when a component handles its own borders or when using a colored background.
Background Options
| Value | CSS Class | Description |
|---|---|---|
:default |
(none) | No background color |
:white |
bg-white |
White background |
:light |
bg-light |
Light gray background |
:mint_green |
mint-green-patterned-background |
Mint green patterned background |
:gradient_light |
bg-gradient-light |
Light gradient |
:gradient_danger |
bg-gradient-danger |
Red/danger gradient |
| Custom string | (your class) | Any custom class |
Motion Animation Presets
The helper method supports these motion presets:
| Preset | Animation | Description |
|---|---|---|
:fade_up |
fadeInUp | Fade in from below |
:fade_left |
fadeInLeft | Fade in from left |
:fade_right |
fadeInRight | Fade in from right |
:zoom |
zoomPop | Zoom/pop in |
:pulse |
pulse | Subtle pulse effect |
For custom motion options, pass a hash:
<%= page_section(motion: { animation: 'fadeInUp', distance: 80, duration: 0.9, stagger: 0.1 }) do %>
...
<% end %>
Examples
Basic section with border
<%= page_section(border: :bottom) do %>
<%= page_header_h2 "Section Title" %>
<p>Content goes here...</p>
<% end %>
Section with colored background
<%= page_section(background: :mint_green, padding: 'py-5') do %>
<%= page_header_h2 "Why Choose Us?" %>
<%= render Www::FeatureListComponent.new(...) %>
<% end %>
Section with reveal animation
<%= page_section(id: 'faq', motion: :fade_up) do %>
<%= render Www::FaqListComponent.new(...) %>
<% end %>
Two-column section with image
<%= page_section(id: 'about', border: :bottom) do %>
<div class="row gx-4 gy-3 align-items-stretch">
<div class="col-12 col-lg-5">
<%= page_header_h2 "About Section" %>
<p class="fw-light">Description text...</p>
</div>
<div class="col-12 col-lg-6 d-flex my-3 my-lg-0">
<%= image_asset_tag 'image-id', width: 1200,
class: 'img-fluid w-100 h-100 object-fit-cover rounded o-hidden' %>
</div>
</div>
<% end %>
Full-width section (no container)
<%= page_section(container: nil, background: :light, padding: 'py-5') do %>
<div class="container-fluid">
<!-- Custom container/layout -->
</div>
<% end %>
Before/After Example
Before (manual markup)
<section class="container-lg py-2" id="showcases">
<% showcases = ... %>
<%= render Www::ShowcaseGridComponent.new(
section_title: 'Projects',
showcases: showcases,
border: :bottom
) %>
</section>
<section class="container-lg py-4">
<div class="row gx-4 gy-3 align-items-stretch">
<div class="col-12 col-lg-5">
<%= page_header_h2 "Title" %>
<p>Content...</p>
</div>
<div class="col-12 col-lg-6 d-flex my-3 my-lg-0">
<%= image_asset_tag 'image', width: 1200 %>
</div>
</div>
</section>
After (using component)
<%= page_section(id: 'showcases', padding: 'py-2') do %>
<% showcases = ... %>
<%= render Www::ShowcaseGridComponent.new(
section_title: 'Projects',
showcases: showcases,
border: :bottom
) %>
<% end %>
<%= page_section do %>
<div class="row gx-4 gy-3 align-items-stretch">
<div class="col-12 col-lg-5">
<%= page_header_h2 "Title" %>
<p>Content...</p>
</div>
<div class="col-12 col-lg-6 d-flex my-3 my-lg-0">
<%= image_asset_tag 'image', width: 1200,
class: 'img-fluid w-100 h-100 object-fit-cover rounded o-hidden' %>
</div>
</div>
<% end %>
Consistent Section Structure
All sections follow this consistent structure:
<section id="section-name" class="py-4 border-bottom"> <!-- PageSectionComponent -->
<div class="container-lg"> <!-- Container -->
<div class="d-flex align-items-start pb-3"> <!-- SectionHeaderComponent -->
<div class="me-3 pt-1"><!-- icon --></div>
<div class="flex-grow-1">
<h2>Section Title</h2>
</div>
</div>
<!-- Content directly here, no extra wrapper padding -->
</div>
</section>
Spacing Rules
| Element | Spacing | Purpose |
|---|---|---|
PageSectionComponent |
py-4 (default) |
Outer section padding |
SectionHeaderComponent |
pb-3 |
Space below header before content |
| Inner components | No wrapper padding | Content spacing handled by components themselves |
border-bottom |
Default on sections | Visual separation between sections |
Related: SectionHeaderComponent
The Www::SectionHeaderComponent renders consistent section headers with optional icon and description.
Important: The header's border (<hr>) is now disabled by default since PageSectionComponent handles section borders. Only enable it when the header is used outside of a PageSectionComponent.
<%# Default: no border (used inside PageSectionComponent) %>
<%= render Www::SectionHeaderComponent.new(title: 'Products', icon: 'box') %>
<%# With border (for standalone use outside PageSectionComponent) %>
<%= render Www::SectionHeaderComponent.new(title: 'Products', icon: 'box', border: true) %>
Inner Components Should NOT Add Wrapper Padding
When creating or updating components that render inside PageSectionComponent:
DO:
<div>
<% if section_title.present? %>
<%= render Www::SectionHeaderComponent.new(title: section_title, icon: icon) %>
<% end %>
<!-- content -->
</div>
DON'T:
<div class="py-2"> <%# NO - don't add wrapper padding %>
...
</div>
Benefits
- Consistency: All sections use the same padding, borders, and structure
- Maintainability: Change section styling in one place
- Animation Support: Easy to add reveal animations
- Fewer Errors: Less manual HTML means fewer typos/inconsistencies
- Documentation: Component API is self-documenting
- No Redundant Borders: Section borders are controlled at one level (PageSectionComponent)