Skip to content

Component Guidelines

This guide provides comprehensive guidelines for creating and maintaining components in LamaPress. Follow these guidelines to ensure consistency and quality.

Table of Contents

Component Structure

Required Files

Sections:

  • index.php - Required: Main template
  • acf.php - Required: ACF field definitions

Blocks and Parts:

  • index.php - Required: Main template
  • acf.php - Optional: Only if fields are reusable

Optional Files

  • index.js - Optional: JavaScript for interactivity
  • style.scss - Optional: Component-specific styles
  • functions.php - Optional: Component-specific functions (sections only)

File Organization

components/
├── sections/
│   └── hero_basic/
│       ├── index.php      # Required
│       ├── acf.php        # Required
│       ├── index.js       # Optional
│       ├── style.scss     # Optional
│       └── functions.php  # Optional
└── blocks/
    └── accordion/
        ├── index.php      # Required
        ├── acf.php        # Optional
        ├── index.js       # Optional
        └── style.scss     # Optional

PHP Template Guidelines

File Header

All component PHP files:

php
<?php

/**
 * Component description here.
 * 
 * This component does X, Y, and Z.
 */

defined('ABSPATH') || exit;

Sections only:

php
<?php

/**
 * Section description here.
 */

defined('ABSPATH') || exit;

$section ??= false;
$key ??= false;

Section Templates

Use llSectionHeader() and llSectionFooter():

php
<?php
llSectionHeader(
    name: 'hero_basic',
    sectionClasses: 'll-section--dark',
    containerClasses: 'll-container--wide',
    constructorName: 'hero_basic'
);

// Section content

llSectionFooter();
?>

Grid Layouts:

php
<?php
llSectionHeader(
    name: 'content_grid',
    containerClasses: 'll-grid'
);

// Use grid column utilities
<div class="col-start-1 col-span-12 md:col-span-6">
    <!-- Content -->
</div>

llSectionFooter();
?>

Block and Part Templates

Property Handling:

php
<?php
$classes = $classes ?? '';
$fields = $fields ?? [];
$title = $fields['title'] ?? false;
?>

<?php if ($title): ?>
    <div class="ll-block--my-component <?= $classes ?>">
        <h2><?= esc_html($title) ?></h2>
    </div>
<?php endif; ?>

Always Check for Data:

php
<?php
// ✅ Good - Check before using
$title = $fields['title'] ?? false;
if ($title) {
    echo esc_html($title);
}

// ❌ Bad - No check
echo esc_html($fields['title']);
?>

Field Access

Use llField() with all arguments:

php
<?php
// ✅ Good - Always pass 3 arguments
$title = llField('title', $key, $section);

// ❌ Bad - Missing arguments
$title = llField('title');
?>

Wrap in Conditionals:

php
<?php
// ✅ Good - Wrapped in if statement
$title = llField('title', $key, $section);
if ($title) {
    echo '<h1>' . esc_html($title) . '</h1>';
}

// ❌ Bad - No check
echo '<h1>' . esc_html(llField('title', $key, $section)) . '</h1>';
?>

HTML Structure

Semantic HTML:

php
<?php
// ✅ Good - Semantic HTML
<section class="ll-section">
    <header>
        <h1><?= esc_html($title) ?></h1>
    </header>
    <main>
        <?= wp_kses_post($content) ?>
    </main>
</section>

// ❌ Bad - Non-semantic
<div class="ll-section">
    <div class="title"><?= esc_html($title) ?></div>
    <div class="content"><?= wp_kses_post($content) ?></div>
</div>
?>

Spacing:

  • Add empty line between sibling HTML elements
  • Add content within HTML element on new line
php
<?php
// ✅ Good
<div class="container">
    <h1><?= esc_html($title) ?></h1>
    
    <p><?= wp_kses_post($content) ?></p>
</div>

// ❌ Bad
<div class="container"><h1><?= esc_html($title) ?></h1><p><?= wp_kses_post($content) ?></p></div>
?>

JavaScript Guidelines

Component Class Structure

Standard Structure:

javascript
import { gsap } from 'gsap/all'
import { app } from '@src/js/core/app'
import { killTimeline } from '@src/js/core/utils/helpers'

export default class MyComponent {
  constructor(element) {
    this.element = element
    this.timeline = null
    this.init()
  }

  init = () => {
    this.bindEvents()
    this.createAnimation()
  }

  bindEvents = () => {
    // Event listeners
  }

  createAnimation = () => {
    // Animation setup
  }

  enter = () => {
    // Reveal animation
  }

  destroy = () => {
    killTimeline(this.timeline)
    // Cleanup
  }
}

Event Handling

Use bindEvents() Method:

javascript
// ✅ Good
bindEvents = () => {
  this.element.addEventListener('click', this.handleClick)
  this.button.addEventListener('mouseenter', this.handleMouseEnter)
}

// ❌ Bad - Events in init()
init = () => {
  this.element.addEventListener('click', this.handleClick)
}

Clean Up in destroy():

javascript
destroy = () => {
  this.element.removeEventListener('click', this.handleClick)
  this.button.removeEventListener('mouseenter', this.handleMouseEnter)
}

Animation Guidelines

Bind Timelines to Instance:

javascript
// ✅ Good
this.timeline = gsap.timeline()

// ❌ Bad
const timeline = gsap.timeline()

Use killTimeline() Helper:

javascript
import { killTimeline } from '@src/js/core/utils/helpers'

destroy = () => {
  killTimeline(this.timeline)
}

Reveal Animations:

javascript
createRevealAnimation = () => {
  this.timeline = gsap.timeline({
    scrollTrigger: {
      trigger: this.element,
      start: 'top 80%',
    }
  })
  
  this.timeline.from(this.element, {
    opacity: 0,
    y: 20,
    duration: 0.6
  })
}

enter = () => {
  // Additional enter logic if needed
}

ACF Field Guidelines

Section Fields

Required Variables:

php
<?php
$title = 'Hero Basic';        // Display name
$name = 'hero_basic';          // Identifier (snake_case)
$category = 'common';          // Category for grouping
$key = 'a1b2c3d4e5f6';         // Unique 12-character key (a-z, 0-9)
$groupFields = [               // Field definitions
    // ...
];
?>

Key Requirements:

  • Must be exactly 12 characters
  • Only use a-z and 0-9
  • Must be unique across all ACF files
  • Must be hard-coded (never change)

Field Instructions:

php
<?php
$groupFields = [
    [
        'key' => $name . '_01',
        'label' => 'Title',
        'name' => 'title',
        'type' => 'text',
        'instructions' => 'Enter a compelling title for this section',
    ],
];
?>

Block and Part Fields

Only Return Fields Array:

php
<?php
// ✅ Good - Only return fields
$groupFields = [
    [
        'key' => 'title',
        'label' => 'Title',
        'name' => 'title',
        'type' => 'text',
    ],
];

Don't Define Variables:

php
<?php
// ❌ Bad - Don't define $key, $name, $title
$key = 'block_key';
$name = 'block_name';
$title = 'Block Title';
$groupFields = [/* ... */];
?>

Styling Guidelines

Tailwind First

Use Tailwind Classes:

php
<?php
// ✅ Good - Tailwind classes
<div class="flex items-center justify-between p-4 bg-white rounded-lg">

// ❌ Bad - Custom CSS
<div class="custom-wrapper">

Custom Classes

Prefix with ll-:

php
<?php
// ✅ Good
<div class="ll-block--accordion">

// ❌ Bad
<div class="accordion">

Only When Necessary:

  • Only create custom classes if Tailwind doesn't have what you need
  • Use @apply in SCSS for common Tailwind patterns
scss
// ✅ Good - Using Tailwind
.ll-block--accordion {
  @apply flex items-center;
}

// ❌ Bad - Recreating Tailwind
.ll-block--accordion {
  display: flex;
  align-items: center;
}

Component Styles

Minimal Custom SCSS:

scss
// ✅ Good - Only what's necessary
.ll-block--accordion {
  // Specific styling not possible with Tailwind
}

// ❌ Bad - Recreating Tailwind
.ll-block--accordion {
  display: flex;
  padding: 1rem;
  background: white;
  // Use Tailwind classes instead
}

Best Practices

1. Single Responsibility

Each component should do one thing well:

php
<?php
// ✅ Good - Focused component
// components/blocks/title/index.php
<h1><?= esc_html($title) ?></h1>

// ❌ Bad - Too many responsibilities
// components/blocks/content/index.php
<h1><?= esc_html($title) ?></h1>
<p><?= wp_kses_post($content) ?></p>
<img src="<?= $image ?>">
<button><?= $cta ?></button>

2. Reusability

Create reusable components:

php
<?php
// ✅ Good - Reusable button component
llPart('buttons/default', [
    'text' => 'Click Me',
    'url' => '/contact'
]);

// ❌ Bad - Inline button
<a href="/contact" class="btn">Click Me</a>

3. Data Validation

Always validate data:

php
<?php
// ✅ Good - Check before use
$items = $fields['items'] ?? [];
if ($items) {
    foreach ($items as $item) {
        // Process item
    }
}

// ❌ Bad - No validation
foreach ($fields['items'] as $item) {
    // May error if items doesn't exist
}

4. Security

Always escape output:

php
<?php
// ✅ Good - Escaped output
echo esc_html($text);
echo wp_kses_post($html);
echo esc_url($url);
echo esc_attr($attribute);

// ❌ Bad - Unescaped output
echo $text;
echo $html;

5. Performance

Optimize for performance:

  • Use efficient queries
  • Minimize database calls
  • Cache expensive operations
  • Lazy load images

Next Steps:

Released under the MIT License.