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

  1. Consistency: All sections use the same padding, borders, and structure
  2. Maintainability: Change section styling in one place
  3. Animation Support: Easy to add reveal animations
  4. Fewer Errors: Less manual HTML means fewer typos/inconsistencies
  5. Documentation: Component API is self-documenting
  6. No Redundant Borders: Section borders are controlled at one level (PageSectionComponent)