Component Example

This guide provides a complete, working example of a Hero component with all required files and best practices.

Component Overview

The Hero component displays a large banner with:

  • Background image
  • Heading and subheading
  • Optional call-to-action button
  • Configurable text alignment
  • Responsive design

File Structure

hero/

├── details.json

├── dialog.json

├── component.html

├── component.css

└── variables.json

details.json

{

  "name": "Hero Banner",

  "description": "A large hero banner with image, heading, and call-to-action",

  "purpose": "Display prominent content at the top of pages to capture attention and drive action",

  "optionsDescription": "Configure background image, heading, subheading, text alignment, and optional button with link"

}

dialog.json

{

  "schemaVersion": "1.0",

  "fieldGroups": [

    {

      "label": "Content",

      "fields": [

        {

          "type": "text",

          "name": "heading",

          "label": "Heading",

          "required": true,

          "maxLength": 100,

          "placeholder": "Enter a compelling heading",

          "defaultValue": "Welcome to Our Site"

        },

        {

          "type": "textarea",

          "name": "subheading",

          "label": "Subheading",

          "required": false,

          "rows": 3,

          "maxLength": 200,

          "placeholder": "Enter a subheading (optional)"

        },

        {

          "type": "image",

          "name": "backgroundImage",

          "label": "Background Image",

          "required": true,

          "defaultValue": {

            "src": "",

            "alt": "Hero background"

          }

        }

      ]

    },

    {

      "label": "Call to Action",

      "fields": [

        {

          "type": "boolean",

          "name": "showButton",

          "label": "Show Button",

          "defaultValue": false

        },

        {

          "type": "text",

          "name": "buttonText",

          "label": "Button Text",

          "required": false,

          "maxLength": 30,

          "defaultValue": "Learn More",

          "showIf": {

            "field": "showButton",

            "equals": true

          }

        },

        {

          "type": "pageLink",

          "name": "buttonLink",

          "label": "Button Link",

          "required": false,

          "showIf": {

            "field": "showButton",

            "equals": true

          }

        }

      ]

    },

    {

      "label": "Styling",

      "fields": [

        {

          "type": "select",

          "name": "textAlignment",

          "label": "Text Alignment",

          "required": true,

          "defaultValue": "center",

          "options": [

            { "value": "left", "label": "Left" },

            { "value": "center", "label": "Center" },

            { "value": "right", "label": "Right" }

          ]

        },

        {

          "type": "select",

          "name": "overlayOpacity",

          "label": "Overlay Opacity",

          "required": true,

          "defaultValue": "medium",

          "options": [

            { "value": "light", "label": "Light (20%)" },

            { "value": "medium", "label": "Medium (40%)" },

            { "value": "dark", "label": "Dark (60%)" }

          ]

        }

      ]

    }

  ]

}

component.html

<section class="hero hero-align-{{textAlignment}}">

  <div class="hero-background">

    <img src="{{backgroundImage.src}}" alt="{{backgroundImage.alt}}" class="hero-image" />

    <div class="hero-overlay hero-overlay-{{overlayOpacity}}"></div>

  </div>

  

  <div class="hero-content">

    <div class="hero-text">

      <h1 class="hero-heading">{{heading}}</h1>

      

      {{#if subheading}}

        <p class="hero-subheading">{{subheading}}</p>

      {{/if}}

      

      {{#if showButton}}

        {{#if buttonLink}}

          <div class="hero-actions">

            <a href="{{buttonLink}}" class="hero-button">{{buttonText}}</a>

          </div>

        {{/if}}

      {{/if}}

    </div>

  </div>

</section>

component.css

.hero {

  position: relative;

  min-height: var(--hero-min-height, 500px);

  display: flex;

  align-items: center;

  justify-content: center;

  overflow: hidden;

}

 

.hero-background {

  position: absolute;

  inset: 0;

  z-index: 0;

}

 

.hero-image {

  width: 100%;

  height: 100%;

  object-fit: cover;

}

 

.hero-overlay {

  position: absolute;

  inset: 0;

  background-color: rgba(0, 0, 0, 0.4);

}

 

.hero-overlay-light {

  background-color: rgba(0, 0, 0, 0.2);

}

 

.hero-overlay-medium {

  background-color: rgba(0, 0, 0, 0.4);

}

 

.hero-overlay-dark {

  background-color: rgba(0, 0, 0, 0.6);

}

 

.hero-content {

  position: relative;

  z-index: 1;

  width: 100%;

  max-width: var(--container-max-width);

  padding: var(--spacing-2xl) var(--container-padding);

}

 

.hero-text {

  color: white;

}

 

.hero-align-left .hero-text {

  text-align: left;

}

 

.hero-align-center .hero-text {

  text-align: center;

}

 

.hero-align-right .hero-text {

  text-align: right;

}

 

.hero-heading {

  font-family: var(--font-family-heading);

  font-size: 3rem;

  font-weight: var(--font-weight-bold);

  line-height: var(--line-height-heading);

  margin: 0 0 var(--spacing-md) 0;

  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);

}

 

.hero-subheading {

  font-family: var(--font-family-base);

  font-size: var(--font-size-xl);

  line-height: var(--line-height-base);

  margin: 0 0 var(--spacing-xl) 0;

  opacity: 0.95;

  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);

}

 

.hero-actions {

  margin-top: var(--spacing-xl);

}

 

.hero-button {

  display: inline-block;

  padding: var(--spacing-md) var(--spacing-xl);

  background-color: var(--color-primary);

  color: white;

  font-family: var(--font-family-base);

  font-size: var(--font-size-base);

  font-weight: var(--font-weight-medium);

  text-decoration: none;

  border-radius: var(--border-radius-md);

  transition: background-color 0.2s ease, transform 0.2s ease;

  box-shadow: var(--shadow-md);

}

 

.hero-button:hover {

  background-color: var(--color-secondary);

  transform: translateY(-2px);

  box-shadow: var(--shadow-lg);

}

 

.hero-button:active {

  transform: translateY(0);

}

 

/* Responsive adjustments */

@media (max-width: 767px) {

  .hero {

    min-height: var(--hero-min-height-mobile, 400px);

  }

  

  .hero-heading {

    font-size: 2rem;

  }

  

  .hero-subheading {

    font-size: var(--font-size-lg);

  }

  

  .hero-content {

    padding: var(--spacing-xl) var(--container-padding);

  }

}

 

@media (min-width: 768px) and (max-width: 1023px) {

  .hero-heading {

    font-size: 2.5rem;

  }

}

variables.json

{

  "variables": [

    {

      "name": "--hero-min-height",

      "category": "layout",

      "description": "Minimum height of hero banner on desktop",

      "defaultValue": "500px"

    },

    {

      "name": "--hero-min-height-mobile",

      "category": "layout",

      "description": "Minimum height of hero banner on mobile devices",

      "defaultValue": "400px"

    }

  ]

}

Packaging the Component

Create a zip file:

zip -r hero.zip hero/

Or on Windows:

Compress-Archive -Path hero -DestinationPath hero.zip

Uploading the Component

  1. Navigate to Components in the Admin Tool
  2. Click Upload Component
  3. Select hero.zip
  4. Wait for validation
  5. Review the validation results
  6. Click Confirm Upload

Testing the Component

Create a Test Page

  1. Navigate to Sites → Select a site → Pages
  2. Click Create Page
  3. Fill in page details and select a template
  4. Click Create

Add the Hero Component

  1. In the page editor, find the Hero Banner in the component palette
  2. Drag it into a content zone
  3. Click the component to open the property dialog
  4. Fill in the fields:
    • Heading: "Welcome to Our Site"
    • Subheading: "Discover amazing content and features"
    • Background Image: Upload an image
    • Show Button: Enable
    • Button Text: "Get Started"
    • Button Link: Select a page
    • Text Alignment: Center
    • Overlay Opacity: Medium
  1. Click Save

Preview and Publish

  1. Check the live preview to see the component
  2. Test different field combinations
  3. Test on mobile, tablet, and desktop
  4. Click Publish when satisfied

Customization Examples

Different Overlay Opacities

Users can choose from three overlay opacity levels:

  • Light (20%): For bright images where text is still readable
  • Medium (40%): Balanced overlay for most images
  • Dark (60%): For very bright images or when maximum text contrast is needed

Text Alignment Options

Users can align text left, center, or right:

  • Left: Traditional alignment, good for longer text
  • Center: Balanced, works well for short, impactful messages
  • Right: Unique alignment, creates visual interest

Responsive Behavior

The component automatically adapts to different screen sizes:

  • Mobile (<768px): Smaller heading, reduced padding, shorter min-height
  • Tablet (768-1023px): Medium heading size
  • Desktop (≥1024px): Full-size heading and spacing

Best Practices Demonstrated

Accessibility

  • Alt text: Background image has alt text
  • Semantic HTML: Uses <section>, <h1>, <p>, <a>
  • Color contrast: Dark overlay ensures text is readable
  • Keyboard navigation: Button is a standard <a> element

Performance

  • Optimized CSS: Uses efficient selectors
  • CSS variables: Leverages design variables for consistency
  • Responsive images: Uses object-fit: cover for proper scaling
  • Minimal JavaScript: No JavaScript required

User Experience

  • Clear labels: Dialog fields have descriptive labels
  • Sensible defaults: All fields have appropriate default values
  • Conditional fields: Button fields only show when button is enabled
  • Visual feedback: Button has hover and active states

Maintainability

  • Organized CSS: Logical grouping of styles
  • Consistent naming: Clear, descriptive class names
  • Documented variables: Custom variables have clear descriptions
  • Modular design: Component is self-contained

Variations

Hero with Video Background

To create a video background variant, modify component.html:

<section class="hero hero-align-{{textAlignment}}">

  <div class="hero-background">

    {{#if videoUrl}}

      <video class="hero-video" autoplay loop muted playsinline>

        <source src="{{videoUrl}}" type="video/mp4">

      </video>

    {{/if}}

    {{#unless videoUrl}}

      <img src="{{backgroundImage.src}}" alt="{{backgroundImage.alt}}" class="hero-image" />

    {{/unless}}

    <div class="hero-overlay hero-overlay-{{overlayOpacity}}"></div>

  </div>

  <!-- Rest of the component -->

</section>

Add a videoUrl field to dialog.json:

{

  "type": "text",

  "name": "videoUrl",

  "label": "Video URL (optional)",

  "required": false,

  "placeholder": "https://example.com/video.mp4"

}

Hero with Multiple Buttons

To support multiple buttons, add additional button fields:

{

  "type": "text",

  "name": "secondaryButtonText",

  "label": "Secondary Button Text",

  "required": false,

  "showIf": {

    "field": "showButton",

    "equals": true

  }

}

Related Topics