# Salieno Template Developer Guide

> Complete reference for building custom templates for the Salieno hosting & billing platform.
> Version: 1.0 · Last Updated: February 2026

---

## Table of Contents

1. [Architecture Overview](#1-architecture-overview)
2. [File Structure](#2-file-structure)
3. [theme.json Schema](#3-themejson-schema)
4. [The Layout File](#4-the-layout-file)
5. [Section System In-Depth](#5-section-system-in-depth)
6. [View Composers & Automatic Data](#6-view-composers--automatic-data)
7. [Helper Functions Reference](#7-helper-functions-reference)
8. [CSS Custom Properties Reference](#8-css-custom-properties-reference)
9. [SEO & Meta Tags](#9-seo--meta-tags)
10. [Dark Mode Integration](#10-dark-mode-integration)
11. [Routing & Page Views](#11-routing--page-views)
12. [View Data Reference Per Page](#12-view-data-reference-per-page)
13. [Asset Management & Vite](#13-asset-management--vite)
14. [Error Page Overrides](#14-error-page-overrides)
15. [Security Checklist](#15-security-checklist)
16. [Performance Best Practices](#16-performance-best-practices)
17. [Complete Working Examples](#17-complete-working-examples)

---

## 1. Architecture Overview

### How Templates Work

Salieno uses a template system where the **public-facing frontend** (homepage, product pages, domain search, contact, etc.) is rendered by the active template. The **client area** (dashboard, invoices, services) and **admin panel** use their own fixed layouts.

```
┌──────────────────────────────────────────────┐
│  What the TEMPLATE controls:                 │
│  ✓ Public homepage                           │
│  ✓ Product/pricing pages                     │
│  ✓ Domain search page                        │
│  ✓ Contact page                              │
│  ✓ Knowledge base pages                      │
│  ✓ Custom pages                              │
│  ✓ Error pages (optional override)           │
│                                              │
│  What the TEMPLATE does NOT control:         │
│  ✗ Admin panel (layouts/admin.blade.php)     │
│  ✗ Client area (layouts/client.blade.php)    │
│  ✗ Auth pages (layouts/admin-auth.blade.php) │
└──────────────────────────────────────────────┘
```

### Key Concepts

| Concept | Description |
|---------|-------------|
| **Template** | A folder in `resources/views/templates/<name>/` containing all view files |
| **theme.json** | Configuration file defining metadata, colors, and feature support |
| **Sections** | CMS-managed content blocks that admins configure from the admin panel |
| **sections.json** | Maps which sections appear on which pages |
| **View Composers** | Classes that automatically inject data into specific views |
| **Frontend model** | Database ORM model (`frontends` table) storing section content |

### Data Flow

```
1. User visits /products
2. SiteController::products() is called
3. Controller fetches page data (products, categories)
4. Returns view: activeTemplate() . 'pages.products'
     → resolves to: templates.salieno.pages.products
5. View extends templates.salieno.layouts.app
6. View Composers inject additional data (menu, social, cart, etc.)
7. Section data loaded via getContent() from frontends table
```

---

## 2. File Structure

```
resources/views/templates/<your-template>/
│
├── theme.json                      # REQUIRED — metadata, colors, support flags
├── preview.jpg                     # REQUIRED — theme preview (1200×800px recommended)
├── sections.json                   # REQUIRED — maps sections to pages
│
├── layouts/
│   └── app.blade.php               # REQUIRED — main public layout (wraps all pages)
│
├── sections/                       # CMS-driven content blocks
│   ├── hero.blade.php              # Hero/banner with heading, subheading, CTA
│   ├── hero_1.blade.php            # Alternate hero (receives productCategories via composer)
│   ├── features.blade.php          # Feature cards grid
│   ├── pricing.blade.php           # Pricing tables (receives $products via composer)
│   ├── domain.blade.php            # Domain search (receives $realCategories via composer)
│   ├── domain_table.blade.php      # TLD pricing table
│   ├── domain_features.blade.php   # Domain feature highlights
│   ├── domain_tips.blade.php       # Domain tips/suggestions
│   ├── domain_transfer.blade.php   # Domain transfer CTA
│   ├── faq.blade.php               # FAQ accordion (multi-item)
│   ├── cta.blade.php               # Call to action banner
│   ├── bottom_cta.blade.php        # Footer CTA
│   ├── how_it_works.blade.php      # Step-by-step process (multi-item)
│   ├── comparison.blade.php        # Feature comparison (receives pricing data)
│   ├── categories.blade.php        # Product categories (receives $serviceCategories)
│   ├── category_pricing.blade.php  # Category-specific pricing
│   ├── category_comparison.blade.php # Category comparison
│   ├── dedicated_banner.blade.php  # Dedicated hosting banner (receives pricing data)
│   ├── kb.blade.php                # Knowledge base section
│   ├── migration.blade.php         # Server migration CTA
│   └── testimonials.blade.php      # Customer testimonials (multi-item)
│
├── pages/                          # Custom page templates (optional)
│   ├── home.blade.php              # Homepage (rendered by SiteController)
│   ├── contact.blade.php           # Contact page
│   └── ...
│
├── partials/                       # Reusable template components
│   ├── header.blade.php            # Navigation (receives $serviceCategories)
│   ├── footer.blade.php            # Footer (receives $socialLinks via composer)
│   ├── breadcrumb.blade.php        # Breadcrumb navigation
│   ├── cart_widget.blade.php       # Cart (receives $cartCount via composer)
│   └── domain_search_form.blade.php # Reusable domain search form
│
└── errors/                         # Custom error pages (optional)
    ├── 404.blade.php
    ├── 500.blade.php
    └── ...
```

---

## 3. theme.json Schema

### Full Schema

```json
{
    "name": "My Theme",
    "version": "1.0.0",
    "author": "Your Name",
    "description": "Short description shown in admin panel.",
    "preview": "preview.jpg",
    "requires_salieno": "1.0.0",

    "supports": {
        "sections": true,
        "dark_mode": true,
        "rtl": false
    },

    "settings": {
        "show_mode_switcher": true
    },

    "colors": {
        "primary": "#CC2628",
        "secondary": "#18181B",
        "neutral_dark": "#0A0A0A",
        "neutral_mid": "#18181B",
        "neutral_light": "#FFFFFF",
        "neutral_border": "#27272A",
        "neutral_muted": "#A1A1AA",
        "heading_light": "#1f1b4d",
        "surface_offwhite": "#fcfcfd",
        "svg_dark": "#0f172a",
        "svg_mid": "#1e293b",
        "svg_light": "#334155",
        "svg_muted": "#94a3b8",
        "svg_subtle": "#cbd5e1",
        "svg_surface": "#f1f5f9",
        "footer_bg": "#111827",
        "footer_text": "#F9FAFB",
        "footer_bg_dark": "#050505",
        "footer_text_dark": "#E2E8F0"
    },

    "gradients": {
        "primary": "linear-gradient(135deg, #CC2628 0%, #B01E20 100%)",
        "neutral": "linear-gradient(135deg, #0A0A0A 0%, #18181B 100%)"
    }
}
```

### Required Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Display name shown in Admin → Templates |
| `version` | semver | Your template version (e.g., `"1.0.0"`) |
| `author` | string | Author name or organization |
| `description` | string | Brief description for admin panel |
| `preview` | string | Filename of preview image (relative to template dir) |
| `requires_salieno` | semver | Minimum Salieno version required |

### `supports` Object

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `sections` | bool | `true` | Enable the section system for CMS content |
| `dark_mode` | bool | `false` | Declare dark mode support |
| `rtl` | bool | `false` | Declare right-to-left layout support |

### `settings` Object

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `show_mode_switcher` | bool | `false` | Show light/dark mode toggle to visitors |

### `colors` Object — Color Mapping

Colors in `theme.json` automatically override the client area defaults:

| theme.json Key | Overrides Client Area Setting | CSS Variable |
|----------------|------------------------------|--------------|
| `primary` | `primary_color` | `--primary` |
| `secondary` | `secondary_color` | — |
| `neutral_dark` | `text_primary` | — |
| `neutral_mid` | `text_secondary` | — |
| `neutral_light` | `card_bg` | `--card-bg` |
| `neutral_border` | `card_border` | `--card-border` |
| `neutral_muted` | `text_muted` | — |
| `header_bg` | `header_bg` | — |

Colors with `_dark` suffix (e.g., `footer_bg_dark`) are used in dark mode.

### Accessing theme.json in Blade

```blade
{{-- Read any key --}}
{{ templateMeta('name') }}                  → "Salieno Theme"
{{ templateMeta('version') }}               → "1.0.0"
{{ templateMeta('colors.primary') }}        → "#CC2628"
{{ templateMeta('supports.dark_mode') }}    → true
{{ templateMeta('gradients.primary') }}     → "linear-gradient(...)"

{{-- With fallback --}}
{{ templateMeta('colors.accent', '#3B82F6') }}
```

> **Caching:** `templateMeta()` is cached for 1 hour, keyed by template name AND file modification time. Editing `theme.json` automatically busts the cache.

---

## 4. The Layout File

Your `layouts/app.blade.php` is the master template. Every public page extends it.

### Full Reference Layout

```blade
<!doctype html>
<html lang="{{ config('app.locale') }}" class="scroll-smooth">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    {{-- ═══ SEO (see Section 9 for full details) ═══ --}}
    @php
        $globalSeo = \Illuminate\Support\Facades\Cache::remember(
            'seo_data_' . activeTemplateName(),
            3600,
            fn () => \App\Models\Frontend::where('data_keys', 'seo.data')
                ->where('tempname', activeTemplateName())
                ->first()?->data_values
        );
        $pageSeo = $seoContents ?? null;
        $seoDescription = $pageSeo?->description ?? $globalSeo?->description ?? '';
        $seoKeywords = $pageSeo?->keywords ?? $globalSeo?->keywords ?? [];
        $seoImage = siteLogo();
    @endphp

    <title>{{ gs()->siteName($pageTitle ?? '') }}</title>
    <meta name="description" content="{{ $seoDescription }}">
    <meta name="robots" content="index, follow">
    <meta name="theme-color" content="{{ templateMeta('colors.primary', '#CC2628') }}">

    {{-- Canonical --}}
    <link rel="canonical" href="{{ url()->current() }}">

    {{-- Open Graph --}}
    <meta property="og:title" content="{{ $pageTitle ?? gs('site_name') }}">
    <meta property="og:description" content="{{ $seoDescription }}">
    <meta property="og:image" content="{{ $seoImage }}">
    <meta property="og:type" content="website">

    {{-- Favicon --}}
    <link rel="icon" type="image/png" href="{{ siteFavicon() }}">

    {{-- Fonts (async loading — non-render-blocking) --}}
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..800&display=swap"
          rel="stylesheet" media="print" onload="this.media='all'" />
    <noscript>
      <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..800&display=swap"
            rel="stylesheet" />
    </noscript>

    {{-- Vite CSS & JS --}}
    @vite(['resources/css/app.css', 'resources/js/app.js'])

    {{-- Per-page styles --}}
    @stack('styles')
</head>
<body class="antialiased">

    {{-- Header navigation --}}
    @include(activeTemplate() . 'partials.header')

    {{-- Breadcrumb (optional — only on inner pages) --}}
    @hasSection('breadcrumb')
        @yield('breadcrumb')
    @endif

    {{-- Main content --}}
    <main>
        @yield('content')
    </main>

    {{-- Footer --}}
    @include(activeTemplate() . 'partials.footer')

    {{-- Per-page scripts (MUST have nonce for CSP) --}}
    @stack('scripts')

    {{-- Cookie consent --}}
    @include('partials.cookie-consent')
</body>
</html>
```

### Key Points

| Rule | Why |
|------|-----|
| Use `activeTemplate() . 'partials.header'` for includes | Resolves to your template's view path |
| Use `@yield('content')` not `{{ $slot }}` | Templates use `@section/@yield`, not Livewire slots |
| Add `nonce="{{ csp_nonce() }}"` to inline `<script>` | Required by the CSP policy (blocks scripts without nonce) |
| Use `@stack('scripts')` at end of body | Allows per-page scripts from child views |
| Use `$pageTitle` variable | Passed by every controller, contains the page title |

---

## 5. Section System In-Depth

### Database Structure

Section content is stored in the `frontends` table:

| Column | Type | Example |
|--------|------|---------|
| `id` | int | `1` |
| `tempname` | string | `salieno` |
| `data_keys` | string | `hero.content` |
| `data_values` | JSON (cast to object) | `{"heading": "Welcome", "image": "hero.jpg"}` |

### Key Format

The `data_keys` column uses dot notation: `<section_name>.<type>`

| Pattern | Meaning | Use Case |
|---------|---------|----------|
| `hero.content` | Single content block | Hero banner heading/subheading |
| `hero.element` | Repeatable items | FAQ items, feature cards |
| `seo.data` | Global data | SEO metadata |

### sections.json — Page-to-Section Mapping

This file tells Salieno which sections should appear on which pages, and in what order:

```json
{
    "home": {
        "hero": { "name": "Hero Banner", "order": 1 },
        "features": { "name": "Features", "order": 2 },
        "pricing": { "name": "Pricing Plans", "order": 3 },
        "domain": { "name": "Domain Search", "order": 4 },
        "faq": { "name": "FAQ", "order": 5 },
        "cta": { "name": "Call to Action", "order": 6 }
    },
    "products": {
        "categories": { "name": "Categories", "order": 1 },
        "comparison": { "name": "Comparison", "order": 2 }
    },
    "domain": {
        "domain": { "name": "Domain Search", "order": 1 },
        "domain_table": { "name": "TLD Pricing", "order": 2 },
        "domain_features": { "name": "Features", "order": 3 }
    }
}
```

### Fetching Section Data — `getContent()`

```php
// Signature:
getContent(
    string $key,            // Section key (e.g., 'hero' or 'hero.content')
    bool $singleQuery,      // true = single record, false = collection
    ?int $limit,            // Max items for collections
    bool $orderById         // true = ascending, false = descending (default)
): Model|Collection

// Under the hood:
Frontend::where('tempname', activeTemplateName())
        ->where('data_keys', $key)
        ->orderBy('id', 'desc')
        ->first(); // or ->get()
```

### Section Template Example — Single Content Block

```blade
{{-- sections/hero.blade.php --}}
@php
    $hero = getContent('hero.content', singleQuery: true);
    $data = $hero->data_values ?? (object)[];
@endphp

@if($hero)
<section class="hero py-20 bg-gradient-to-br from-gray-900 to-black text-white">
    <div class="container mx-auto px-4 text-center">
        <h1 class="text-5xl font-bold mb-6">{{ __($data->heading ?? 'Welcome') }}</h1>
        <p class="text-xl text-gray-300 mb-8 max-w-2xl mx-auto">
            {{ __($data->subheading ?? '') }}
        </p>
        @if(!empty($data->button_text))
            <a href="{{ $data->button_url ?? '#' }}"
               class="inline-block px-8 py-3 rounded-lg font-semibold
                      bg-[{{ templateMeta('colors.primary', '#CC2628') }}]
                      text-white hover:opacity-90 transition">
                {{ __($data->button_text) }}
            </a>
        @endif
    </div>
</section>
@endif
```

### Section Template Example — Repeatable Items

```blade
{{-- sections/faq.blade.php --}}
@php
    $title = getContent('faq.content', singleQuery: true);
    $items = getContent('faq.element', singleQuery: false);
    $heading = $title?->data_values?->heading ?? 'Frequently Asked Questions';
@endphp

@if($items->count())
<section class="py-20" id="faq">
    <div class="container mx-auto px-4 max-w-3xl">
        <h2 class="text-3xl font-bold text-center mb-12">{{ __($heading) }}</h2>

        <div class="space-y-4" x-data="{ active: null }">
            @foreach($items as $index => $item)
                @php $faq = $item->data_values; @endphp
                <div class="border rounded-xl overflow-hidden">
                    <button @click="active = active === {{ $index }} ? null : {{ $index }}"
                            class="w-full p-4 text-left font-medium flex justify-between">
                        {{ __($faq->question ?? '') }}
                        <span x-text="active === {{ $index }} ? '−' : '+'"></span>
                    </button>
                    <div x-show="active === {{ $index }}"
                         x-transition class="p-4 border-t text-gray-600">
                        {{ __($faq->answer ?? '') }}
                    </div>
                </div>
            @endforeach
        </div>
    </div>
</section>
@endif
```

### Using Images from Section Data

Section images are stored in `assets/images/frontend/`:

```blade
@if(!empty($data->image))
    <img src="{{ getImage('assets/images/frontend/' . $data->image) }}"
         alt="{{ $data->heading ?? '' }}"
         width="600" height="400"
         loading="lazy">
@endif
```

---

## 6. View Composers & Automatic Data

View composers automatically inject data into specific views. **You don't need to query this data yourself** — it's available automatically when your view file matches the registered pattern.

### Composers Registered for Templates

| View Pattern | Composer | Variables Injected |
|---|---|---|
| `templates.<name>.partials.footer` | `FooterComposer` | `$socialLinks` — all configured social media links |
| `templates.<name>.partials.cart_widget` | `CartWidgetComposer` | `$cartCount` — items in shopping cart |
| `templates.<name>.sections.pricing` | `PricingComposer` | `$products`, `$billingCycles`, `$longestCycle` |
| `templates.<name>.sections.hero_1` | `BannerPricingComposer` | `$productCategories`, `$billingCycles` |
| `templates.<name>.sections.comparison` | `BannerPricingComposer` | Same as above |
| `templates.<name>.sections.categories` | `BannerPricingComposer` | Same as above |
| `templates.<name>.sections.category_pricing` | `BannerPricingComposer` | Same + `$category` |
| `templates.<name>.sections.dedicated_banner` | `BannerPricingComposer` | Same as above |
| `templates.<name>.sections.domain` | Inline (AppServiceProvider) | `$realCategories` — domain categories |
| `templates.<name>.partials.header` | Inline (AppServiceProvider) | `$serviceCategories` — product categories with products |
| `templates.<name>.sections.categories` | Inline (AppServiceProvider) | `$serviceCategories` |

### How to Use Injected Data

```blade
{{-- partials/footer.blade.php --}}
{{-- $socialLinks is auto-injected by FooterComposer --}}
@foreach($socialLinks as $link)
    <a href="{{ $link->url }}" target="_blank" rel="noopener">
        <i class="{{ $link->icon }}"></i>
    </a>
@endforeach
```

```blade
{{-- sections/pricing.blade.php --}}
{{-- $products, $billingCycles, $longestCycle are auto-injected --}}
@foreach($products as $product)
    <div class="pricing-card">
        <h3>{{ $product->name }}</h3>
        <p>{{ pricing($product->payment_type, $product->pricing, 'price') }}/mo</p>
    </div>
@endforeach
```

> **Important:** The view composer only fires when the view name **exactly matches** the registered pattern. If you rename `sections/pricing.blade.php` to `sections/plans.blade.php`, the composer won't inject data — you'll need to register your own or query manually.

---

## 7. Helper Functions Reference

### Site Settings — `gs()`

The `gs()` helper fetches values from the `general_settings` table. It's **cached for 1 hour** with per-request memoization.

```blade
{{-- Get entire settings object --}}
@php $settings = gs(); @endphp
{{ $settings->site_name }}
{{ $settings->cur_sym }}      {{-- Currency symbol: $ --}}
{{ $settings->cur_text }}     {{-- Currency code: USD --}}

{{-- Get single value --}}
{{ gs('site_name') }}
{{ gs('active_template') }}

{{-- Get multiple values at once --}}
@php [$name, $currency] = array_values(gs(['site_name', 'cur_sym'])); @endphp
```

**Available `gs()` keys** (most commonly used):

| Key | Type | Example | Description |
|-----|------|---------|-------------|
| `site_name` | string | `"Salieno"` | Site name |
| `cur_text` | string | `"USD"` | Currency code |
| `cur_sym` | string | `"$"` | Currency symbol |
| `tax` | int | `0` | Tax percentage |
| `active_template` | string | `"salieno"` | Active template name |
| `paginate_number` | int | `20` | Default pagination count |
| `ev` | bool | `1` | Email verification enabled |
| `sv` | bool | `1` | SMS verification enabled |
| `registration` | bool | `1` | User registration enabled |
| `agree` | bool | `1` | Terms agreement required |
| `multi_language` | bool | `1` | Multi-language enabled |
| `force_ssl` | bool | `0` | Force HTTPS |
| `maintenance_mode` | bool | `0` | Maintenance mode active |
| `logo` | string | `"logo.png"` | Logo filename |
| `dark_logo` | string | `"logo_dark.png"` | Dark mode logo filename |
| `deposit_module` | bool | `1` | Deposits enabled |
| `affiliate_enabled` | bool | `0` | Affiliate system enabled |
| `ticket_enabled` | bool | `1` | Support tickets enabled |
| `kyc_required_for_orders` | bool | `0` | KYC required before ordering |

### Template & Theme Helpers

```blade
{{-- Template path prefix for view references --}}
{{ activeTemplate() }}
→ "templates.salieno."

{{-- For asset paths --}}
{{ activeTemplate(true) }}
→ "assets/templates/salieno/"

{{-- Just the name --}}
{{ activeTemplateName() }}
→ "salieno"

{{-- Read from theme.json (cached 1hr, bust on file change) --}}
{{ templateMeta('name') }}
{{ templateMeta('colors.primary', '#000') }}
{{ templateMeta('supports.dark_mode') }}

{{-- theme_setting() — read admin-configured theme settings --}}
{{ theme_setting('custom_key', 'default_value') }}
```

### Content & Section Helpers

```blade
{{-- Single content block --}}
@php $hero = getContent('hero.content', singleQuery: true); @endphp

{{-- Multiple items (e.g., FAQ items) --}}
@php $faqs = getContent('faq.element', singleQuery: false, limit: 10); @endphp

{{-- Ordered ascending (oldest first) --}}
@php $steps = getContent('how_it_works.element', false, null, orderById: true); @endphp

{{-- Page sections configuration from sections.json --}}
@php $sections = getPageSections(asArray: true); @endphp
```

### Asset & Image Helpers

```blade
{{-- Site logo --}}
<img src="{{ siteLogo() }}" alt="{{ gs('site_name') }}">

{{-- Dark mode logo --}}
<img src="{{ siteLogo('dark') }}" alt="{{ gs('site_name') }}">

{{-- Favicon --}}
<link rel="icon" href="{{ siteFavicon() }}">

{{-- Uploaded image (with optional size) --}}
<img src="{{ getImage('assets/images/frontend/hero.jpg') }}">
<img src="{{ getImage('assets/images/frontend/hero.jpg', '600x400') }}">

{{-- Storage path for a file category --}}
{{ getFilePath('logo') }}       → "assets/images/logo"
{{ getFilePath('seo') }}        → "assets/images/seo"
{{ getFilePath('frontend') }}   → "assets/images/frontend"
```

### Formatting Helpers

```blade
{{-- Currency formatting --}}
{{ showAmount(125) }}           → "$125.00"
{{ showAmount(125, 0) }}        → "$125"
{{ format_price(99.99) }}       → "$99.99"
{{ formatCurrency(1500, 2) }}   → "$1,500.00"

{{-- Date/time --}}
{{ showDateTime($date) }}       → "2026-02-25 10:30 PM"
{{ showDateTime($date, 'd M Y') }} → "25 Feb 2026"
{{ diffForHumans($date) }}      → "2 hours ago"

{{-- Text --}}
{{ strLimit($text, 50) }}       → "This is a long text th..."
{{ keyToTitle('some_key') }}    → "Some Key"

{{-- Billing cycles --}}
{{ billing_cycle_name('monthly') }}  → "Monthly"
{{ billing_cycle_short('monthly') }} → "/mo"
```

### Navigation Helpers

```blade
{{-- Feature flags --}}
@if(isFeatureEnabled('domain_module'))
    <a href="{{ route('domain.search') }}">Domains</a>
@endif

{{-- Menu system --}}
@foreach(menuItems('header') as $item)
    <a href="{{ $item->url }}"
       class="{{ isMenuItemActive($item) ? 'active' : '' }}">
        {{ $item->label }}
    </a>
@endforeach

{{-- Social links --}}
@foreach(socialLinks() as $link)
    <a href="{{ $link->url }}">{{ $link->name }}</a>
@endforeach
```

### Security Helpers

```blade
{{-- CSP nonce (REQUIRED for inline scripts) --}}
<script nonce="{{ csp_nonce() }}">
    console.log('This script is allowed by CSP');
</script>

{{-- Sanitize user-generated HTML --}}
{!! purify($htmlContent) !!}

{{-- Sanitize CSS (strips script tags) --}}
<style>{{ sanitize_css($adminCustomCss) }}</style>

{{-- URL sanitization --}}
<a href="{{ clean_url($userUrl) }}">Link</a>

{{-- reCAPTCHA --}}
{!! loadReCaptcha() !!}
```

### Pricing Helper

The `pricing()` function generates `<option>` elements for billing cycle dropdowns:

```blade
{{-- Full options list --}}
<select name="billing_cycle">
    {!! pricing($product->payment_type, $product->pricing) !!}
</select>

{{-- Just the starting price --}}
{{ pricing($product->payment_type, $product->pricing, 'price') }}

{{-- Just the setup fee --}}
{{ pricing($product->payment_type, $product->pricing, 'setupFee') }}

{{-- Billing type label --}}
{{ pricing($product->payment_type, $product->pricing, null, showText: true) }}
→ "Monthly" or "One Time"
```

---

## 8. CSS Custom Properties Reference

The client area injects CSS custom properties via `ClientAreaThemeService`. These are available in your template CSS via `var(--name)`.

### Full Variable List

| Variable | Default | Description |
|----------|---------|-------------|
| **Colors** | | |
| `--primary` | `#18181b` | Primary brand color |
| `--primary-hover` | `#09090b` | Hover state for primary |
| `--primary-rgb` | `24, 24, 27` | Primary as RGB (for rgba()) |
| `--bg` | `#ffffff` | Page background |
| `--bg-subtle` | `#f4f4f5` | Subtle background variation |
| `--card-bg` | `#ffffff` | Card/panel background |
| `--card-border` | `#e4e4e7` | Card border color |
| `--success` | `#10b981` | Success/active state |
| `--warning` | `#f59e0b` | Warning state |
| `--danger` | `#ef4444` | Error/danger state |
| `--info` | `#71717a` | Informational state |
| **Typography** | | |
| `--font-family` | `Inter` | Body font family |
| `--heading-font` | `Inter` | Heading font family |
| **Layout** | | |
| `--border-radius` | `12px` | Global border radius |
| `--sidebar-width` | `280px` | Sidebar width |
| `--shadow-base-opacity` | `0.1` | Shadow intensity |

### Using CSS Variables

```css
/* In your template CSS */
.cta-button {
    background: var(--primary);
    color: white;
    border-radius: calc(var(--border-radius) * 1px);
    transition: background 0.2s;
}
.cta-button:hover {
    background: var(--primary-hover);
}

/* Use RGB variant for transparency */
.hero-overlay {
    background: rgba(var(--primary-rgb), 0.8);
}

.card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: calc(var(--border-radius) * 1px);
}
```

### Overriding via theme.json

Colors set in your `theme.json` automatically override the defaults in the client area:

```json
{
    "colors": {
        "primary": "#3B82F6"
    }
}
```

This changes `--primary` from `#18181b` to `#3B82F6` in the client area.

---

## 9. SEO & Meta Tags

### How SEO Works

SEO data flows from three sources, with this priority:

```
Page-level SEO ($seoContents) → Global SEO (frontends table) → Fallback (gs())
```

| Source | Set By | Example |
|--------|--------|---------|
| **Page-level** | Controller passes `$seoContents` to view | Product page SEO |
| **Global** | Admin → Settings → SEO | Site-wide defaults |
| **Fallback** | `gs('site_name')` | Last resort |

### Implementation in Layout

```blade
@php
    // 1. Load global SEO (cached 1 hour)
    $globalSeo = Cache::remember(
        'seo_data_' . activeTemplateName(),
        3600,
        fn () => \App\Models\Frontend::where('data_keys', 'seo.data')
            ->where('tempname', activeTemplateName())
            ->first()?->data_values
    );

    // 2. Page-level override (passed by controller as $seoContents)
    $pageSeo = $seoContents ?? null;

    // 3. Resolve with fallback chain
    $seoTitle       = $pageSeo?->meta_title ?? $globalSeo?->meta_title ?? ($pageTitle ?? '');
    $seoDescription = $pageSeo?->description ?? $globalSeo?->description ?? '';
    $seoKeywords    = $pageSeo?->keywords ?? $globalSeo?->keywords ?? [];
    $seoImage       = siteLogo();
@endphp

<title>{{ gs()->siteName($seoTitle) }}</title>
<meta name="description" content="{{ $seoDescription }}">
@if(is_array($seoKeywords) && count($seoKeywords))
    <meta name="keywords" content="{{ implode(', ', $seoKeywords) }}">
@endif
<meta property="og:title" content="{{ $seoTitle }}">
<meta property="og:description" content="{{ $seoDescription }}">
<meta property="og:image" content="{{ $seoImage }}">
<link rel="canonical" href="{{ url()->current() }}">
```

### SEO Data Fields (from `frontends` table)

| Field | Type | Description |
|-------|------|-------------|
| `meta_title` | string | Browser tab title (appended to site name) |
| `description` | string | Meta description |
| `keywords` | array | SEO keywords |
| `meta_robots` | string | robots directive (e.g., `"index, follow"`) |
| `social_title` | string | OG/Twitter card title |
| `social_description` | string | OG/Twitter card description |
| `image` | string | Filename of social sharing image |

---

## 10. Dark Mode Integration

### Step 1: Declare support

```json
{
    "supports": { "dark_mode": true },
    "settings": { "show_mode_switcher": true },
    "colors": {
        "footer_bg": "#111827",
        "footer_bg_dark": "#050505"
    }
}
```

### Step 2: Include the theme controller

```blade
{{-- In your layout, before </body> --}}
@include('partials.theme-controller')
```

This provides:
- A toggle button for light/dark mode
- `localStorage` persistence of user preference
- Adds/removes `dark` class on `<html>` element
- Respects `prefers-color-scheme` OS setting

### Step 3: Write dark-aware CSS

**Option A — Tailwind dark: prefix:**

```blade
<div class="bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100">
    <h1 class="text-zinc-800 dark:text-zinc-200">Hello</h1>
</div>
```

**Option B — CSS with .dark class:**

```css
.hero { background: #ffffff; color: #09090b; }
.dark .hero { background: #0a0a0a; color: #f4f4f5; }
```

**Option C — CSS custom properties (auto-switches):**

```css
:root { --hero-bg: #ffffff; }
.dark { --hero-bg: #0a0a0a; }
.hero { background: var(--hero-bg); }
```

---

## 11. Routing & Page Views

### How URLs Map to Views

The `SiteController` handles public page routing. Each action passes a `$pageTitle` and section data to the appropriate template view:

| URL | Controller Method | Template View |
|-----|-------------------|---------------|
| `/` | `index()` | `templates.<name>.home` |
| `/products` | `products()` | `templates.<name>.pages.products` |
| `/products/{slug}` | `categoryProducts()` | `templates.<name>.pages.category_products` |
| `/product/{slug}` | `productDetails()` | `templates.<name>.pages.product_detail` |
| `/domain` | `domain()` | `templates.<name>.pages.domain` |
| `/contact` | `contact()` | `templates.<name>.pages.contact` |
| `/knowledgebase` | `knowledgebase()` | `templates.<name>.pages.kb` |
| `/knowledgebase/{slug}` | `kbArticle()` | `templates.<name>.pages.kb_article` |
| `/page/{slug}` | `page()` | `templates.<name>.pages.custom` |

### Creating a Page View

```blade
{{-- pages/products.blade.php --}}
@extends(activeTemplate() . 'layouts.app')

@section('content')
    {{-- Breadcrumb --}}
    @include(activeTemplate() . 'partials.breadcrumb', [
        'title' => $pageTitle ?? __('Products'),
        'breadcrumbs' => [['name' => __('Products')]]
    ])

    {{-- Sections from sections.json (rendered in order) --}}
    @foreach($sections ?? [] as $key => $section)
        @if(view()->exists(activeTemplate() . 'sections.' . $key))
            @include(activeTemplate() . 'sections.' . $key)
        @endif
    @endforeach
@endsection

@push('scripts')
<script nonce="{{ csp_nonce() }}">
    // Page-specific JavaScript
</script>
@endpush
```

---

## 12. View Data Reference Per Page

### Variables Available in ALL Views

| Variable | Type | How to Access |
|----------|------|---------------|
| Site settings | `GeneralSetting` | `gs()` or `gs('site_name')` |
| Template path | `string` | `activeTemplate()` |
| Template name | `string` | `activeTemplateName()` |
| Theme metadata | `mixed` | `templateMeta('key')` |
| CSP nonce | `string` | `csp_nonce()` |

### Variables Passed by Controllers

| Variable | Available In | Type | Description |
|----------|-------------|------|-------------|
| `$pageTitle` | All pages | `string` | Page title for `<title>` and breadcrumbs |
| `$sections` | Homepage, product pages | `array` | Ordered sections from sections.json |
| `$seoContents` | Pages with custom SEO | `object\|null` | Page-specific SEO overrides |
| `$products` | Product pages | `Collection` | Product models with pricing |
| `$categories` | Product pages | `Collection` | Service categories |
| `$product` | Product detail | `Product` | Single product with pricing, features |
| `$tlds` | Domain page | `Collection` | TLD models with pricing |
| `$article` | KB article | `KbArticle` | Single knowledge base article |
| `$articles` | KB index | `Collection` | KB articles list |

### Variables Injected by View Composers

| Variable | Available In | Type |
|----------|-------------|------|
| `$socialLinks` | `partials.footer` | `Collection` |
| `$cartCount` | `partials.cart_widget` | `int` |
| `$serviceCategories` | `partials.header`, `sections.categories`, `home` | `Collection` (with `activeProducts.pricing`) |
| `$realCategories` | `sections.domain` | `Collection` of domain categories |
| `$products` | `sections.pricing` | `Collection` (with pricing) |
| `$billingCycles` | `sections.pricing`, banner sections | `array` |
| `$longestCycle` | `sections.pricing` | `string` |
| `$productCategories` | Banner sections (`hero_1`, `comparison`, etc.) | `Collection` |
| `$category` | `sections.category_pricing`, `sections.category_comparison` | `ServiceCategory\|null` |

---

## 13. Asset Management & Vite

### Vite Entry Points

```blade
{{-- In your layout <head> --}}
@vite(['resources/css/app.css', 'resources/js/app.js'])
```

This loads the core CSS and JS. The client area has separate entries (`client-area.css`, `client-area.js`) that your template doesn't need to include.

### Static Assets

Place template-specific assets in the public directory:

```
public/assets/templates/<your-template>/
├── images/
├── css/
└── js/
```

Reference them with:

```blade
<img src="{{ asset(activeTemplate(true) . 'images/pattern.svg') }}">
```

### External CDN Scripts

Always use `defer` for external scripts to avoid render-blocking:

```blade
<script src="https://cdn.example.com/lib.js" defer></script>
```

For modules loaded only on specific pages, use `@push`:

```blade
@push('scripts')
<script src="https://cdn.example.com/chart.js" defer></script>
<script nonce="{{ csp_nonce() }}">
    // Initialize after DOM ready
    document.addEventListener('DOMContentLoaded', () => {
        // ...
    });
</script>
@endpush
```

---

## 14. Error Page Overrides

Templates can override the default error pages. Create styled versions in your template's `errors/` directory:

```
templates/<name>/errors/
├── 403.blade.php    # Forbidden
├── 404.blade.php    # Not Found
├── 419.blade.php    # Session Expired
├── 429.blade.php    # Too Many Requests
├── 500.blade.php    # Server Error
└── 503.blade.php    # Maintenance
```

### Error View Template

```blade
{{-- errors/404.blade.php --}}
@extends(activeTemplate() . 'layouts.app')

@php $pageTitle = '404 — Page Not Found'; @endphp

@section('content')
<section class="py-32 text-center">
    <div class="container mx-auto px-4">
        <h1 class="text-8xl font-black text-gray-200 mb-4">404</h1>
        <p class="text-xl text-gray-600 mb-8">
            {{ __("The page you're looking for doesn't exist.") }}
        </p>
        <a href="{{ url('/') }}"
           class="px-6 py-3 bg-[{{ templateMeta('colors.primary') }}] text-white rounded-lg">
            {{ __('Go Home') }}
        </a>
    </div>
</section>
@endsection
```

### Resolution Order

1. `templates.<active>.errors.<code>` — Your template's error page
2. `errors.<code>` — Core branded error pages
3. Laravel's built-in error rendering — Last resort

---

## 15. Security Checklist

| Requirement | How | Why |
|-------------|-----|-----|
| **CSP nonce on `<script>`** | `nonce="{{ csp_nonce() }}"` | Scripts without nonce are blocked |
| **Escape user content** | `{{ $var }}` not `{!! $var !!}` | Prevents XSS |
| **Sanitize HTML from DB** | `{!! purify($html) !!}` | Strips malicious tags |
| **Sanitize admin CSS** | `{{ sanitize_css($css) }}` | Strips `<script>` tags from CSS |
| **Sanitize URLs** | `{{ clean_url($url) }}` | Prevents `javascript:` URLs |
| **CSRF token** | `<meta name="csrf-token" content="{{ csrf_token() }}">` | Required for AJAX |
| **Translate strings** | `{{ __('text') }}` | Multi-language support |

---

## 16. Performance Best Practices

| Practice | Implementation |
|----------|---------------|
| **Cache expensive queries** | `Cache::remember('key', 3600, fn() => ...)` |
| **Async font loading** | `media="print" onload="this.media='all'"` with `<noscript>` fallback |
| **Defer external JS** | `<script src="..." defer></script>` |
| **Lazy load images** | `loading="lazy"` on below-fold `<img>` |
| **Set image dimensions** | Always add `width` and `height` attributes |
| **Reduce motion** | `@media (prefers-reduced-motion: reduce)` |
| **Minimize inline CSS/JS** | Use external files via Vite where possible |
| **Translate strings** | Wrap all text in `__()` for localization |

---

## 17. Complete Working Examples

### Example: Product Category Page Section

```blade
{{-- sections/categories.blade.php --}}
{{-- $serviceCategories is AUTO-INJECTED by view composer --}}

@if(isset($serviceCategories) && $serviceCategories->count())
<section class="py-20 bg-gray-50">
    <div class="container mx-auto px-4">
        <h2 class="text-3xl font-bold text-center mb-4">
            {{ __('Our Hosting Solutions') }}
        </h2>
        <p class="text-gray-600 text-center mb-12 max-w-2xl mx-auto">
            {{ __('Choose the perfect hosting plan for your needs') }}
        </p>

        <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
            @foreach($serviceCategories as $category)
                <div class="bg-white rounded-2xl p-6 shadow-sm hover:shadow-lg transition">
                    @if($category->image)
                        <img src="{{ getImage('assets/images/category/' . $category->image) }}"
                             alt="{{ $category->name }}"
                             width="64" height="64"
                             class="mb-4">
                    @endif

                    <h3 class="text-xl font-semibold mb-2">{{ __($category->name) }}</h3>
                    <p class="text-gray-600 mb-4">{{ strLimit($category->description, 100) }}</p>

                    @if($category->activeProducts->count())
                        <p class="text-sm text-gray-500 mb-4">
                            {{ __('Starting from') }}
                            <span class="text-lg font-bold text-gray-900">
                                {{ pricing(
                                    $category->activeProducts->first()->payment_type,
                                    $category->activeProducts->first()->pricing,
                                    'price'
                                ) }}
                            </span>
                            {{ pricing(
                                $category->activeProducts->first()->payment_type,
                                $category->activeProducts->first()->pricing,
                                null,
                                showText: true
                            ) }}
                        </p>
                    @endif

                    <a href="{{ route('products.category', $category->slug) }}"
                       class="inline-flex items-center text-sm font-medium
                              text-[{{ templateMeta('colors.primary', '#CC2628') }}]
                              hover:underline">
                        {{ __('View Plans') }} →
                    </a>
                </div>
            @endforeach
        </div>
    </div>
</section>
@endif
```

### Example: Domain Search Form

```blade
{{-- partials/domain_search_form.blade.php --}}
<form action="{{ route('domain.search') }}" method="POST" class="max-w-2xl mx-auto">
    @csrf
    <div class="flex rounded-xl overflow-hidden shadow-lg">
        <input type="text"
               name="domain"
               value="{{ old('domain') }}"
               placeholder="{{ __('Find your perfect domain...') }}"
               class="flex-1 px-6 py-4 text-lg border-0 focus:ring-0"
               required>
        <button type="submit"
                class="px-8 py-4 font-semibold text-white
                       bg-[{{ templateMeta('colors.primary', '#CC2628') }}]
                       hover:opacity-90 transition">
            {{ __('Search') }}
        </button>
    </div>
</form>
```

### Example: Testimonials Section with Alpine.js Carousel

```blade
{{-- sections/testimonials.blade.php --}}
@php
    $title = getContent('testimonials.content', singleQuery: true);
    $items = getContent('testimonials.element', singleQuery: false, limit: 9);
    $heading = $title?->data_values?->heading ?? __('What Our Customers Say');
@endphp

@if($items->count())
<section class="py-20">
    <div class="container mx-auto px-4">
        <h2 class="text-3xl font-bold text-center mb-12">{{ $heading }}</h2>

        <div x-data="{ active: 0, total: {{ $items->count() }} }"
             class="relative max-w-4xl mx-auto">

            @foreach($items as $i => $item)
                @php $t = $item->data_values; @endphp
                <div x-show="active === {{ $i }}"
                     x-transition:enter="transition ease-out duration-300"
                     x-transition:enter-start="opacity-0 translate-x-4"
                     x-transition:enter-end="opacity-100 translate-x-0"
                     class="text-center px-8">

                    <p class="text-xl text-gray-600 italic mb-6">
                        "{{ $t->review ?? '' }}"
                    </p>

                    <div class="flex items-center justify-center gap-3">
                        @if(!empty($t->avatar))
                            <img src="{{ getImage('assets/images/frontend/' . $t->avatar) }}"
                                 alt="{{ $t->name ?? '' }}"
                                 width="48" height="48"
                                 class="rounded-full">
                        @endif
                        <div class="text-left">
                            <p class="font-semibold">{{ $t->name ?? '' }}</p>
                            <p class="text-sm text-gray-500">{{ $t->designation ?? '' }}</p>
                        </div>
                    </div>
                </div>
            @endforeach

            {{-- Navigation dots --}}
            <div class="flex justify-center gap-2 mt-8">
                @foreach($items as $i => $item)
                    <button @click="active = {{ $i }}"
                            :class="active === {{ $i }}
                                ? 'bg-[{{ templateMeta('colors.primary', '#CC2628') }}] w-8'
                                : 'bg-gray-300 w-3'"
                            class="h-3 rounded-full transition-all duration-300">
                    </button>
                @endforeach
            </div>
        </div>
    </div>
</section>
@endif
```
