FaceFlow Components
Reusable business blocks with schema-driven fields, Facet templates, scoped styles, and optional runtime behavior.
Component Developer Docs
Components are schema-driven website sections rendered through FaceFlow.
For technical users, Components are the core reusable section contract in the system. They combine field definitions, Facet template output, optional styling, optional client-side behavior, and scope-aware reuse.
Core Responsibility
A Component is responsible for:
- defining a reusable section contract
- exposing a controlled authoring schema
- rendering output through Facet
- optionally attaching scoped styles and behavior
- supporting reuse at page, layout, or site level
Components should solve repeatable section problems. They should not become loose containers for arbitrary page-specific markup.
Component Definition Model
A typical Component definition includes:
{
"name": "contact-section",
"title": "Contact Section",
"version": "1.0.0",
"type": "component",
"category": "contact",
"defaultScope": "page",
"description": "Contact section with CTA and embedded form support",
"fields": [
{ "name": "title", "label": "Title", "type": "text" },
{ "name": "summary", "label": "Summary", "type": "textarea" },
{ "name": "contactForm", "label": "Form", "type": "formSelect" }
],
"html": "<section><h2>{{ title }}</h2><p>{{ summary }}</p><div data-form-embed=\"{contactForm}\"></div></section>",
"style": ".contact-section { padding: 4rem 0; }",
"script": "/* optional behavior */"
}This model is what allows a Component to act like a governed section contract instead of a loose fragment of copied code.
Core Metadata
Important metadata fields include:
nametitleversiontypecategorydefaultScopedescription
Guidance:
- keep
namestable after release - use
titlefor editor clarity - update
versionintentionally when schema or render behavior changes - choose
defaultScopebased on expected reuse, not convenience
Field Schema
The field schema defines what authors can change without altering the section structure.
Supported top-level field types include:
texttextareanumberurldateimageimagesfileselectcheckboxrichtexthtmlcodemaprepeaterformSelectreviewSelect
Additional subfield types can be used inside repeater, including:
coloremailtelrange
Example:
{
"name": "faqItems",
"label": "FAQ Items",
"type": "repeater",
"fields": [
{ "name": "question", "type": "text" },
{ "name": "answer", "type": "textarea" }
]
}Choose field types by content intent, not by convenience. A url should stay a URL. A repeated card set should be a repeater, not several unrelated text fields.
See the full field reference:
Template Model
The html portion of a Component is rendered through Facet.
That means templates can use:
- field output
- conditionals
- loops
- filters
- page-aware context
- reusable Variables
Example:
<section class="hero-section">
<div class="container">
<h1>{{ title }}</h1>
{{#if summary}}
<p>{{ summary }}</p>
{{/if}}
{{#if ctaUrl}}
<a href="{{ ctaUrl }}" class="btn-primary">{{ ctaLabel }}</a>
{{/if}}
</div>
</section>Or with a repeater:
<div class="faq-list">
{{#each faqItems as="item"}}
<article class="faq-item">
<h3>{{ item.question }}</h3>
<p>{{ item.answer }}</p>
</article>
{{/each}}
</div>For syntax details:
Scope Model
Components can exist at three reuse levels:
sitelayoutpage
Conceptually:
site scope -> reusable across the broader site
layout scope -> shared inside one page family
page scope -> local to one pageScope determines both visibility and change impact. If a Component changes at site scope, that is a broad architectural change, not a local edit.
For the broader model:
Rendering Behavior
At render time, FaceFlow resolves the Component instance by combining:
- the Component definition
- saved field values
- current rendering context
- related runtime services such as media, Variables, Forms, and Reviews
Conceptually:
component definition
+ saved field values
+ runtime context
-> Facet render
-> final section outputThis is the point where field data becomes runtime markup.
Styles and Behavior
Components may attach optional styles and optional behavior.
Example:
.pricing-card {
border-radius: 1rem;
padding: 2rem;
}document.querySelectorAll('.faq-item button').forEach((button) => {
button.addEventListener('click', () => {
button.closest('.faq-item').classList.toggle('is-open');
});
});Use this sparingly. Most section behavior should remain simple, predictable, and easy to maintain.
Forms and Reviews Inside Components
Two field types are especially important for interactive sections:
formSelectreviewSelect
These allow Components to embed business workflows without hard-coding one specific model.
Typical markers:
<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>This keeps the section reusable while allowing the selected Form or Review model to change at authoring time.
Example Component Patterns
Basic content section:
fields:
title
summary
template:
heading + paragraphFAQ section:
fields:
faqItems repeater
template:
each item -> question + answerLead capture section:
fields:
title
summary
contactForm (formSelect)
template:
copy + data-form-embed markerTechnical Review Checklist
- does the Component solve one repeatable section problem?
- is the field schema clear and intentional?
- is the template readable and stable?
- is the chosen scope correct for the expected reuse?
- would a future editor understand how to use it from the field contract alone?
Anti-Patterns
Avoid:
- overloading one Component with unrelated use cases
- adding page-specific business logic that should stay outside the section contract
- using
htmlcodewhen a structured field model would be safer - widening scope just because the section might be reused someday
- renaming fields after content is already stored against them
When To Split a Component
Split a Component when:
- the field schema has become too broad
- several layouts or page contexts need conflicting versions
- the template is full of exceptions and one-off branches
- editors no longer understand what the section is supposed to do
If the contract stops being clear, reuse usually stops being valuable.