---
title: Twig-Ready Micro-Components: Architectural & Development Guide
date: unknown
---

# Twig-Ready Micro-Components: Architectural & Development Guide

# Twig-Ready Micro-Components: Architectural & Development Guide

Rezilienz features a powerful, decoupled **Micro-Component Engine** in its presentation layer. By combining **server-side layout encapsulation (Twig)**, **recursive content processing (Shortcode Processor)**, and **client-side interactivity (JavaScript/Alpine.js)**, this engine enables authors to insert rich, dynamic, and reactive blocks inside standard Markdown dossiers using simple shortcode wrappers.

This document serves as the canonical reference for authors writing content and developers building or extending components.

---

## 1. Core Architecture Overview

When an article containing a component is rendered, it flows through a multi-stage compilation pipeline:

```mermaid
graph TD
    A[Markdown Source] -->|1. Parse Frontmatter & HTML Conversion| B[Raw HTML Output]
    B -->|"2. Detect [component name='x'] slot /component"| C[ShortcodeProcessor]
    C -->|3. Resolve Twig Partial _x.html.twig| D[ThemeEngine]
    D -->|4. Render Twig layout with slot content| E[HTML + Inline JS Output]
    E -->|5. Recursive Compile inner shortcodes| F[Final Client HTML]
    F -->|6. Client Execution| G[Browser JS / Alpine.js Reactivity]
```

By separating structural presentation from content markup, Rezilienz achieves the modular power of modern component frameworks (like React or Vue) while remaining an ultra-lightweight, zero-dependency headless CMS.

---

## 2. Author Usage (Shortcode Syntax)

Authors can invoke any active theme micro-component by wrapping content in a `[component]` block.

### Basic Syntax
```markdown
[component name="alertbox"]
The decryption keys for **Operation VENONA** were recovered from a charred notebook.
[/component]
```

### Key Rules for Authors
1. **Recursive Processing:** You can nest standard markdown formatting (bold, italic, list items) or other shortcodes (like `[link="slug"]` or `[image]`) inside a component. The engine will compile the outer component first, and then recursively parse all nested elements within its slot.
2. **Self-Closing Avoidance:** Always close every component tag using `[/component]`.

---

## 3. Creating a Custom Component

All micro-component layout templates are stored in your active theme's `partials` folder:
`apps/blog/themes/<active_theme>/partials/_<component_name>.html.twig`

### File Naming Convention
* **Prefix:** All partial components must start with an underscore (e.g. `_alertbox.html.twig` or `_accordion.html.twig`).
* **Extension:** Use `.html.twig` for Twig-compiled files.
* **Component Resolution:** When matching `[component name="card"]`, the engine automatically searches for `_card.html.twig`.

### Step-by-Step: Building a Static Card Component
Let's create a template file at `partials/_infocard.html.twig`:

```html
<!-- apps/blog/themes/default/partials/_infocard.html.twig -->
<div class="info-card border-2 border-olive-light bg-paper-dark p-6 my-4 shadow rounded relative">
    <div class="font-mono text-xs uppercase tracking-widest text-olive-light mb-2">
        📂 Classified Memorandum
    </div>
    <div class="font-serif leading-relaxed text-dark-ink">
        {{ slot | raw }}
    </div>
</div>
```
* **`{{ slot | raw }}`:** This is the magic variable. The text wrapped inside the author's shortcode (`[component]...[/component]`) is captured and injected directly into this placeholder. The `| raw` filter is critical; it ensures that any compiled HTML/Markdown formatting inside the slot is rendered correctly, rather than outputting escaped plain text.

---

## 4. Client-Side Dynamism (JavaScript & Alpine.js)

Since components are served to the user's browser as standard HTML, they can house any client-side JavaScript. This allows you to build highly interactive widgets.

### A. Scoping Vanilla JS using Twig-Generated IDs
If a component is inserted multiple times on the same page, hardcoded element IDs will clash, causing your scripts to fail. To prevent this, **always use Twig to generate a unique instance ID**:

```html
<!-- apps/blog/themes/default/partials/_copy_bulletin.html.twig -->
{% set uid = 'bulletin-' ~ random(10000, 99999) %}

<div id="{{ uid }}" class="bulletin-card p-4 bg-paper-dark border my-4 relative">
    <div class="content-body">{{ slot | raw }}</div>
    <button id="btn-{{ uid }}" class="mt-2 text-xs font-mono text-primary-red hover:underline">
        📋 Copy Intelligence String
    </button>
</div>

<script>
    (function() {
        const uid = "{{ uid }}";
        const card = document.getElementById(uid);
        const button = document.getElementById("btn-" + uid);
        
        button.addEventListener('click', function() {
            const text = card.querySelector('.content-body').innerText;
            navigator.clipboard.writeText(text);
            button.innerText = "✓ Copied!";
            setTimeout(() => { button.innerText = "📋 Copy Intelligence String"; }, 2000);
        });
    })();
</script>
```

### B. Declarative Reactivity via Alpine.js
If your theme includes Alpine.js, you can bypass vanilla scripting entirely and declare component states directly in the markup. 

Here is how to create a beautiful, collapsible dossier panel (`_dossier_disclosure.html.twig`):

```html
<!-- apps/blog/themes/default/partials/_dossier_disclosure.html.twig -->
<div class="border border-border-color my-4 rounded" x-data="{ open: false }">
    <!-- Header / Toggle -->
    <button @click="open = !open" 
            class="w-full bg-paper-dark px-6 py-4 flex justify-between items-center text-left font-mono font-extrabold text-sm uppercase tracking-wider hover:bg-paper-dark/80">
        <span>📂 Section Disclosure Brief</span>
        <span x-text="open ? '[-] Collapse' : '[+] Expand'">[+] Expand</span>
    </button>
    
    <!-- Content Slot -->
    <div class="px-6 py-6 bg-paper-aged border-t border-border-color font-serif leading-relaxed" 
         x-show="open" 
         x-transition:enter="transition ease-out duration-200"
         x-transition:enter-start="opacity-0 transform -translate-y-2"
         x-transition:enter-end="opacity-100 transform translate-y-0"
         style="display: none;">
        {{ slot | raw }}
    </div>
</div>
```

---

## 5. Server-Side Dynamism (Twig & PHP Functions)

While Twig templates cannot execute raw PHP code directly inside their markup (which guarantees excellent design/logic decoupling), you can hook PHP logic into Twig to power them from the backend.

### A. Registering Custom PHP Helper Functions in Twig
To call database queries, fetch configurations, or fetch directory listings from your PHP backend inside a Twig micro-component, register your PHP callback as a Twig function inside the `initTwig()` method in `ThemeEngine.php`:

```php
// apps/blog/src/ThemeEngine.php
private function initTwig(string $configPath): void
{
    // ... setup loader ...
    $this->twig = new \Twig\Environment($loader, $twigOpts);
    
    // Example: Expose a dynamic PHP function to Twig templates
    $this->twig->addFunction(new \Twig\TwigFunction('get_active_alerts', function() {
        // Run database, SFTP, or API query here:
        return ['VENONA Intercepted', 'Berlin Outpost Alert', 'Clandestine Courier Transmit'];
    }));
}
```

### B. Consuming PHP Functions in the Twig Component
Now, you can loop over the results of this PHP callback inside your Twig component layout (`_alerts_ticker.html.twig`):

```html
<!-- apps/blog/themes/default/partials/_alerts_ticker.html.twig -->
<div class="ticker-box border-l-4 border-primary-red p-4 bg-paper-dark">
    <h4 class="font-mono text-xs uppercase tracking-wider text-primary-red mb-2">🔴 Active Transmission Ticker</h4>
    <ul class="list-disc pl-5 font-mono text-xs text-dark-olive space-y-1">
        {% for alert in get_active_alerts() %}
            <li>{{ alert }}</li>
        {% endfor %}
    </ul>
    <div class="mt-3 text-serif text-sm">
        {{ slot | raw }}
    </div>
</div>
```

---

## 6. Procedural Fallback (PHP-Based Components)

If a component's backend logic is highly complex, sequential, or procedural, you can skip Twig completely and write raw PHP.

Because of the **PHP Fallback Guard** in the `ThemeEngine->partial()` resolver:
1. Save your file in the active theme's `partials` folder as `_custom_block.php` (instead of `.html.twig`).
2. The engine will detect that no Twig layout exists, locate the PHP file, run standard PHP buffering (`ob_start`), execute your procedural code, and capture the output.

Inside `_custom_block.php`, the `$slot` variable is automatically unpacked and available:

```php
<!-- apps/blog/themes/default/partials/_custom_block.php -->
<?php
// Procedural PHP inside the component template:
$protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
$timestamp = date('r');
?>
<div class="php-component p-4 bg-paper-dark border">
    <p class="text-xs font-mono">Rendered by PHP at: <?= $timestamp ?></p>
    <div class="content">
        <?= $slot ?>
    </div>
</div>
```

---

## Summary Grid: When to Use What

| Target | Technology | Perfect For | Example |
| :--- | :--- | :--- | :--- |
| **Static Layouts** | HTML + CSS + SVG | Basic stylized containers, info callouts, and watermarks | `_alertbox.html.twig` |
| **Client-Side Interactivity** | Alpine.js / Vanilla JS | Accordions, tabs, print buttons, collapse boxes | `_dossier_disclosure.html.twig` |
| **Server-Side Data/Queries** | PHP-backed Twig Functions | Recent posts loops, site configurations, database counters | `_alerts_ticker.html.twig` |
| **Procedural Backends** | Native PHP Layouts (`.php`) | Heavy, procedural, legacy scripts or server-level bindings | `_custom_block.php` |
