Skip to content

PageSectionComponent - Consistent Page Section Wrapper

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
<%= render Www::PageSectionComponent.new(
id: 'features',
border: :bottom,
padding: 'py-4'
) do %>
<h2>Features</h2>
<p>Your content here...</p>
<% end %>
<%= page_section(id: 'features', border: :bottom) do %>
<h2>Features</h2>
<p>Your content here...</p>
<% end %>
OptionTypeDefaultDescription
idStringnilOptional section ID for anchor links
containerString’container-lg’Container class. Use nil for no container
paddingString’py-4’Vertical padding class
borderSymbol:bottomBorder style: :none, :top, :bottom, :both
backgroundSymbol/String:defaultBackground style or custom class
motionSymbol/HashnilMotion animation preset or custom options
extra_classesStringnilAdditional CSS classes for the section
dataHash{}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.

ValueCSS ClassDescription
:default(none)No background color
:whitebg-whiteWhite background
:lightbg-lightLight gray background
:mint_greenmint-green-patterned-backgroundMint green patterned background
:gradient_lightbg-gradient-lightLight gradient
:gradient_dangerbg-gradient-dangerRed/danger gradient
Custom string(your class)Any custom class

The helper method supports these motion presets:

PresetAnimationDescription
:fade_upfadeInUpFade in from below
:fade_leftfadeInLeftFade in from left
:fade_rightfadeInRightFade in from right
:zoomzoomPopZoom/pop in
:pulsepulseSubtle pulse effect

For custom motion options, pass a hash:

<%= page_section(motion: { animation: 'fadeInUp', distance: 80, duration: 0.9, stagger: 0.1 }) do %>
...
<% end %>
<%= page_section(border: :bottom) do %>
<%= page_header_h2 "Section Title" %>
<p>Content goes here...</p>
<% end %>
<%= page_section(background: :mint_green, padding: 'py-5') do %>
<%= page_header_h2 "Why Choose Us?" %>
<%= render Www::FeatureListComponent.new(...) %>
<% end %>
<%= page_section(id: 'faq', motion: :fade_up) do %>
<%= render Www::FaqListComponent.new(...) %>
<% end %>
<%= 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 %>
<%= page_section(container: nil, background: :light, padding: 'py-5') do %>
<div class="container-fluid">
<!-- Custom container/layout -->
</div>
<% end %>
<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>
<%= 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 %>

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>
ElementSpacingPurpose
PageSectionComponentpy-4 (default)Outer section padding
SectionHeaderComponentpb-3Space below header before content
Inner componentsNo wrapper paddingContent spacing handled by components themselves
border-bottomDefault on sectionsVisual separation between sections

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

Section titled “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>
  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)