meno-interactions
v0.1.0
Published
Attribute-driven JavaScript interactions for Meno and static no-code web projects.
Maintainers
Readme
Meno Interactions
Attribute-driven JavaScript interactions for Meno projects and static no-code sites.
Meno already supports component JavaScript with vanilla DOM code scoped to the component root, and it supports interactive styles through state classes such as .is-open or .is-scrolled. This package gives no-code builders a reusable script they can add once, then control behavior with custom attributes in the Meno Attributes panel.
Install
npm install meno-interactionsOr use the browser files from dist/ in a Meno project:
{
"meta": {
"libraries": {
"js": [{ "url": "/libraries/meno-interactions.umd.js", "mode": "defer" }],
"css": [{ "url": "/libraries/meno-interactions.css" }]
}
}
}CDN Setup
After this package is published to npm, you can load it from a CDN with one CSS file and one deferred script.
Option 1: jsDelivr
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/meno-interactions/dist/meno-interactions.css">
<script defer src="https://cdn.jsdelivr.net/npm/meno-interactions/dist/meno-interactions.umd.js"></script>Option 2: unpkg
<link rel="stylesheet" href="https://unpkg.com/meno-interactions/dist/meno-interactions.css">
<script defer src="https://unpkg.com/meno-interactions/dist/meno-interactions.umd.js"></script>Option 3: Version-pin the CDN
Pinning a version prevents future package updates from changing an already-launched site.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/meno-interactions.css">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/meno-interactions.umd.js"></script>Add CDN Files in Meno
In Meno, add the CDN URLs as page or project libraries. If editing JSON directly, put them in meta.libraries:
{
"meta": {
"libraries": {
"css": [
{ "url": "https://cdn.jsdelivr.net/npm/[email protected]/dist/meno-interactions.css" }
],
"js": [
{ "url": "https://cdn.jsdelivr.net/npm/[email protected]/dist/meno-interactions.umd.js", "mode": "defer" }
]
}
}
}The script auto-initializes on page load. You do not need to write JavaScript for normal use.
Self-hosted CDN-style Setup
If you do not want third-party CDN URLs, copy these files into your Meno project's /libraries/ folder:
dist/meno-interactions.umd.jsdist/meno-interactions.css
Then load them like this:
{
"meta": {
"libraries": {
"css": [{ "url": "/libraries/meno-interactions.css" }],
"js": [{ "url": "/libraries/meno-interactions.umd.js", "mode": "defer" }]
}
}
}Quick Use
Add attributes in Meno's Attributes panel. For example, an accordion only needs a wrapper and trigger attributes:
<div data-meno-accordion data-meno-single="true">
<button data-meno-accordion-trigger>Question</button>
<div>Answer</div>
</div>In Meno JSON, the same idea is represented through attributes:
{
"type": "node",
"tag": "div",
"attributes": { "data-meno-accordion": "", "data-meno-single": "true" },
"children": [
{
"type": "node",
"tag": "button",
"attributes": { "data-meno-accordion-trigger": "" },
"children": "Question"
},
{ "type": "node", "tag": "div", "children": "Answer" }
]
}Attribute Reference and Examples
Disclosure / Simple Toggle
Use this for one button that opens or closes one nearby panel.
<button data-meno-toggle data-meno-target="#details">Show details</button>
<div id="details" hidden>
Extra content goes here.
</div>Attributes:
data-meno-toggle: marks the clickable trigger.data-meno-target="#details": optional selector for the panel. If omitted, the next sibling is used.
State classes and attributes:
- The panel receives
.is-openwhen open. - The trigger receives
aria-expanded="true"oraria-expanded="false". - The panel
hiddenattribute is toggled.
Accordion
Use this for FAQ rows or any stacked expandable content.
<div data-meno-accordion data-meno-single="true">
<button data-meno-accordion-trigger>What is included?</button>
<div hidden>Everything needed for the starter setup.</div>
<button data-meno-accordion-trigger>Can I open many rows?</button>
<div hidden>Set data-meno-single="false" on the wrapper.</div>
</div>Attributes:
data-meno-accordion: marks the accordion wrapper.data-meno-accordion-trigger: marks each clickable row header.data-meno-single="true": only one panel can be open at a time.data-meno-single="false": multiple panels can stay open.
Panel rule:
- Each trigger controls its next sibling unless the trigger has
aria-controls="panel-id".
Tabs
Use this for switching between multiple panels.
<div data-meno-tabs data-meno-initial-tab="overview">
<div role="tablist">
<button data-meno-tab="overview">Overview</button>
<button data-meno-tab="specs">Specs</button>
<button data-meno-tab="faq">FAQ</button>
</div>
<section data-meno-tab-panel="overview">Overview content.</section>
<section data-meno-tab-panel="specs">Specs content.</section>
<section data-meno-tab-panel="faq">FAQ content.</section>
</div>Attributes:
data-meno-tabs: marks the tab set wrapper.data-meno-tab="id": marks a tab button.data-meno-tab-panel="id": marks the matching panel.data-meno-initial-tab="id": optional initial tab. If omitted, the first tab is selected.
State:
- Active tab and panel receive
.is-active. - Inactive panels receive
hidden. - Buttons receive
aria-selected.
Dropdown
Use this for nav menus or compact option panels.
<nav>
<button data-meno-dropdown="services">Services</button>
<div data-meno-dropdown-panel="services">
<a href="/design">Design</a>
<a href="/development">Development</a>
</div>
</nav>Attributes:
data-meno-dropdown="services": trigger key.data-meno-dropdown-panel="services": matching panel key.
State:
- The panel receives
.is-open. - The trigger receives
aria-expanded. - Clicking outside the root closes open dropdowns.
Modal
Use this for centered overlays, signup prompts, video popups, and confirmations.
<button data-meno-open-modal="signup">Open signup</button>
<div data-meno-modal="signup" data-meno-backdrop-close="true">
<div data-meno-modal-panel>
<button data-meno-close aria-label="Close">Close</button>
<h2>Join the list</h2>
<p>Modal content goes here.</p>
</div>
</div>Attributes:
data-meno-open-modal="signup": opens a modal with the same key.data-meno-modal="signup": modal wrapper.data-meno-close: closes the nearest modal.data-meno-backdrop-close="true": clicking the backdrop closes the modal.data-meno-backdrop-close="false": disables backdrop close.
State:
- The modal receives
.is-open. - The modal
hiddenattribute is toggled. - Body scrolling is locked while a modal is open.
- Escape closes open modals.
Drawer / Mobile Nav
Use this for side panels, mobile menus, filter panels, or cart drawers.
<button data-meno-open-drawer="menu">Menu</button>
<aside data-meno-drawer="menu">
<button data-meno-close aria-label="Close menu">Close</button>
<a href="/">Home</a>
<a href="/about">About</a>
</aside>Attributes:
data-meno-open-drawer="menu": opens a drawer with the same key.data-meno-drawer="menu": drawer wrapper.data-meno-close: closes the drawer.
Meno agent compatibility:
data-action="toggle-mobile-nav"is also treated as a drawer opener.data-action="open-drawer"is also supported.
State:
- The drawer receives
.is-open. - The drawer
hiddenattribute is toggled. - Body scrolling is locked while a drawer is open.
- Escape closes open drawers.
Carousel
Use this for horizontally scrolling cards, logos, images, or testimonials.
<div data-meno-carousel>
<button data-meno-carousel-prev aria-label="Previous">Previous</button>
<div data-meno-carousel-track>
<article data-meno-carousel-item>Slide 1</article>
<article data-meno-carousel-item>Slide 2</article>
<article data-meno-carousel-item>Slide 3</article>
</div>
<button data-meno-carousel-next aria-label="Next">Next</button>
</div>Attributes:
data-meno-carousel: marks the carousel wrapper.data-meno-carousel-track: marks the scrollable track.data-meno-carousel-item: optional item marker for scroll snapping styles.data-meno-carousel-prev: previous button.data-meno-carousel-next: next button.
Meno agent compatibility:
data-el="carousel-track"is also supported.data-action="carousel-prev"anddata-action="carousel-next"are also supported.
Behavior:
- Prev/next scroll by one item width.
- The CSS file adds smooth horizontal scroll and scroll snapping.
Sticky Header
Use this to add a class after the visitor scrolls.
<header data-meno-sticky data-meno-sticky-threshold="16">
Site header
</header>Attributes:
data-meno-sticky: watches scroll position.data-meno-sticky-threshold="16": optional pixel threshold. Default is8.
Meno agent compatibility:
data-el="site-header"is also treated as sticky.
State:
- The header receives
.is-scrolledafter the threshold.
Smooth Scroll
Use this for in-page anchor navigation.
<nav data-meno-smooth-scroll>
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
</nav>
<section id="features">Features content</section>
<section id="pricing">Pricing content</section>Attributes:
data-meno-smooth-scroll: put this on a wrapper containing anchor links, or directly on an anchor.
Behavior:
- Links starting with
#scroll smoothly to matching element IDs. - The URL hash is updated without a full jump.
Copy to Clipboard
Use this for copying codes, links, email addresses, or snippets.
<code id="coupon">SAVE20</code>
<button data-meno-copy="#coupon" data-meno-copied-label="Copied!">Copy code</button>You can also copy a literal value without a source element:
<button data-meno-copy data-meno-copy-text="[email protected]">Copy email</button>Attributes:
data-meno-copy="#coupon": selector or ID of the source element.data-meno-copy-text="value": fallback literal text to copy.data-meno-copied-label="Copied!": temporary success label.data-meno-copy-timeout="1500": how long the success label stays, in milliseconds.
Meno agent compatibility:
data-action="copy"anddata-copy-target="coupon"are also supported.
Form State
Use this for simple no-backend validation and success state.
<form data-meno-form data-meno-success="Thanks, we received it.">
<label>
Email
<input name="email" type="email" required data-meno-error="Email is required">
</label>
<button type="submit">Submit</button>
<p data-meno-form-status></p>
</form>Attributes:
data-meno-form: marks a form for handling.data-meno-form-status: element where error/success text appears.data-meno-success="message": success message.data-meno-reset-on-submit="true": reset form after success. Default istrue.data-meno-reset-on-submit="false": keep form values after success.data-meno-error="message": field-level required error message.
Behavior:
- The package prevents default submission.
- Required fields are checked.
- A
meno:form-submitevent is dispatched with{ data }. - No network call is made. Wire your own backend by listening for
meno:form-submit.
Lightbox
Use this for image gallery zoom.
<a href="/images/photo-large.webp" data-meno-open-lightbox data-meno-lightbox-alt="Project photo">
<img src="/images/photo-thumb.webp" alt="Project photo">
</a>
<div data-meno-lightbox hidden>
<button data-meno-close aria-label="Close">Close</button>
<img src="" alt="">
</div>Attributes:
data-meno-open-lightbox: opens the lightbox.data-meno-open-lightbox="/images/photo-large.webp": optional explicit image URL.data-meno-lightbox-alt="Text": alt text for the opened image.data-meno-lightbox: lightbox wrapper.data-meno-close: close button.
State:
- The lightbox receives
.is-open. - The lightbox
hiddenattribute is toggled.
Generic Class Toggle
Use this when you only need to add, remove, or toggle a class.
<button data-meno-class-toggle="is-highlighted" data-meno-target="#card">
Toggle highlight
</button>
<article id="card">Card content</article>Attributes:
data-meno-class-toggle="class-name": toggles a class.data-meno-class-add="class-name": adds a class.data-meno-class-remove="class-name": removes a class.data-meno-target="#selector": optional target. If omitted, the clicked element is used.
Examples:
<button data-meno-class-add="is-open" data-meno-target="#panel">Open</button>
<button data-meno-class-remove="is-open" data-meno-target="#panel">Close</button>
<div id="panel">Panel</div>Custom Event Dispatch
Use this to let no-code controls trigger custom JavaScript elsewhere.
<button data-meno-dispatch="pricing:select">Choose plan</button>
<script>
document.addEventListener('pricing:select', function (event) {
console.log('Selected from', event.detail.source);
});
</script>Attributes:
data-meno-dispatch="event-name": dispatches a bubbling custom event from the clicked element.
MenoFilter
MenoFilter is built into Meno and already works with attributes such as data-meno-filter, data-meno-list, data-meno-search, data-meno-sort, and data-meno-page. This package does not replace it; it complements it for UI interactions outside filtering.
Common MenoFilter attributes:
<div data-meno-filter="posts" data-meno-url-sync="true" data-meno-per-page="6">
<input data-meno-search data-meno-search-fields="title,excerpt" placeholder="Search">
<button data-meno-filter-field="category" data-meno-filter-value="Design">Design</button>
<button data-meno-clear>Clear</button>
<div data-meno-list>
<article data-category="Design" data-title="Example post">Example post</article>
</div>
<p><span data-meno-count="results"></span> results</p>
<div data-meno-empty>No results found.</div>
</div>Interactive Styles Pattern
Use the package to toggle state classes, then style those states in Meno interactiveStyles or component CSS.
{
"interactiveStyles": [
{
"name": "Open panel",
"prefix": "",
"postfix": ".is-open",
"style": { "base": { "display": "block", "opacity": "1" } }
}
]
}For child targets, put the interactive style on the target node and use a parent selector:
{
"prefix": "[data-el='dropdown']:hover ",
"postfix": "",
"style": { "base": { "opacity": "1", "visibility": "visible" } }
}Meno Agent Compatibility
The script recognizes Meno agent conventions as aliases:
| Agent convention | Package behavior |
| --- | --- |
| data-action="toggle-accordion" | Accordion trigger |
| data-action="select-tab" | Tab trigger |
| data-el="tab-panel" | Tab panel |
| data-action="toggle-dropdown" | Dropdown trigger |
| data-el="dropdown-panel" | Dropdown panel |
| data-action="open-modal" | Modal trigger |
| data-action="close-modal" | Modal close |
| data-action="open-drawer" | Drawer trigger |
| data-action="close-drawer" | Drawer close |
| data-action="toggle-mobile-nav" | Drawer/mobile nav trigger |
| data-el="carousel-track" | Carousel track |
| data-action="carousel-prev" | Carousel previous |
| data-action="carousel-next" | Carousel next |
| data-el="site-header" | Sticky header |
| data-action="copy" | Copy trigger |
| data-copy-target="id" | Copy source |
| data-el="form-status" | Form status element |
JavaScript API
import { init, destroy, register } from 'meno-interactions';
const app = init({ root: document });
app.refresh();
destroy();
register('my-interaction', function (root) {
const button = root.querySelector('[data-my-button]');
if (!button) return [];
const handler = function () {
console.log('Clicked');
};
button.addEventListener('click', handler);
return [function () { button.removeEventListener('click', handler); }];
});The browser build exposes window.MenoInteractions:
<script>
window.MenoInteractions.init();
</script>Build
npm run build
npm test