Utility Trigger

Trigger

Declarative click-action triggers. Add data-wf-trigger to any clickable element to wire up common page behaviors without writing JavaScript. Thirteen actions are built in: show , hide , toggle , scroll-to , scroll-top , toggle-password , generate-password , copy , toggle-theme , set-localstorage , set-cookie , load-more , and multi-element . Show/hide actions preserve the element’s original CSS display value (flex, grid, inline-flex…) so toggling never clobbers it with display:block .

Show / Hide / Toggle

Point data-wf-target at a CSS selector to show, hide, or toggle the visibility of any element. Use data-wf-hide-self to hide the trigger after it fires, and data-wf-show-self to reveal a partner element — useful for paired open/close buttons. Add data-wf-animation-in or data-wf-animation-out with any wf-ani-* class to animate the transition.

Toggle

Paired Show / Hide

This panel is toggled by the button above. It starts visible.

This panel was hidden. It's now visible, and the button swapped itself out for a "Hide" button.

Toggle
<button data-wf-trigger="toggle"
        data-wf-target="#panel"
        data-wf-animation-in="wf-ani-fade-in">Toggle</button>

<div id="panel">Content</div>
Paired Show / Hide
<!-- Open button hides itself and shows the close button -->
<button id="open-btn"
        data-wf-trigger="show"
        data-wf-target="#panel"
        data-wf-hide-self
        data-wf-show-self="#close-btn"
        data-wf-animation-in="wf-ani-fade-in">Open</button>

<!-- Close button hides itself and shows the open button -->
<button id="close-btn"
        data-wf-trigger="hide"
        data-wf-target="#panel"
        data-wf-hide-self
        data-wf-show-self="#open-btn"
        class="wf-hidden">Close</button>

<div id="panel">Content</div>

Scroll To & Top

scroll-to smoothly scrolls to any element on the page. Use data-wf-offset to subtract the height of a sticky header so the target is not obscured. Set data-wf-behavior="auto" for instant (no animation) scrolling. The component automatically detects whether to scroll window or a scrollable ancestor container. scroll-top requires no target and scrolls back to the very top.

Smooth

Instant

HTML
<!-- Scroll to a section, offset by 72 px (e.g. sticky navbar height) -->
<button data-wf-trigger="scroll-to"
        data-wf-target="#intro"
        data-wf-offset="72">Jump to Intro</button>

<!-- Instant scroll (no animation) -->
<button data-wf-trigger="scroll-to"
        data-wf-target="#section"
        data-wf-behavior="auto">Go to section</button>

<!-- Back to top -->
<button data-wf-trigger="scroll-top">↑ Back to top</button>

Password Toggle

Add data-wf-trigger="toggle-password" to a button inside a wf-input-group . Point data-wf-target at the password input. The component swaps the input type between password and text , toggles the button icon between icon view and icon view slash , updates aria-pressed , and updates any [data-wf-toggle-text] label span.

HTML
<label class="wf-label" for="pwd">Password</label>
<div class="wf-input-group">
  <input type="password" id="pwd" class="wf-input" autocomplete="current-password">
  <button class="wf-btn wf-btn-outline" type="button"
          data-wf-trigger="toggle-password"
          data-wf-target="#pwd"
          aria-label="Show password">
    <i class="icon eye" aria-hidden="true"></i>
  </button>
</div>

Generate Password

Add data-wf-trigger="generate-password" to a button and point data-wf-target at a password <input> . On click the component fills the input with a cryptographically secure random password (via crypto.getRandomValues() ), dispatches input and change events so reactive form libraries stay in sync, and emits wf:generate-password . Password length defaults to 16 and all character sets (uppercase, lowercase, numbers) are on by default. Symbols are opt-in via data-wf-password-symbols . At least one character from every enabled set is always guaranteed before the rest is filled, and the result is Fisher–Yates shuffled to remove positional bias.

HTML
<!-- Input paired with a toggle-password and a generate button -->
<div class="wf-input-group">
  <input type="password" id="new-pwd" class="wf-input" autocomplete="new-password">
  <button type="button"
          data-wf-trigger="toggle-password"
          data-wf-target="#new-pwd"
          aria-label="Show password">
    <i class="icon view" aria-hidden="true"></i>
  </button>
</div>

<!-- Default: 16 chars, upper + lower + numbers, no symbols -->
<button type="button"
        data-wf-trigger="generate-password"
        data-wf-target="#new-pwd">Generate</button>

<!-- 24 chars with symbols -->
<button type="button"
        data-wf-trigger="generate-password"
        data-wf-target="#new-pwd"
        data-wf-password-length="24"
        data-wf-password-symbols="true"
        data-wf-password-symbol-set="!@#$%^&*()">Generate strong</button>

<!-- Numbers only (e.g. a PIN) -->
<button type="button"
        data-wf-trigger="generate-password"
        data-wf-target="#pin-input"
        data-wf-password-length="6"
        data-wf-password-upper="false"
        data-wf-password-lower="false">Generate PIN</button>

Copy to Clipboard

Add data-wf-trigger="copy" to a button and point data-wf-target at any input, textarea, or element. On click the component copies the element’s value (inputs and textareas) or textContent (any other element) to the clipboard using the native navigator.clipboard API. After a successful copy the trigger receives the class is-copied for data-wf-copy-success-duration milliseconds (default 2 000) — use it to swap icons or show a “Copied!” label via CSS. Requires HTTPS or localhost.

HTML
<!-- Copy from a readonly input (e.g. API key) -->
<div class="wf-input-wrap wf-has-action">
  <input type="text" id="api-key" class="wf-input" value="sk-abc123" readonly>
  <button type="button"
          class="wf-btn wf-btn-outline"
          data-wf-trigger="copy"
          data-wf-target="#api-key"
          aria-label="Copy to clipboard">
    <i class="icon copy" aria-hidden="true"></i>
    <i class="icon check" aria-hidden="true"></i>
    <span>Copy</span>
  </button>
</div>

<!-- Copy from any element's text content -->
<code id="snippet">npm install wojo-framework</code>
<button type="button"
        data-wf-trigger="copy"
        data-wf-target="#snippet">Copy</button>

Theme Toggle

Reads and writes the data-theme attribute on <html> and persists the choice in localStorage . Omit data-wf-theme-mode to cycle between the current effective theme (light ↔ dark). Set it to light , dark , or system to jump to a specific mode directly. Any element with data-theme-mode (toolbar toggle buttons) or data-theme-menu-option (dropdown items) is automatically kept in sync.

Cycles light → dark → light
HTML
<!-- Cycle button — flips between current light/dark theme -->
<button data-wf-trigger="toggle-theme"
        data-wf-theme-text-dark="Dark"
        data-wf-theme-text-light="Light">
    <i class="icon sun"></i>
    <span data-wf-theme-text></span>
</button>

<!-- Explicit-mode buttons (toolbar style, auto-synced) -->
<button data-wf-trigger="toggle-theme" data-wf-theme-mode="light">Light</button>
<button data-wf-trigger="toggle-theme" data-wf-theme-mode="dark">Dark</button>
<button data-wf-trigger="toggle-theme" data-wf-theme-mode="system">System</button>

<!-- Standalone sync targets (no trigger required) -->
<button data-theme-mode="light" aria-pressed="false">Light</button>
<button data-theme-mode="dark"  aria-pressed="false">Dark</button>

localStorage & Cookie

Write a key/value to localStorage or a browser cookie on click — useful for persisting user preferences without a round trip to the server. For localStorage use data-wf-storage-key / data-wf-storage-value . For cookies use data-wf-cookie-name / data-wf-cookie-value . Cookie expiry is controlled by data-wf-cookie-days (default: 365). Add the boolean data-wf-storage-remove-empty or data-wf-cookie-remove-empty to delete the entry when the value is an empty string.

HTML
<!-- Write to localStorage -->
<button data-wf-trigger="set-localstorage"
        data-wf-storage-key="sidebar-collapsed"
        data-wf-storage-value="true">Collapse sidebar</button>

<!-- Remove from localStorage when value is empty -->
<button data-wf-trigger="set-localstorage"
        data-wf-storage-key="sidebar-collapsed"
        data-wf-storage-value=""
        data-wf-storage-remove-empty>Reset sidebar</button>

<!-- Write a cookie (expires in 7 days) -->
<button data-wf-trigger="set-cookie"
        data-wf-cookie-name="accepted-cookies"
        data-wf-cookie-value="true"
        data-wf-cookie-days="7">Accept cookies</button>

Load More

AJAX pagination without writing JavaScript. The component fetches data-wf-url with limit and offset query parameters appended. Results are appended to the element matched by data-wf-parent and the offset is updated automatically. The endpoint can return plain HTML or a JSON object { html, hasMore, nextOffset } . When hasMore is false the button is disabled and marked aria-disabled . The number of records per request is set with data-wf-records (default: 10).

Load more demo record #1
Load more demo record #2
Load more demo record #3
HTML
<!-- List that gets new items appended -->
<ul id="post-list">
    <li>Post 1</li>
    <li>Post 2</li>
    <li>Post 3</li>
</ul>

<button data-wf-trigger="load-more"
        data-wf-url="/api/posts"
        data-wf-parent="#post-list"
        data-wf-records="5">Load more posts</button>
JSON Response (optional)
{
    "html": "<li>Post 4</li><li>Post 5</li>",
    "hasMore": true,
    "nextOffset": 8
}

Multi-element Switcher

Use data-wf-trigger="multi-element" to build plan toggles, tab panels, and any pattern that shows one group of elements while hiding all peer groups. Set data-wf-target to a data-attribute name; every element carrying that attribute is shown, and all others in the same group are hidden. Group buttons with data-wf-group , or use <input type="radio"> elements grouped by their shared name attribute.

Starter

For individuals

$9 /mo
$7 /mo

$84 billed annually

  • 5 projects
  • 10 GB storage
  • Email support

Pro

Popular

For growing teams

$29 /mo
$23 /mo

$276 billed annually

  • Unlimited projects
  • 100 GB storage
  • Priority support

Business

For large organizations

$79 /mo
$63 /mo

$756 billed annually

  • Unlimited projects
  • 1 TB storage
  • Dedicated support
HTML
<!-- Switcher — radio toggle (auto-grouped by name, no data-wf-group needed) -->
<!-- Add data-wf-crossfade to enable smooth opacity crossfade via .wf-swap -->
<div class="wf-toggle-slide wf-toggle-slide-pill">
    <input type="radio" id="plan-m" name="plan" checked
           data-wf-trigger="multi-element"
           data-wf-target="data-plan-monthly"
           data-wf-crossfade>
    <label for="plan-m">Monthly</label>
    <input type="radio" id="plan-y" name="plan"
           data-wf-trigger="multi-element"
           data-wf-target="data-plan-yearly"
           data-wf-crossfade>
    <label for="plan-y">Annually</label>
</div>

<!-- Card grid — wrap each pair in .wf-swap for layout-shift-free crossfade -->
<div class="wf-grid wf-grid-3 wf-gap-4">
    <div class="wf-card">
        <div class="wf-card-body">
            <div class="wf-swap">
                <div data-plan-monthly>
                    <span>$9</span><span> /mo</span>
                </div>
                <div data-plan-yearly class="wf-hidden">
                    <span>$7</span><span> /mo</span>
                    <p>$84 billed annually</p>
                </div>
            </div>
        </div>
    </div>
    <!-- repeat for each tier -->
</div>

<!-- Button group alternative (explicit data-wf-group required) -->
<div class="wf-btn-group">
    <button class="wf-btn wf-btn-primary wf-btn-sm"
            data-wf-trigger="multi-element"
            data-wf-group="pricing-switcher"
            data-wf-target="data-plan-monthly"
            data-wf-crossfade>Monthly</button>
    <button class="wf-btn wf-btn-outlined wf-btn-sm"
            data-wf-trigger="multi-element"
            data-wf-group="pricing-switcher"
            data-wf-target="data-plan-yearly"
            data-wf-crossfade>Annually</button>
</div>

Animations

Add data-wf-animation-in to any show or toggle trigger to animate the target element when it becomes visible. Use data-wf-animation-out to animate it as it hides. Set data-wf-duration to an empty string or "" to disable animation entirely. Control the speed with data-wf-duration in seconds (default: 0.3 ). Animation classes come from wf-transitions.css .

HTML
<!-- Fade in on show, fade out on hide -->
<button data-wf-trigger="toggle"
        data-wf-target="#panel"
        data-wf-animation-in="wf-ani-fade-in"
        data-wf-animation-out="wf-ani-fade-out"
        data-wf-duration="0.25">Toggle</button>

<!-- No animation -->
<button data-wf-trigger="toggle"
        data-wf-target="#panel2"
        data-wf-animation-in=""
        data-wf-animation-out="">Toggle (instant)</button>
Class Description
wf-ani-fade-in Default in-animation. Fade from transparent to opaque.
wf-ani-fade-out Default out-animation. Fade from opaque to transparent.
wf-ani-fade-in-up Fade in while sliding up.
wf-ani-fade-in-down Fade in while sliding down.
wf-ani-slide-down Slide in from above.
wf-ani-slide-up Slide in from below.
wf-ani-zoom-in Scale from 0.8 to 1.
wf-ani-bounce-in Spring/bounce entrance.

Data Attributes

Attribute Applies to Description
data-wf-trigger All triggers Required. Action name: show , hide , toggle , scroll-to , scroll-top , toggle-password , generate-password , copy , toggle-theme , set-localstorage , set-cookie , load-more , multi-element
data-wf-target show, hide, toggle, scroll-to, toggle-password, generate-password, copy CSS selector of the target element. For multi-element , pass a data-attribute name instead (e.g. data-plan-monthly ) — all elements carrying that attribute are shown.
data-wf-group multi-element Groups button-based multi-element triggers so only one target is visible at a time. Omit when using <input type="radio"> (the shared name attribute serves as the group).
data-wf-hide-self show, hide, toggle Boolean. Hide the trigger element after firing.
data-wf-show-self show, hide, toggle CSS selector of a partner element to reveal after firing. Typically, a paired open/close button.
data-wf-animation-in show, hide, toggle Animation class applied when the target becomes visible (default: wf-ani-fade-in ). Set "" to disable.
data-wf-animation-out show, hide, toggle Animation class applied when the target hides (default: wf-ani-fade-out ). Set "" to disable.
data-wf-duration show, hide, toggle Animation duration in seconds (default: 0.3 ). Sets the --wf-anim-duration CSS variable.
data-wf-offset scroll-to Pixels subtracted from the scroll position. Use to account for a sticky header (default: 0 ).
data-wf-behavior scroll-to Scroll behaviour: smooth (default) or auto (instant).
data-wf-theme-mode toggle-theme Explicit mode to set: light , dark , or system . Omit to cycle between the current effective theme.
data-wf-theme-text-dark toggle-theme Label text to show inside the button when dark theme is active (default: Dark ).
data-wf-theme-text-light toggle-theme Label text to show inside the button when light theme is active (default: Light ).
data-wf-theme-storage-key toggle-theme localStorage key used to persist the effective theme (default: wf-theme ).
data-wf-theme-mode-storage-key toggle-theme localStorage key used to persist the mode (default: wf-theme-mode ).
data-wf-storage-key set-localstorage localStorage key to write. Required.
data-wf-storage-value set-localstorage Value to write to localStorage.
data-wf-storage-remove-empty set-localstorage Boolean. Remove the key instead of writing when the value is an empty string.
data-wf-cookie-name set-cookie Cookie name to write. Required.
data-wf-cookie-value set-cookie Value to write to the cookie.
data-wf-cookie-days set-cookie Cookie expiry in days (default: 365 ).
data-wf-cookie-remove-empty set-cookie Boolean. Expire (delete) the cookie when the value is an empty string.
data-wf-url load-more Endpoint URL for the AJAX request. Required.
data-wf-parent load-more CSS selector of the container that receives appended HTML. Required.
data-wf-records load-more Number of records per page sent as the limit parameter (default: 10 ).
data-theme-mode Any element Synced by toggle-themearia-pressed is set to true on the element whose value matches the active mode.
data-theme-menu-option Any element Synced by toggle-themearia-checked and wf-active are set on the matching element.
data-wf-theme-text Any element Text content is replaced with the current theme/mode name by toggle-theme .
data-wf-toggle-text Any element inside the trigger Text content is updated by toggle-password when the type toggles.
data-wf-password-length generate-password Number of characters in the generated password (default: 16 , minimum: 4 ).
data-wf-password-upper generate-password Include uppercase letters A–Z (default: true ). Set to false to exclude.
data-wf-password-lower generate-password Include lowercase letters a–z (default: true ). Set to false to exclude.
data-wf-password-numbers generate-password Include digits 0–9 (default: true ). Set to false to exclude.
data-wf-password-symbols generate-password Include special characters from the symbol set (default: false ). Set to true to enable.
data-wf-password-symbol-set generate-password Characters used when symbols are enabled (default: !@#$%^&*() ). Replace to restrict or expand the symbol pool.
data-wf-copy-success-duration copy Milliseconds the is-copied class stays on the trigger after a successful copy (default: 2000 ). Set to 0 to skip the class entirely.
Show More

Programmatic API

All triggers are auto-initialized on DOMContentLoaded . Retrieve an instance via the _wfTrigger property on the element, or import and construct WojoTrigger directly. The exported helpers triggerShow() and triggerHide() let you show or hide any element from JavaScript without needing a trigger element at all.

JavaScript
import { WojoTrigger, triggerShow, triggerHide } from './wf-trigger.js';

// Retrieve the auto-initiated instance and fire programmatically
const btn = document.querySelector('[data-wf-trigger]');
const trigger = btn._wfTrigger;
trigger.fire();       // execute the configured action
trigger.destroy();    // remove the click listener

// Construct manually (opt-out of auto-init with a custom element)
const t = new WojoTrigger(btn, { trigger: 'toggle', target: '#panel' }).init();

// Standalone helpers — no trigger element required
triggerShow(document.querySelector('#panel'));
triggerHide(document.querySelector('#panel'));
Method / Property Description
new WojoTrigger(el[, opts]) Create an instance. Options map directly to data attribute names in camelCase (e.g. animationIn ).
instance.init() Attach the click listener and return the instance. Chainable.
instance.fire() Programmatically execute the configured action.
instance.destroy() Remove the click listener and delete el._wfTrigger .
el._wfTrigger Reference to the instance stored on the element by init() .
triggerShow(el[, opts]) Show an element, restoring its original display value (flex, grid, inline-flex…). Optionally accepts animation options.
triggerHide(el[, opts, onDone]) Hide an element, caching its current display value for later restore. Calls onDone after the out-animation finishes.

Events

All events bubble from the trigger element and can be listened to on any ancestor, including document . The detail payload is described below.

JavaScript
document.addEventListener('wf:show', e => {
    console.log('shown', e.detail.target);
});

document.addEventListener('wf:theme-change', e => {
    console.log('theme:', e.detail.theme, 'mode:', e.detail.mode, 'dark?', e.detail.isDark);
});

document.addEventListener('wf:load-more', e => {
    console.log('loaded, hasMore:', e.detail.hasMore, 'nextOffset:', e.detail.nextOffset);
});
Event Detail payload
wf:show { target } — the element that was shown.
wf:hide { target } — the element that was hidden.
wf:scroll-to { target, offset } — scroll-to action started.
wf:scroll-top No detail payload.
wf:toggle-password { target, visible }visible: true when password is shown as plain text.
wf:theme-change { theme, mode, isDark }theme is the effective value ( light / dark ), mode includes system .
wf:localstorage-set { key, value, removed }removed: true when the key was deleted.
wf:cookie-set { name, value, removed, days }removed: true when the cookie was expired.
wf:load-more { parent, appended, limit, offset, nextOffset, hasMore, response }
wf:load-more-error { error, parent, limit, offset }
wf:generate-password { target, password }target is the input element that was filled; password is the generated string.
wf:copy { target, text } — fired after a successful clipboard write. text is the string that was copied.
wf:copy-error { target, text, error } — fired when the clipboard write fails (e.g. non-HTTPS context or permission denied).
wf:multi-element-change { targetAttr } — name of the data attribute whose elements were just made visible (e.g. data-plan-monthly ). Fires on the trigger element and bubbles.