Component Contract

Blog Listing

Filterable grid of blog and article teasers for pension education content across the CAAT Pension Plan member experience.

Live Demo

Card Grid

List View

Featured + Grid

With Category Filters

States

1. Summary

ComponentBlog Listing
Resource typecaat/components/blog-listing
PurposeDisplays a filterable grid of blog and article teaser cards, linking to full articles on pension education, retirement planning, employer news, and member stories.
VariantsCard Grid · List View · Featured + Grid · Filtered Grid
StatusEnhance
ReplacesMulti Tile Listing (#43)

2. Design Tokens

TokenRoleValue (light)
--caat-blue-900Card title colour, active filter bg#003750
--caat-blue-700Title hover colour#0b5a80
--caat-blue-100Category badge background#e7f4fb
--caat-greenOptional success accent#55a546
--caat-grey-200Filter button border, card separator#dfe6ef
--caat-focusFocus ring on interactive elements#4D90FE
--caat-inkExcerpt text colour#102637
--caat-radiusCard border-radius.75rem
--caat-font-primaryFont familyLibre Franklin

3. Authoring Fields

FieldTypeRequiredNotes
VariantSelectYescard-grid | list-view | featured-grid | filtered-grid
HeadingTextNoOptional section heading above the listing.
Content sourcePathfieldYesRoot page path for blog content (e.g. /content/caat/en/blog).
Max itemsNumberNoLimit number of displayed cards. Default 6.
Category tagsTag pickerNoFilter source content by tag taxonomy.
Show filtersToggleNoRenders category filter bar above grid.
Featured articlePathfieldNoFeatured variant only — specific article path for the hero card.
Card: ImageImage (DAM)YesPulled from article page thumbnail or DAM reference.
Card: TitleTextYesPulled from article jcr:title.
Card: ExcerptTextareaNoPulled from article jcr:description. Truncated at 160 chars.
Card: CategoryTextNoDerived from article tags.
Card: DateDateYesArticle publish date.
Card: Read timeTextNoEstimated reading time in minutes.

4. Validation

RuleError message
Content source path is required."Provide a root content path for the blog listing."
Content source must resolve to at least 1 child page."No articles found at the specified path."
Each card must have a title."One or more articles are missing a title."
Each card must have an image or fallback."Provide an image for each article or configure a default thumbnail."
Featured variant requires a featured article path."Select a featured article for this variant."
Max items must be between 1 and 24."Enter a value between 1 and 24."

5. Content Guidance

  • Write descriptive, action-oriented titles: "5 Steps to a Confident Retirement" rather than "Retirement Info".
  • Keep excerpts to 1 – 2 sentences (under 160 characters) so cards remain uniform in height.
  • Use consistent category labels aligned to the CAAT tag taxonomy: Pension Basics, Employer News, Member Stories.
  • Ensure images are relevant, high quality, and at least 800 × 450 px to support retina displays.
  • Add alt text to images only when they convey information not in the title; decorative images use alt="".
  • Provide a read-time estimate — it sets user expectations and improves click-through rates.
  • Featured articles should be timely and editorially significant; rotate regularly.

6. Semantic HTML

Card grid variant

<section class="caat-blog-listing" aria-label="Blog articles">
  <div class="caat-blog-listing__grid">
    <article class="caat-blog-listing__card">
      <a href="/blog/article-slug">
        <img class="caat-blog-listing__image"
             src="/content/dam/caat/blog/thumb.jpg"
             alt="" loading="lazy">
        <div class="caat-blog-listing__body">
          <span class="caat-blog-listing__category">Pension Basics</span>
          <h3 class="caat-blog-listing__title">Article Title</h3>
          <p class="caat-blog-listing__excerpt">Brief description…</p>
          <div class="caat-blog-listing__meta">
            <span>Jan 15, 2026</span>
            <span>5 min read</span>
          </div>
        </div>
      </a>
    </article>
    <!-- …more cards -->
  </div>
</section>

Filter bar

<div class="caat-blog-listing__filters" role="toolbar"
     aria-label="Filter articles by category">
  <button class="caat-blog-listing__filter-btn active"
          aria-pressed="true" data-filter="all">All</button>
  <button class="caat-blog-listing__filter-btn"
          aria-pressed="false" data-filter="pension-basics">Pension Basics</button>
  <!-- …more filters -->
</div>

7. CSS Contract

SelectorPurpose
.caat-blog-listingRoot component container.
.caat-blog-listing__gridCSS Grid container — 3-col desktop, 2-col tablet, 1-col mobile.
.caat-blog-listing__grid--listModifier: single-column list view with horizontal cards.
.caat-blog-listing__cardIndividual card — white bg, subtle shadow, hover lift.
.caat-blog-listing__imageCard image — object-fit:cover, aspect-ratio:16/9.
.caat-blog-listing__bodyCard content wrapper with padding.
.caat-blog-listing__categoryCategory pill badge — blue-100 bg, blue-900 text.
.caat-blog-listing__titleCard heading — blue-900, font-weight 700.
.caat-blog-listing__excerptTeaser text — ink colour, 0.9375 rem.
.caat-blog-listing__metaDate and read-time row — muted small text.
.caat-blog-listing__filtersFilter toolbar container — flex, gap.
.caat-blog-listing__filter-btnFilter pill button — active state uses blue-900 bg.
.caat-blog-listing__featuredFeatured card wrapper — full-width horizontal layout.

8. Accessibility

RequirementImplementation
LandmarkWrap listing in <section> with aria-label.
Card linksEach card is a single <a> wrapping all content; aria-label provides full article title.
ImagesDecorative images use alt="". Meaningful images get descriptive alt text.
Filtersrole="toolbar" on filter container; aria-pressed on each filter button.
FocusVisible :focus-visible ring (--caat-focus) on cards and filter buttons.
Colour contrastCategory badge text (blue-900 on blue-100) meets 4.5:1. Meta text meets 4.5:1 against white.
MotionHover lift transition respects prefers-reduced-motion: reduce.
KeyboardAll cards and filter buttons reachable via Tab. Filter state toggled via Enter / Space.

9. SEO

  • Each card links to a unique article URL — search engines follow these links to index article content.
  • Use semantic <article> elements so crawlers understand the listing structure.
  • Card titles use heading elements (<h3> or <h4>) to contribute to page outline.
  • Excerpt text provides unique indexable content for each card on the listing page.
  • Lazy-loaded images use loading="lazy"; above-the-fold featured images should use loading="eager".

10. Analytics

Analytics are captured through the shared data-analytics-component attribute on the component root (emitted automatically) and reported to the central data layer. Event names and payloads follow the organisation's standard analytics schema — defined centrally with the analytics team, not invented per component — so no bespoke tracking is specified here.

11. AEM Notes

  • Resource type: caat/components/blog-listing
  • Sling Model: BlogListingModel — exposes variant, heading, contentPath, maxItems, categoryTags[], showFilters, featuredArticlePath, articles[].
  • HTL template: blog-listing.html — uses data-sly-test to switch between grid, list, featured, and filtered variants. Cards iterated with data-sly-list.
  • Client library: caat.components.blog-listing — CSS + JS. JS handles filter toggle, card analytics events.
  • Content query: Sling Model queries child pages under contentPath, sorted by publish date descending, filtered by category tags.
  • Policy: Authors can restrict available variants and configure default max items per template policy.
  • Allowed parents: Layout Container, Section Container.

12. Dialog Model

Simplified model — shows field intent and Granite UI resource types, not the literal cq:dialog node tree (e.g. select options are authored as child items nodes).

{
  "jcr:primaryType": "nt:unstructured",
  "sling:resourceType": "cq/gui/components/authoring/dialog",
  "tabs": {
    "properties": {
      "variant": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/select",
        "label": "Variant",
        "name": "./variant",
        "required": true,
        "options": ["card-grid", "list-view", "featured-grid", "filtered-grid"]
      },
      "heading": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/textfield",
        "label": "Heading",
        "name": "./heading"
      },
      "contentSource": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/pathfield",
        "label": "Content source",
        "name": "./contentSource",
        "required": true,
        "rootPath": "/content/caat"
      },
      "maxItems": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/numberfield",
        "label": "Max items",
        "name": "./maxItems",
        "min": 1,
        "max": 24,
        "value": 6
      },
      "categoryTags": {
        "sling:resourceType": "cq/gui/components/coral/common/form/tagfield",
        "label": "Category tags",
        "name": "./categoryTags",
        "multiple": true
      },
      "showFilters": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/checkbox",
        "label": "Show category filters",
        "name": "./showFilters"
      },
      "featuredArticle": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/pathfield",
        "label": "Featured article",
        "name": "./featuredArticle",
        "rootPath": "/content/caat"
      }
    }
  }
}

13. QA Acceptance Checklist

  • Card grid renders 3 columns on desktop, 2 on tablet, 1 on mobile.
  • List view renders horizontal cards (image left, content right).
  • Featured + grid shows large hero card above standard 3-col grid.
  • Filter buttons toggle active state; grid filters cards by category.
  • Cards show hover lift effect (translateY + shadow increase).
  • Images display at 16:9 aspect ratio with object-fit:cover.
  • Category badge renders as small pill with correct token colours.
  • aria-pressed toggles correctly on filter buttons.
  • Cards are keyboard-navigable via Tab; focus ring visible.
  • Screen reader announces article title via aria-label on each card link.
  • Lazy-loaded images load only when scrolled into view.
  • Analytics events (blog-listing-viewed, blog-card-click, blog-filter-change) fire correctly.
  • Cross-browser tested: Chrome, Firefox, Safari, Edge.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. All four variants documented with responsive breakpoints. Category badge, filter bar, and featured card states included.
Development AEM proxy component created. HTL template emits semantic HTML per Section 6. CSS uses design tokens exclusively. Filter JS handles aria-pressed toggle and card visibility. Sling Model queries and sorts content. Unit tests pass.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Cross-browser tested (Chrome, Firefox, Safari, Edge).
Launch Component deployed to AEM production. Content authors trained on variant selection and content source configuration. Documentation published. Analytics dashboards confirmed receiving events.