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.
<!-- 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.
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.
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>
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.
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-theme —
aria-pressed
is set to
true
on the element whose value matches the active mode.
data-theme-menu-option
Any element
Synced by
toggle-theme —
aria-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.