CLAUDE.mdjavascript

ultimate-jekyll-manager CLAUDE.md

Ultimate Jekyll Manager is a template framework that consuming projects install as an NPM module to build Jekyll sites quickly and efficiently. It provides best-practice configurations, default components, themes, and build tools.

View Source

Ultimate Jekyll Manager

Project Overview

Ultimate Jekyll Manager is a template framework that consuming projects install as an NPM module to build Jekyll sites quickly and efficiently. It provides best-practice configurations, default components, themes, and build tools.

Important: This is NOT a standalone project. You cannot run npm start or npm run build directly in this repository.

Project Structure

Directory Organization

  • src/gulp/tasks - Gulp tasks for building Jekyll sites
  • src/defaults/src - Default source files (editable by users, copied to consuming project's src/)
  • src/defaults/dist - Default distribution files (not editable by users, copied to consuming project's dist/)
  • src/assets/css - Stylesheets (global, pages, themes)
  • src/assets/js - JavaScript modules (core, pages, libraries)
  • src/assets/themes - Theme SCSS and JS files

Consuming Project Structure

  • src/ - Compiled to dist/ via npm
  • dist/ - Compiled to _site/ via Jekyll

Local Development

The local development server URL is stored in .temp/_config_browsersync.yml in the consuming project's root directory. Read this file to determine the correct URL for browsing and testing.

Asset Organization

Ultimate Jekyll Manager Files (THIS project)

CSS:

  • src/assets/css/ultimate-jekyll-manager.scss - Main UJ stylesheet (provides core styles)
  • src/assets/css/global/ - Global UJ styles
  • src/assets/css/pages/ - Page-specific styles provided by UJ
    • Format: src/assets/css/pages/[page-name]/index.scss
    • Example: src/assets/css/pages/download/index.scss

JavaScript:

  • src/assets/js/ultimate-jekyll-manager.js - Main UJ JavaScript entry point (provides core functionality)
  • src/assets/js/core/ - Core UJ modules
  • src/assets/js/pages/ - Page-specific JavaScript provided by UJ
    • Format: src/assets/js/pages/[page-name]/index.js
    • Example: src/assets/js/pages/download/index.js
  • src/assets/js/libs/ - UJ library modules (prerendered-icons, form-manager, authorized-fetch, etc.)

Default Pages & Layouts:

UJ provides default page templates and layouts in src/defaults/dist/ that are copied to consuming projects. These are NOT meant to be edited by users.

  • Format: src/defaults/dist/_layouts/themes/[theme-id]/frontend/pages/[page-name].html
  • Examples:
    • src/defaults/dist/_layouts/themes/classy/frontend/pages/download.html
    • src/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html
    • src/defaults/dist/_layouts/themes/classy/frontend/pages/payment/checkout.html
    • src/defaults/dist/_layouts/themes/classy/frontend/pages/payment/confirmation.html
    • src/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html
  • Core layouts:
    • src/defaults/dist/_layouts/core/root.html - Root HTML wrapper
    • src/defaults/dist/_layouts/themes/[theme-id]/frontend/core/base.html - Theme base layout

Complete UJ Page Example:

  • HTML: src/defaults/dist/_layouts/themes/classy/frontend/pages/download.html
  • CSS: src/assets/css/pages/download/index.scss
  • JS: src/assets/js/pages/download/index.js

These files serve as blueprints and reference implementations. When building custom pages in consuming projects, reference these for patterns and best practices.

IMPORTANT: Consuming projects CAN create files with the same paths in their own src/ directory to override UJ defaults, but this should ONLY be done when absolutely necessary. Prefer using src/pages/ and src/_layouts/ for custom pages instead of overriding UJ default files.

Section Configuration Files (JSON)

UJ provides JSON configuration files for common sections like navigation and footer. These JSON files are consumed by corresponding HTML templates during the build process.

Configuration Files:

  • src/defaults/src/_includes/frontend/sections/nav.json - Navigation configuration
  • src/defaults/src/_includes/frontend/sections/footer.json - Footer configuration

How It Works:

  1. JSON files contain structured data (links, labels, settings)
  2. HTML templates in src/defaults/dist/_includes/themes/[theme-id]/frontend/sections/ read and render this data
  3. The build process converts .json → data loaded by .html templates

Customizing Navigation/Footer:

Consuming projects should create their own JSON files in src/_includes/frontend/sections/:

  • src/_includes/frontend/sections/nav.json
  • src/_includes/frontend/sections/footer.json

Example: Footer Configuration

{
  logo: {
    href: '/',
    class: 'filter-adaptive',
    text: '{{ site.brand.name }}',
    description: '{{ site.meta.description }}',
  },
  links: [
    {
      label: 'Company',
      href: null,
      links: [
        {
          label: 'About Us',
          href: '/about',
        },
        {
          label: 'Pricing',
          href: '/pricing',
        },
      ],
    },
  ],
  socials: {
    enabled: true,
  },
  copyright: {
    enabled: true,
    text: null,
  },
}

Note: These are JSON5 files (support comments, trailing commas, unquoted keys). The corresponding HTML templates automatically process these files during the build.

Customizing Default Pages via Frontmatter

BEST PRACTICE: UJ default pages are designed to be customized through frontmatter WITHOUT writing any HTML. Consuming projects can create a simple page that includes ONLY frontmatter to configure the default page's behavior.

How It Works:

  1. UJ default pages use page.resolved to access merged frontmatter (site → layout → page)
  2. IMPORTANT: Before customizing, READ the UJ default page in src/defaults/dist/_layouts/ to understand available frontmatter options and how they're used
  3. Consuming projects create a page in src/pages/ with custom frontmatter
  4. The page uses a UJ layout (e.g., blueprint/pricing)
  5. Frontmatter overrides default values without any HTML

Example: Customizing the Pricing Page

Step 1: Read the UJ default pricing page to see available frontmatter options:

  • File: src/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html
  • Look for frontmatter at the top and how page.resolved.pricing is used in the HTML

Step 2: In consuming project, create src/pages/pricing.html:

---
### ALL PAGES ###
layout: blueprint/pricing
permalink: /pricing

### PAGE CONFIG ###
pricing:
  price_per_unit:
    enabled: true
    feature_id: "credits"
    label: "credit"
  plans:
    - id: "basic"
      name: "Basic"
      tagline: "best for getting started"
      url: "/download"
      pricing:
        monthly: 0
        annually: 0
      features:
        - id: "credits"
          name: "Credits"
          value: 1
          icon: "sparkles"
    ...
---

That's it! No HTML needed. The UJ pricing layout reads page.resolved.pricing and renders the plans accordingly.

When to Use Frontmatter Customization:

  • ✅ Customizing UJ default pages (pricing, contact, download, etc.)
  • ✅ Changing configuration without touching HTML
  • ✅ Maintaining upgradability when UJ updates

When to Create Custom Pages:

  • ❌ Building entirely new page types
  • ❌ Needing custom HTML structure
  • ❌ Pages with unique layouts not provided by UJ

Consuming Project Files

CSS:

  • src/assets/css/main.scss - Site-wide custom styles (runs on every page, edits by consuming project)
  • src/assets/css/pages/ - Page-specific custom styles
    • Format: src/assets/css/pages/[page-name]/index.scss

JavaScript:

  • src/assets/js/main.js - Site-wide custom JavaScript (runs on every page, edits by consuming project)
  • src/assets/js/pages/ - Page-specific custom JavaScript
    • Format: src/assets/js/pages/[page-name]/index.js

Pages & Layouts:

  • src/pages/ - Individual page HTML/Markdown files
  • src/_layouts/ - Custom layouts for the consuming project

Asset Loading: Page-specific CSS/JS files are automatically included based on the page's canonical path. Override with asset_path frontmatter.

Page Module Structure

All page modules must follow this standardized pattern:

/**
 * [Page Name] Page JavaScript
 */

// Libraries
let webManager = null;

// Module
export default (Manager) => {
  return new Promise(async function (resolve) {
    // Shortcuts
    webManager = Manager.webManager;

    // Initialize when DOM is ready
    await webManager.dom().ready();

    // Page initialization logic
    helper1();

    // Resolve after initialization
    return resolve();
  });
};

// Helper functions
function helper1() {
  // Helper implementation
}

Key Points:

  • Helpers are defined outside the main export function
  • Use webManager shortcuts for common operations
  • Always wait for DOM ready before manipulating elements

Layouts and Pages

Page Types

  • One-off pages (e.g., /categories, /sitemap) - Create as pages without custom layouts; use existing layouts
  • Repeating page types (e.g., blog posts, category pages) - Create a dedicated layout (e.g., _layouts/category.html)

Layout Requirements

All layouts and pages must eventually require a theme entry point:

layout: themes/[ site.theme.id ]/frontend/core/base

Note: The [ site.theme.id ] syntax is correct and allows dynamic theme selection.

Asset Path Configuration

For pages sharing the same assets, use the asset_path frontmatter variable:

---
# Instead of deriving path from page.canonical.path
asset_path: categories/category
---

Example:

  • One-off page: pages/categories.htmlsrc/assets/css/pages/categories/index.scss
  • Repeating layout: _layouts/category.htmlsrc/assets/css/pages/categories/category.scss (set asset_path: categories/category in layout frontmatter)

UJ Powertools (Jekyll Plugin)

Ultimate Jekyll uses the jekyll-uj-powertools gem for custom Liquid functionality.

Documentation: /Users/ian/Developer/Repositories/ITW-Creative-works/jekyll-uj-powertools/README.md

Available Features

  • Filters: uj_strip_ads, uj_json_escape, uj_title_case, uj_content_format
  • Tags: iftruthy, iffalsy, uj_icon, uj_logo, uj_image, uj_member, uj_post, uj_readtime, uj_social, uj_translation_url, uj_fake_comments, uj_language
  • Global Variables: site.uj.cache_breaker
  • Page Variables: page.random_id, page.extension, page.layout_data, page.resolved

Always check the README before assuming functionality.

Key Liquid Functions

uj_content_format

Formats content by first liquifying it, then markdownifying it (if markdown file).

iftruthy / iffalsy

Custom tags that check JavaScript truthiness (not null, undefined, or empty string).

{% iftruthy variable %}
  <!-- Content -->
{% endiftruthy %}

Limitations:

  • Does NOT support logical operators
  • Does NOT support else statements
  • CAN contain nested sub-statements

page.resolved

A deeply merged object containing all site, layout, and page variables. Precedence: page > layout > site. Enables a system of defaults with progressive overrides.

uj_icon

Inserts Font Awesome icons:

{% uj_icon icon-name, "fa-md" %}
{% uj_icon "rocket", "fa-3xl" %}

Parameters:

  1. Icon name (string or variable, without "fa-" prefix)
  2. CSS classes (optional, defaults to "fa-3xl")

Available Icon Sizes:

  • fa-2xs - Extra extra small
  • fa-xs - Extra small
  • fa-sm - Small
  • fa-md - Medium (default base size)
  • fa-lg - Large
  • fa-xl - Extra large
  • fa-2xl - 2x extra large
  • fa-3xl - 3x extra large
  • fa-4xl - 4x extra large
  • fa-5xl - 5x extra large

Size Examples:

{% uj_icon "check", "fa-sm" %}     <!-- Small inline icon -->
{% uj_icon "star", "fa-lg" %}      <!-- Slightly larger -->
{% uj_icon "rocket", "fa-2xl" %}   <!-- Hero/feature icons -->
{% uj_icon "chart-pie", "fa-4xl" %}<!-- Large placeholder icons -->

asset_path Override

Override default page-specific CSS/JS path derivation:

---
asset_path: blog/post
---

Uses /assets/css/pages/{{ asset_path }}.bundle.css instead of deriving from page.canonical.path. Useful when multiple pages share assets (e.g., all blog posts).

Icon System

Ultimate Jekyll uses Font Awesome icons but does NOT include the Font Awesome JavaScript or CSS library. All icons must be rendered server-side using Jekyll's {% uj_icon %} tag.

When to Use {% uj_icon %} vs Prerendered Icons

IMPORTANT: Use the correct method based on WHERE the icon will be used:

Use {% uj_icon %} in HTML/Liquid Templates

When icons are part of the static HTML template, use {% uj_icon %} directly:

<!-- Alerts -->
<div class="alert alert-success">
  {% uj_icon "circle-check", "fa-sm" %} Success message
</div>

<!-- Buttons -->
<button class="btn btn-primary">
  {% uj_icon "paper-plane", "fa-md me-2" %}
  Send
</button>

<!-- Labels -->
<label>
  {% uj_icon "envelope", "fa-sm me-1 text-info" %}
  Email
</label>

Use this when:

  • The icon is in a Jekyll template (.html file)
  • The icon is static and known at build time
  • The icon is part of the page structure

Use Prerendered Icons in JavaScript

When icons need to be dynamically inserted via JavaScript, pre-render them in frontmatter and access them via the library:

1. Add icons to page frontmatter:

---
prerender_icons:
  - name: "mobile"
    class: "fa-sm me-1"
  - name: "envelope"
    class: "fa-sm me-1"
  - name: "bell"
    class: "fa-sm me-1"
---

2. Import the library in JavaScript:

import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';

3. Use in your code:

const iconHTML = getPrerenderedIcon('mobile');
$badge.innerHTML = `${iconHTML}Push Notification`;

Use this when:

  • Icons are dynamically inserted via JavaScript
  • Icons are part of dynamically generated content
  • Icons are added to elements created with document.createElement()

What NOT to Do

NEVER use manual icon HTML in JavaScript:

// ❌ WRONG - Bootstrap Icons (we don't use Bootstrap Icons)
$el.innerHTML = '<i class="bi bi-check-circle"></i> Text';

// ❌ WRONG - Manual Font Awesome (we don't have FA JS/CSS)
$el.innerHTML = '<i class="fa-solid fa-check"></i> Text';

// ✅ CORRECT - Use prerendered icons
const iconHTML = getPrerenderedIcon('circle-check');
$el.innerHTML = `${iconHTML} Text`;

Benefits

  • Icons are rendered server-side with proper Font Awesome classes
  • No client-side icon generation overhead
  • Consistent icon styling across the application
  • No Font Awesome JavaScript/CSS library needed

CSS Guidelines

Theme-Adaptive Classes

DO NOT USE: bg-light, bg-dark, text-light, text-dark

Ultimate Jekyll supports both light and dark modes. Use adaptive classes instead:

Backgrounds:

  • bg-body - Primary background
  • bg-body-secondary - Secondary background
  • bg-body-tertiary - Tertiary background

Text:

  • text-body - Body text color

Buttons:

  • btn-adaptive - Adaptive button
  • btn-outline-adaptive - Adaptive outline button

These classes automatically adapt to the current theme mode.

Page Loading Protection System

Ultimate Jekyll prevents race conditions by disabling buttons during JavaScript initialization.

How It Works

  1. HTML element starts with data-page-loading="true" and aria-busy="true" (src/defaults/dist/_layouts/core/root.html)
  2. Protected elements are automatically disabled during this state
  3. Attributes are removed when JavaScript completes (src/assets/js/core/complete.js)

Protected Elements

  • All form buttons (<button>, <input type="submit">, <input type="button">, <input type="reset">)
  • Elements with .btn class (Bootstrap buttons)
  • Elements with .btn-action class (custom action triggers)

The .btn-action Class

Selectively protect non-standard elements that trigger important actions:

<!-- Protected during page load -->
<a href="/api/delete" class="custom-link btn-action">Delete Item</a>
<div class="card-action btn-action" onclick="processData()">Process</div>

<!-- NOT protected (regular navigation/UI) -->
<a href="/about" class="btn btn-primary">About Us</a>
<button data-bs-toggle="modal">Show Info</button>

Use .btn-action for:

  • API calls
  • Form submissions
  • Data modifications
  • Payment processing
  • Destructive actions

Don't use for:

  • Navigation links
  • UI toggles (modals, accordions, tabs)
  • Harmless interactions

Implementation

  • CSS: src/assets/css/core/utilities.scss - Disabled styling
  • Click Prevention: src/defaults/dist/_includes/core/body.html - Inline script
  • State Removal: src/assets/js/core/complete.js - Removes loading state

Lazy Loading System

Ultimate Jekyll uses a custom lazy loading system powered by web-manager.

Syntax

data-lazy="@type value"

Supported Types

@src - Lazy load src attribute

<img data-lazy="@src /assets/images/hero.jpg" alt="Hero">
<iframe data-lazy="@src https://example.com/embed"></iframe>

@srcset - Lazy load srcset attribute

<img data-lazy="@srcset /img/small.jpg 480w, /img/large.jpg 1024w">

@bg - Lazy load background images

<div data-lazy="@bg /assets/images/background.jpg"></div>

@class - Lazy add CSS classes

<div data-lazy="@class animation-fade-in">Content</div>

@html - Lazy inject HTML content

<div data-lazy="@html <p>Lazy loaded content</p>"></div>

@script - Lazy load external scripts

<div data-lazy='@script {"src": "https://example.com/widget.js", "attributes": {"async": true}}'></div>

Features

  • Automatic cache busting via buildTime
  • IntersectionObserver for performance (50px threshold)
  • Loading state CSS classes: lazy-loading, lazy-loaded, lazy-error
  • Intelligent handling of video/audio sources
  • Automatic DOM re-scanning for dynamic elements

Implementation: src/assets/js/core/lazy-loading.js

Appearance Switching System

Ultimate Jekyll supports dark/light/system theme switching with user preference persistence.

Supported Modes

  • dark - Force dark mode
  • light - Force light mode
  • system - Auto-detect from OS preference (prefers-color-scheme)

JavaScript API

// Get/set preference
webManager.uj().appearance.get();        // Returns 'dark', 'light', 'system', or null
webManager.uj().appearance.set('dark');  // Save and apply preference
webManager.uj().appearance.getResolved(); // Returns actual theme: 'dark' or 'light'

// Utilities
webManager.uj().appearance.toggle();     // Toggle between dark/light
webManager.uj().appearance.cycle();      // Cycle: dark → light → system → dark
webManager.uj().appearance.clear();      // Clear saved preference

HTML Data Attributes

<!-- Buttons to set appearance (auto-gets 'active' class) -->
<button data-appearance-set="light">Light</button>
<button data-appearance-set="dark">Dark</button>
<button data-appearance-set="system">System</button>

<!-- Display current mode as text -->
<span data-appearance-current></span>

<!-- Show/hide icons based on current mode -->
<span data-appearance-icon="light" hidden>☀️</span>
<span data-appearance-icon="dark" hidden>🌙</span>
<span data-appearance-icon="system" hidden>💻</span>

Dropdown Example

<div class="dropdown">
  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">
    <span data-appearance-icon="light" hidden>{% uj_icon "sun", "fa-md me-2" %}</span>
    <span data-appearance-icon="dark" hidden>{% uj_icon "moon-stars", "fa-md me-2" %}</span>
    <span data-appearance-icon="system" hidden>{% uj_icon "circle-half-stroke", "fa-md me-2" %}</span>
    <span data-appearance-current></span>
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#" data-appearance-set="light">Light</a></li>
    <li><a class="dropdown-item" href="#" data-appearance-set="dark">Dark</a></li>
    <li><a class="dropdown-item" href="#" data-appearance-set="system">System</a></li>
  </ul>
</div>

Implementation

  • Inline script: src/defaults/dist/_includes/core/body.html - Runs immediately to prevent flash
  • Module: src/assets/js/core/appearance.js - API and UI handling
  • Storage: Saved under _manager.appearance.preference in localStorage
  • Test page: /test/libraries/appearance

JavaScript Libraries

WebManager

Custom library for site management functionality.

Documentation: /Users/ian/Developer/Repositories/ITW-Creative-Works/web-manager/README.md

Available Utilities:

  • webManager.auth() - Authentication management
  • webManager.utilities() - Utility functions
  • webManager.sentry() - Error tracking
  • webManager.dom() - DOM manipulation

Important: Always check the source code or README before assuming a function exists. Do not guess at API methods.

Ultimate Jekyll Libraries

Ultimate Jekyll provides helper libraries in src/assets/js/libs/ that can be imported as needed.

Prerendered Icons Library

Provides access to icons defined in page frontmatter and rendered server-side.

Import:

import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';

Usage:

const iconHTML = getPrerenderedIcon('apple');

Reference: src/assets/js/libs/prerendered-icons.js

Authorized Fetch Library

Simplifies authenticated API requests by automatically adding Firebase authentication tokens via Authorization Bearer header.

Import:

import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';

Usage:

const response = await authorizedFetch(url, options);

Key Benefits:

  • No need to manually call webManager.auth().getIdToken()
  • Automatic token injection as Authorization Bearer header
  • Centralized authentication handling

⚠️ IMPORTANT: Auth State Requirement

authorizedFetch requires Firebase Auth to have determined the current user's authentication state before being called. On fresh page loads (e.g., OAuth callback pages, deep links), Firebase Auth needs time to restore the session from IndexedDB/localStorage.

If called before auth state is determined, it will throw: "No authenticated user found"

Solution: Wait for auth state before calling authorizedFetch:

// Wait for auth state to be determined (fires once auth is known)
webManager.auth().listen({ once: true }, async () => {
  // Now safe to use authorizedFetch
  const response = await authorizedFetch(url, options);
});

When this matters:

  • Pages that load and immediately need to make authenticated API calls
  • OAuth callback pages (user returns from external auth provider)
  • Deep links that require authenticated requests on load

When NOT needed:

  • User-triggered actions (button clicks, form submissions) - by then auth state is always determined
  • Pages that wait for user interaction before making API calls

Reference: src/assets/js/libs/authorized-fetch.js

FormManager Library

Lightweight form state management library with built-in validation, state machine, and event system.

Import:

import { FormManager } from '__main_assets__/js/libs/form-manager.js';

Basic Usage:

const formManager = new FormManager('#my-form', options);

formManager.on('submit', async ({ data, $submitButton }) => {
  const response = await fetch('/api', { body: JSON.stringify(data) });
  if (!response.ok) throw new Error('Failed');
  formManager.showSuccess('Form submitted!');
});

State Machine:

initializing → ready ⇄ submitting → ready (or submitted)

Configuration Options:

{
  autoReady: true,           // Auto-transition to initialState when DOM ready
  initialState: 'ready',     // State after autoReady fires
  allowResubmit: true,       // Allow resubmission after success (false = 'submitted' state)
  resetOnSuccess: false,     // Clear form fields after successful submission
  warnOnUnsavedChanges: false, // Warn user before leaving with unsaved changes
  submittingText: 'Processing...', // Text shown on submit button during submission
  submittedText: 'Processed!', // Text shown on submit button after success (when allowResubmit: false)
  inputGroup: null           // Filter getData() by data-input-group attribute (null = all fields)
}

Events:

| Event | Payload | Description | |-------|---------|-------------| | submit | { data, $submitButton } | Form submission (throw error to show failure) | | validation | { data, setError } | Custom validation before submit | | change | { field, name, value, data } | Field value changed | | statechange | { state, previousState } | State transition | | honeypot | { data } | Honeypot triggered (for spam tracking) |

Validation System:

FormManager runs validation automatically before submit:

  1. HTML5 validation - Checks required, minlength, maxlength, min, max, pattern, type="email", type="url"
  2. Custom validation - Use validation event for business logic
fm.on('validation', ({ data, setError }) => {
  if (data.age && parseInt(data.age) < 18) {
    setError('age', 'You must be 18 or older');
  }
});

Errors display with Bootstrap's is-invalid class and .invalid-feedback elements.

Autofocus:

When the form transitions to ready state, FormManager automatically focuses the field with the autofocus attribute (if present and not disabled).

Methods:

| Method | Description | |--------|-------------| | on(event, callback) | Register event listener (chainable) | | ready() | Transition to ready state | | getData() | Get form data as nested object (supports dot notation, respects input group filter) | | setData(obj) | Set form values from nested object | | setInputGroup(group) | Set input group filter (string, array, or null) | | getInputGroup() | Get current input group filter | | showSuccess(msg) | Show success notification | | showError(msg) | Show error notification | | reset() | Reset form and go to ready state | | isDirty() | Check if form has unsaved changes | | setDirty(bool) | Set dirty state | | clearFieldErrors() | Clear all field validation errors | | throwFieldErrors({ field: msg }) | Set and display field errors, throw error |

Nested Field Names (Dot Notation):

Use dot notation in field names for nested data:

<input name="user.address.city" value="NYC">

Results in:

{ user: { address: { city: 'NYC' } } }

Input Groups:

Filter getData() to only return fields matching a specific group. Fields without data-input-group are "global" and always included.

<!-- Global fields (no data-input-group) - always included -->
<input name="settings.theme" value="dark">

<!-- Group-specific fields -->
<input name="options.url" data-input-group="url" value="https://example.com">
<input name="options.ssid" data-input-group="wifi" value="MyWiFi">
<input name="options.password" data-input-group="wifi" value="secret123">
// Set group filter (accepts string or array)
formManager.setInputGroup('url');           // Single group
formManager.setInputGroup(['url', 'wifi']); // Multiple groups
formManager.setInputGroup(null);            // Clear filter (all fields)

// Get current filter
formManager.getInputGroup(); // Returns ['url'] or null

// getData() respects the filter
formManager.setInputGroup('wifi');
formManager.getData();
// Returns: { settings: { theme: 'dark' }, options: { ssid: 'MyWiFi', password: 'secret123' } }
// Note: 'url' field excluded, global 'settings.theme' included

Can also be set via config:

const fm = new FormManager('#form', { inputGroup: 'wifi' });

Honeypot (Bot Detection):

FormManager automatically rejects submissions if a honeypot field is filled. Honeypot fields are hidden from users but bots fill them automatically.

<!-- Hidden from users via CSS -->
<input type="text" name="honey" autocomplete="off" tabindex="-1"
       style="position: absolute; left: -9999px;" aria-hidden="true">

Fields matching [data-honey] or [name="honey"] are:

  • Excluded from getData() output
  • Checked during validation — if filled, submission is rejected with generic error

Checkbox Handling:

  • Single checkbox: Returns true/false
  • Checkbox group (same name): Returns object { value1: true, value2: false }

Multiple Submit Buttons:

Access the clicked button via $submitButton:

<button type="submit" data-action="save">Save</button>
<button type="submit" data-action="draft">Save Draft</button>
fm.on('submit', async ({ data, $submitButton }) => {
  const action = $submitButton?.dataset?.action; // 'save' or 'draft'
});

Reference: src/assets/js/libs/form-manager.js Test Page: src/assets/js/pages/test/libraries/form-manager/index.js Example: src/assets/js/pages/contact/index.js

Analytics & Tracking

Ultimate Jekyll uses three tracking platforms: Google Analytics (gtag), Facebook Pixel (fbq), and TikTok Pixel (ttq).

ITM (Internal Tracking Medium)

Internal tracking system modeled after UTM for cross-property user journey tracking.

| Parameter | Purpose | Examples | |-----------|---------|----------| | itm_source | Platform/origin | website, browser-extension, app, email | | itm_medium | Delivery mechanism | modal, prompt, banner, tooltip | | itm_campaign | Specific campaign/feature | exit-popup, premium-unlock, newsletter-signup | | itm_content | Specific context | Page path, feature ID, variant |

Examples:

# Website exit popup
?itm_source=website&itm_medium=modal&itm_campaign=exit-popup&itm_content=/pricing

# Extension premium unlock
?itm_source=browser-extension&itm_medium=prompt&itm_campaign=premium-unlock&itm_content=bulk-export

Tracking Guidelines

IMPORTANT Rules:

  • Track important user events with gtag(), fbq(), and ttq() functions
  • NEVER add conditional checks for tracking functions (e.g., if (typeof gtag !== 'undefined'))
  • Always assume tracking functions exist - they're globally available or stubbed
  • Reference standard events documentation before implementing custom tracking

Standard Events Documentation:

  • Google Analytics GA4: https://developers.google.com/analytics/devguides/collection/ga4/reference/events
  • Facebook Pixel: https://www.facebook.com/business/help/402791146561655?id=1205376682832142
  • TikTok Pixel: https://ads.tiktok.com/help/article/standard-events-parameters?redirected=2

Platform-Specific Requirements

TikTok Pixel Requirements

TikTok has strict validation requirements:

Required Parameters:

  • content_id - MUST be included in all events

Valid Content Types:

  • "product"
  • "product_group"
  • "destination"
  • "hotel"
  • "flight"
  • "vehicle"

Any other content type will generate a validation error.

Example:

// ✅ CORRECT
ttq.track('ViewContent', {
  content_id: 'product-123',
  content_type: 'product'
});

// ❌ WRONG - Missing content_id
ttq.track('ViewContent', {
  content_type: 'product'
});

// ❌ WRONG - Invalid content_type
ttq.track('ViewContent', {
  content_id: 'product-123',
  content_type: 'custom'  // Not in approved list
});

Tracking Implementation

IMPORTANT: Always track events to ALL THREE platforms in this order:

  1. Google Analytics (gtag)
  2. Facebook Pixel (fbq)
  3. TikTok Pixel (ttq)

Track events directly without existence checks. All three tracking calls should be made together for every event.

Development Mode: In development mode, all tracking calls are intercepted and logged to the console for debugging. See src/assets/js/libs/dev.js for implementation.

HTML Element Attributes

The <html> element has data attributes for JavaScript/CSS targeting:

| Attribute | Values | |-----------|--------| | data-theme-id | Theme ID (e.g., classy) | | data-theme-target | frontend, backend, docs | | data-bs-theme | light, dark | | data-page-path | Page permalink (e.g., /about) | | data-asset-path | Custom asset path or empty | | data-environment | development, production | | data-platform | windows, mac, linux, ios, android, chromeos, unknown | | data-browser | chrome, firefox, safari, edge, opera, brave | | data-device | mobile (<768px), tablet (768-1199px), desktop (>=1200px) | | data-runtime | web, browser-extension, electron, node | | aria-busy | true (loading), false (ready) |

Detection source: web-manager/src/modules/utilities.js

Audit Workflow

When fixing issues identified by the audit task (src/gulp/tasks/audit.js):

  1. Review the audit file location provided
  2. Create a TODO list for each audit category
  3. Read the ENTIRE audit file and plan fixes for each category
  4. Tackle issues incrementally - DO NOT attempt to fix everything at once
  5. Work through one category at a time

Remember: Audit files are large. Systematic, incremental fixes prevent errors and ensure thoroughness.