Appearance
Component System Overview
LamaPress uses a component-based architecture that promotes reusability, maintainability, and separation of concerns. This guide explains how the component system works and how to create and use components effectively.
Table of Contents
- Component Types
- Component Structure
- Component Loading
- Data Flow
- Creating Components
- JavaScript Integration
- Best Practices
Component Types
LamaPress organizes components into three main categories:
1. Sections (components/sections/)
Full-width page sections that typically span the entire viewport width. Examples:
- Hero sections
- Content sections
- Image galleries
- Video sections
Characteristics:
- Full-width layout
- Usually have ACF field groups
- Can be used in flexible content layouts
- Often have their own container and spacing
Example: components/sections/hero_basic/
2. Blocks (components/blocks/)
Reusable UI components that can be placed anywhere in a layout. Examples:
- Accordions
- Image sliders
- Rich content blocks
- Video players
Characteristics:
- Self-contained components
- Can be nested within sections
- Reusable across different contexts
- May or may not have ACF fields
Example: components/blocks/accordion/
3. Parts (components/parts/)
Small, atomic components used for specific UI elements. Examples:
- Buttons
- Icons
- Images
- Form inputs
Characteristics:
- Small, focused components
- Highly reusable
- Often used within blocks and sections
- Minimal or no ACF fields
Example: components/parts/buttons/default/
Component Structure
Each component follows a consistent directory structure:
components/
├── sections/
│ └── hero_basic/
│ ├── index.php # Required: Main template
│ ├── acf.php # Required: ACF field definitions
│ ├── index.js # Optional: JavaScript class
│ ├── style.scss # Optional: Component styles
│ └── functions.php # Optional: Component functions
│
├── blocks/
│ └── accordion/
│ ├── index.php # Required: Main template
│ ├── acf.php # Optional: ACF fields (if reusable)
│ ├── index.js # Optional: JavaScript class
│ └── style.scss # Optional: Component styles
│
└── parts/
└── buttons/
└── default/
├── index.php # Required: Main template
├── index.js # Optional: JavaScript class
└── style.scss # Optional: Component stylesRequired Files
index.php (Required)
The main template file that renders the component. This file receives data through variables extracted from the $props array.
Sections:
php
<?php
// Variables are available from ACF fields via $key
$title = llField('title', $key);
$content = llField('content', $key);
?>
<section class="ll-section ll-section--hero">
<div class="ll-container">
<h1><?= esc_html($title) ?></h1>
<div><?= wp_kses_post($content) ?></div>
</div>
</section>Blocks and Parts:
php
<?php
// Properties are passed directly via $props
$title = $fields['title'] ?? false;
$content = $fields['content'] ?? false;
$classes = $classes ?? '';
?>
<div class="ll-block--accordion <?= $classes ?>" data-component="blocks/accordion">
<?php if ($title): ?>
<h2><?= esc_html($title) ?></h2>
<?php endif; ?>
</div>Optional Files
acf.php (Required for sections, optional for blocks/parts)
Defines Advanced Custom Fields for the component.
Sections:
php
<?php
$key = 'section_hero_basic';
$name = 'hero_basic';
$title = 'Hero Basic';
$groupFields = [
[
'key' => 'title',
'label' => 'Title',
'name' => 'title',
'type' => 'text',
],
[
'key' => 'content',
'label' => 'Content',
'name' => 'content',
'type' => 'wysiwyg',
],
];Blocks/Parts:
php
<?php
// Only return $groupFields array, no $key, $name, or $title
$groupFields = [
[
'key' => 'title',
'label' => 'Title',
'name' => 'title',
'type' => 'text',
],
];index.js (Optional)
JavaScript class for component interactivity. Automatically loaded when data-component attribute is present.
javascript
export default class Accordion {
constructor(element) {
this.element = element
this.init()
}
init = () => {
// Initialization logic
this.bindEvents()
}
bindEvents = () => {
// Event listeners
}
enter = () => {
// Animation when component enters viewport
}
destroy = () => {
// Cleanup when component is destroyed
}
}style.scss (Optional)
Component-specific styles. Only add if Tailwind classes aren't sufficient.
scss
.ll-block--accordion {
// Component-specific styles
}functions.php (Optional, sections only)
Component-specific PHP functions.
Component Loading
Components are loaded using helper functions defined in functions/includes.php:
Loading Sections
php
<?php
// In a template file
llSection('hero_basic', 'hero_section_1');Parameters:
$name- Section name (directory name)$key- Unique key for ACF field group
Loading Blocks
php
<?php
llBlock('accordion', [
'fields' => [
'title' => 'FAQ Section',
'items' => $accordion_items
],
'classes' => 'my-custom-class'
]);Parameters:
$name- Block name (directory name)$props- Array containing:fields- Field data for the componentclasses- Additional CSS classes
Loading Parts
php
<?php
llPart('buttons/default', [
'text' => 'Click Me',
'url' => '/contact',
'classes' => 'll-btn-primary'
]);Parameters:
$name- Part name (can include subdirectories)$props- Array of properties to pass to the component
Data Flow
Understanding how data flows from ACF to components:
Sections
- ACF Fields → Defined in
acf.php - Template Registration → Automatically registered via
llSections() - Field Retrieval → Accessed via
llField()with$keyprefix - Template Rendering →
index.phpreceives data through variables
php
// In section template
$title = llField('title', $key); // Gets 'hero_section_1_title'
$content = llField('content', $key); // Gets 'hero_section_1_content'Blocks and Parts
- Props Passed → Data passed directly via
llBlock()orllPart() - Variable Extraction →
extract($props)makes variables available - Template Rendering →
index.phpuses the extracted variables
php
// When calling
llBlock('accordion', [
'fields' => ['title' => 'Hello'],
'classes' => 'custom'
]);
// In block template
$fields = $fields ?? []; // Available from props
$classes = $classes ?? ''; // Available from propsCreating Components
Step-by-Step: Creating a Block Component
Create Directory Structure
bashmkdir -p components/blocks/my-componentCreate
index.phpphp<?php $classes = $classes ?? ''; $title = $fields['title'] ?? false; ?> <div class="ll-block--my-component <?= $classes ?>" data-component="blocks/my-component"> <?php if ($title): ?> <h2><?= esc_html($title) ?></h2> <?php endif; ?> </div>Create
acf.php(if needed)php<?php $groupFields = [ [ 'key' => 'title', 'label' => 'Title', 'name' => 'title', 'type' => 'text', ], ];Create
index.js(if needed)javascriptexport default class MyComponent { constructor(element) { this.element = element this.init() } init = () => { this.bindEvents() } bindEvents = () => { // Event listeners } enter = () => { // Reveal animation } destroy = () => { // Cleanup } }Use the Component
php<?php llBlock('my-component', [ 'fields' => [ 'title' => 'My Component Title' ] ]); ?>
JavaScript Integration
Automatic Component Loading
Components with JavaScript are automatically initialized when:
- The component has a
data-componentattribute - A corresponding
index.jsfile exists - The JavaScript file exports a default class
Example:
html
<div data-component="blocks/accordion">
<!-- Component content -->
</div>The system will automatically:
- Find
components/blocks/accordion/index.js - Import the default class
- Instantiate it with the element
- Call lifecycle methods (
init,enter, etc.)
Component Lifecycle
Components follow a consistent lifecycle:
- Constructor - Receives the DOM element
- init() - Initialization (called automatically)
- bindEvents() - Set up event listeners (called from
init) - enter() - Reveal animation (called when component enters viewport)
- destroy() - Cleanup (called when component is destroyed)
Accessing Other Components
Components can access other component instances through the app:
javascript
import { app } from '@src/js/core/app'
export default class MyComponent {
init = () => {
// Access another component instance
const accordion = app.instances.get('page').instances.get(accordionElement)
}
}Best Practices
1. Naming Conventions
- Use lowercase with underscores:
hero_basic,rich_content - Be descriptive:
image_slidernotslider - Follow existing patterns in the codebase
2. File Organization
- Keep components focused and single-purpose
- Only add files you actually need
- Don't create empty
acf.phpfiles if no fields are needed
3. Data Handling
- Always use null coalescing (
??) for optional fields - Escape output:
esc_html()for text,wp_kses_post()for HTML - Validate data before using it
4. CSS Classes
- Prefix custom classes with
ll- - Use Tailwind classes when possible
- Only add custom SCSS when necessary
5. JavaScript
- Use arrow functions for methods
- Bind timelines to component instance
- Always implement
destroy()for cleanup - Use
data-componentattribute for automatic loading
6. ACF Fields
- Use descriptive field keys and names
- Follow ACF best practices
- Group related fields logically
- Only create
acf.phpfor reusable field groups
Common Patterns
Pattern 1: Conditional Rendering
php
<?php
$title = $fields['title'] ?? false;
$content = $fields['content'] ?? false;
?>
<?php if ($title || $content): ?>
<div class="ll-block--content">
<?php if ($title): ?>
<h2><?= esc_html($title) ?></h2>
<?php endif; ?>
<?php if ($content): ?>
<div><?= wp_kses_post($content) ?></div>
<?php endif; ?>
</div>
<?php endif; ?>Pattern 2: Looping Through Items
php
<?php
$items = $fields['items'] ?? [];
?>
<?php if ($items): ?>
<div class="ll-block--list">
<?php foreach ($items as $item): ?>
<div class="ll-block--list-item">
<?= esc_html($item['title']) ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>Pattern 3: Nested Components
php
<?php
$items = $fields['items'] ?? [];
?>
<?php if ($items): ?>
<div class="ll-block--accordion" data-component="blocks/accordion">
<?php foreach ($items as $item): ?>
<?php llBlock('accordion_item', ['fields' => $item]); ?>
<?php endforeach; ?>
</div>
<?php endif; ?>Related Documentation
- PHP Functions Reference - Component loading functions
- JavaScript Architecture - How JavaScript components work
- ACF Integration - Advanced Custom Fields setup
- Component Guidelines - Component creation guidelines
Next Steps:
- Learn about JavaScript Architecture to understand component interactivity
- Review PHP Functions Reference for all available helper functions
- Check existing components for real-world examples