FaceFlow Lists — Dynamic Content Listings

The List system in FaceFlow enables you to create dynamic, data-driven pages that query and display collections of content — blog archives, product catalogs, team directories, portfolios, and more. Lists combine a powerful query builder with flexible rendering options (card layout or custom Handlebars templates) and built-in AJAX pagination.

List Developer Docs

Lists are the dynamic content layer in FaceFlow.

They allow technical users to define how content collections are selected, rendered, paginated, and published inside a Page experience without converting every archive or directory into manual page editing.

Core Responsibility

A List is responsible for:

  • selecting a content set
  • sorting and limiting results
  • rendering each item consistently
  • paginating larger collections
  • exposing a predictable browsing contract

Lists should answer one clear question: "what collection is this page responsible for presenting?"

List Model

A typical List combines:

  • a query definition
  • a result ordering strategy
  • a page size
  • a rendering mode
  • optional custom fields
  • optional item template logic

Conceptually:

{
  "name": "resource-library",
  "query": {
    "template": "resource-item",
    "parent": "/resources/",
    "sort": "-published",
    "limit": 12
  },
  "display": {
    "mode": "custom-template",
    "pagination": true
  }
}

Query Contract

Technical teams should keep List queries explicit and bounded.

Typical inputs include:

  • content type
  • section boundary
  • sorting
  • result limit
  • additional filter constraints

A weak query creates weak output. If the content boundary is vague, the List quickly becomes a dumping ground.

Example:

template=resource-item, parent=/resources/, sort=-published, limit=12

That query is understandable. A catch-all archive with several unrelated types usually is not.

Query Design Example

Good List definitions usually keep the selection rule and display rule separate:

{
  "name": "case-studies",
  "query": {
    "template": "case-study",
    "parent": "/customers/",
    "sort": "-published",
    "limit": 9
  },
  "display": {
    "mode": "custom-template",
    "pagination": true
  }
}

That makes it easier to review whether the content boundary is correct before discussing presentation.

Rendering Modes

Lists usually follow one of two models.

Card-style output

Use this when:

  • editors need a low-friction configuration path
  • the archive follows a standard visual pattern
  • one consistent card contract is enough

Custom template output

Use this when:

  • result items need richer mapping
  • the design is not a simple card grid
  • metadata, badges, or media need custom placement
  • the archive depends on Facet logic

Example custom item output:

<div class="resource-grid">
  {{#each items}}
    <article class="resource-card">
      <a href="{{ this.url }}">
        <h3>{{ this.title }}</h3>
        <p>{{ this.summary }}</p>
      </a>
    </article>
  {{/each}}
</div>

Pagination

Pagination is part of the List contract, not just a visual detail.

Technical review should define:

  • default page size
  • page count behavior
  • navigation pattern
  • empty-state behavior

Conceptually:

page 1 -> items 1-12
page 2 -> items 13-24
page 3 -> items 25-36

If a List is expected to grow, pagination should be intentional from the first release.

Relationship to Pages and Components

The clean mental model is:

  • Layout provides the shell
  • Page provides the publishable route and surrounding experience
  • List provides the dynamic collection
  • Component may provide supporting sections around the List

Example page composition:

resource-layout
  -> archive-hero component
  -> resource-library list
  -> newsletter-signup component

This keeps dynamic browsing separate from one-off page content.

List Template Example

<section class="archive">
  <header class="archive-header">
    <h1>{{ page.title }}</h1>
  </header>

  <div class="archive-items">
    {{#each items}}
      <article class="archive-item">
        <a href="{{ this.url }}">{{ this.title }}</a>
      </article>
    {{/each}}
  </div>

  {{#if pagination}}
    <nav class="archive-pagination">
      {{{ pagination.links }}}
    </nav>
  {{/if}}
</section>

The goal is a stable contract between the List query and the archive template.

Empty-State and Growth Rules

Technical review should also account for what happens when the collection changes shape:

  • no results
  • one result
  • many pages of results
  • missing optional fields on some items

A List template is only stable if it survives all four conditions without layout breakage or confusing output.

Technical Review Checklist

  • is the query boundary clear and narrow?
  • does sorting match the browsing intent?
  • can the template handle growth in result volume?
  • is pagination part of the design contract?
  • is the List solving one archive problem, not several unrelated ones?

Anti-Patterns

Avoid:

  • mixing unrelated content types into one archive just because they are available
  • building an archive entirely by hand with repeated manual cards
  • using one List for several visual modes with incompatible contracts
  • leaving result ordering implicit
  • ignoring empty states and pagination until content volume becomes a problem