JavaScript API

Initialize and control interactive Byteion components with the behavior-only runtime.

Overview

@byteion/js is an optional, behavior-only runtime for interactive Byteion components. It activates modals, dropdowns, popovers, tabs, accordions, drawers, tooltips, and toasts by scanning for data-bi-* attributes in the DOM.

The runtime does not inject design styles — component appearance is governed by Tailwind utility classes. It does not manage application or business state. It manages DOM interaction state: open/closed UI, active tabs, ARIA attributes, keyboard navigation, focus trapping, and click-outside dismissal.

Static components such as Button, Card, Badge, Input, and Table do not require JavaScript. You can build complete layouts without loading the JS runtime. JavaScript is only needed when you want interactive behavior (modal open/close, dropdown toggle, tab switching, etc.).

Installation

npm

Requires @byteion/core (peer dependency).
npm install @byteion/js@0.2.0-beta.1

ESM import and initialization

import ByteionCore from '@byteion/js';

document.addEventListener('DOMContentLoaded', () => {
  ByteionCore.init();
});

Initialization

ByteionCore.init() is called automatically on DOMContentLoaded. You only need to call it manually if you are using a module bundler or injecting HTML dynamically.

Full initialization

ByteionCore.init();                    // Initialize all modules on the page
ByteionCore.init(rootElement);         // Initialize only within a specific container

Initializing a single module

ByteionCore.Modal.init();              // Initialize only modals
ByteionCore.Dropdown.init();           // Initialize only dropdowns
ByteionCore.Popover.init();            // Initialize only popovers
ByteionCore.Tabs.init();               // Initialize only tab sets
ByteionCore.Accordion.init();          // Initialize only accordions
ByteionCore.Drawer.init();             // Initialize only drawers
ByteionCore.Tooltip.init();            // Initialize only tooltips
ByteionCore.Toast.init();              // Initialize only toasts

// Each accepts an optional root element for scoped initialization
ByteionCore.Modal.init(sectionElement);

init() is idempotent — calling it multiple times on the same element will not re-initialize already wired components. However, repeated unnecessary calls should be avoided for performance. After injecting new HTML with data-bi-* attributes, call init() again to wire the new content — the MutationObserver handles this automatically when enabled.

Data attributes

Interactive components are activated by adding data-bi-* attributes to HTML elements. The runtime scans for these attributes and wires behavior automatically.

Modal

AttributeOn elementPurpose
data-bi-modalOverlay <div>Identifies the modal container. Value is used as the modal ID.
data-bi-modal-contentInner <div>Focus-trap boundary inside the modal.
data-bi-modal-toggle<button>Value = modal ID. Click toggles the modal.
data-bi-modal-hide<button>Value = modal ID (optional). Click hides the modal or the closest [data-bi-modal] ancestor.
data-bi-modal-staticModal overlay"true" disables backdrop-click and Escape dismiss.
data-bi-modal-keyboardModal overlay"false" disables Escape dismiss only.

Dropdown

AttributeOn elementPurpose
data-bi-dropdownMenu <div>Identifies the dropdown menu container.
data-bi-dropdown-toggle<button>Value = dropdown ID. Click toggles the menu.

Popover

AttributeOn elementPurpose
data-bi-popoverPopover <div>Identifies the popover container.
data-bi-popover-toggle<button>Value = popover ID. Click toggles the popover.

Tabs

AttributeOn elementPurpose
data-bi-tabsWrapper <div>Identifies a tabset container.
data-bi-tabs-toggle<button>Value = panel ID. Click activates that panel.
data-bi-tabs-panelPanel <div>Identifies a tab panel. Value used as panel ID.

Accordion

AttributeOn elementPurpose
data-bi-accordionWrapper <div>Identifies the accordion container.
data-bi-accordion-toggle<button>Value = panel ID. Click toggles that panel.
data-bi-accordion-panelPanel <div>Identifies a collapsible panel.
data-bi-accordion-multipleAccordion wrapper"true" allows multiple panels open simultaneously.
data-bi-accordion-chevronIcon inside toggleGets rotate-180 class toggled on expand/collapse.

Drawer

AttributeOn elementPurpose
data-bi-drawerOverlay <div>Identifies the drawer container.
data-bi-drawer-toggle<button>Value = drawer ID. Click toggles the drawer.
data-bi-drawer-hide<button>Value = drawer ID (optional). Click hides the drawer or the closest [data-bi-drawer] ancestor.
data-bi-drawer-placementDrawer overlay"left" | "right" | "top" | "bottom". Default: "right".
data-bi-drawer-staticDrawer overlay"true" disables backdrop-click and Escape dismiss.
data-bi-drawer-keyboardDrawer overlay"false" disables Escape dismiss only.

Tooltip

AttributeOn elementPurpose
data-bi-tooltipAny elementValue = tooltip text content. The tooltip <div> is created programmatically.
data-bi-tooltip-placementAny element"top" | "right" | "bottom" | "left". Default: "top".

Toast (declarative)

AttributeOn elementPurpose
data-bi-toast<div>Identifies a declarative toast element in the DOM.
data-bi-toast-hide<button>Value = toast ID (optional). Click hides the referenced toast or the closest [data-bi-toast] ancestor.
data-bi-toast-fire<button>Value = JSON matching ToastInput. Click creates and shows a programmatic toast.

Universal

AttributeOn elementPurpose
data-bi-dismiss<button>Value = CSS selector. Click removes the closest matching ancestor element. If empty, removes the direct parent.

Programmatic API

Each module exposes show, hide, toggle, and destroy methods for programmatic control. Targets can be an element reference, an ID string, or a CSS selector.

Modal

ByteionCore.Modal.show('#my-modal');
ByteionCore.Modal.hide('#my-modal');
ByteionCore.Modal.toggle('#my-modal');

Dropdown

ByteionCore.Dropdown.show('#menu-1');
ByteionCore.Dropdown.hide('#menu-1');
ByteionCore.Dropdown.toggle('#menu-1');

Popover

ByteionCore.Popover.show('#popover-1');
ByteionCore.Popover.hide('#popover-1');
ByteionCore.Popover.toggle('#popover-1');

Tabs

ByteionCore.Tabs.show('panel-2');     // Activate panel by panel ID
ByteionCore.Tabs.toggle('panel-2');   // Alias for show()

Accordion

ByteionCore.Accordion.show('section-1');   // Open panel (closes others unless multiple mode)
ByteionCore.Accordion.hide('section-1');   // Close panel
ByteionCore.Accordion.toggle('section-1'); // Toggle panel

Drawer

ByteionCore.Drawer.show('#my-drawer');
ByteionCore.Drawer.hide('#my-drawer');
ByteionCore.Drawer.toggle('#my-drawer');

Tooltip

ByteionCore.Tooltip.show(triggerElement);  // Pass the trigger element
ByteionCore.Tooltip.hide(triggerElement);

Toast (programmatic)

Toasts are created programmatically via ByteionCore.Toast.show(). Accept a ToastInput object with title, message, variant (or tone), and optional duration:

// Success toast
ByteionCore.Toast.show({
  title: 'Saved',
  message: 'Your changes were saved.',
  variant: 'success',
  duration: 4000
});

// Error toast
ByteionCore.Toast.show({
  title: 'Error',
  message: 'Something went wrong.',
  variant: 'danger',
  duration: 8000
});

// Info toast (sticky — does not auto-dismiss)
ByteionCore.Toast.show({
  title: 'New version',
  message: 'v0.2.0 is available.',
  variant: 'info',
  duration: 0
});

// Hide a specific toast by its element reference
const toast = ByteionCore.Toast.show({ title: 'Hello', variant: 'primary' });
ByteionCore.Toast.hide(toast);

Not supported (use the declarative API instead)

  • There is no ByteionCore.init('modal') string-based dispatch. Call ByteionCore.Modal.init() instead.
  • There is no ByteionCore.toast.success() shortcut. Use ByteionCore.Toast.show({ variant: 'success', ... }).

Events

The runtime dispatches CustomEvent instances on component elements. All events bubble and are cancelable. Use e.detail to access event-specific data.

EventTargetWhen it fires
bi:modal:showModal elementAfter the modal becomes visible. detail.modal contains the element.
bi:modal:hideModal elementAfter the modal becomes hidden.
bi:dropdown:showDropdown elementAfter the menu becomes visible.
bi:dropdown:hideDropdown elementAfter the menu becomes hidden.
bi:popover:showPopover elementAfter the popover becomes visible.
bi:popover:hidePopover elementAfter the popover becomes hidden.
bi:tabs:showTabset rootAfter a panel is activated. detail.panelId and detail.root available.
bi:accordion:showAccordion rootAfter a panel is opened. detail.panelId available.
bi:accordion:hideAccordion rootAfter a panel is collapsed.
bi:drawer:showDrawer elementAfter the drawer becomes visible.
bi:drawer:hideDrawer elementAfter the drawer becomes hidden.
bi:tooltip:showTooltip elementAfter the tooltip is created and positioned.
bi:tooltip:hideTooltip elementWhen the tooltip starts its hide animation.
bi:toast:showToast elementAfter the toast is appended to the container.
bi:toast:hideToast elementAfter the hide animation completes and the element is removed.

Listening to events

// Track modal open/close for analytics
document.addEventListener('bi:modal:show', (e) => {
  console.log('Modal opened:', e.detail.modal.id);
});

document.addEventListener('bi:modal:hide', (e) => {
  console.log('Modal closed:', e.detail.modal.id);
});

// React to tab changes
document.addEventListener('bi:tabs:show', (e) => {
  console.log('Tab activated:', e.detail.panelId);
});

Triggering behavior via custom events

You can also show or hide a component by dispatching the corresponding event on its element:

// Open a modal by dispatching an event
const modal = document.querySelector('[data-bi-modal="my-modal"]');
modal.dispatchEvent(new CustomEvent('bi:modal:show'));

// Close it
modal.dispatchEvent(new CustomEvent('bi:modal:hide'));

Accessibility behavior

The runtime manages ARIA attributes and keyboard behavior for interactive components. Here is what each module handles:

  • Modal — Sets role="dialog", aria-modal="true", aria-hidden, and tabindex="-1". Creates a focus trap inside [data-bi-modal-content]. Locks body scroll when open. Supports Escape key and backdrop-click to dismiss (configurable via data-bi-modal-static and data-bi-modal-keyboard).
  • Dropdown — Sets role="menu", aria-hidden. On the toggle: aria-haspopup="true", aria-controls, and aria-expanded. Supports Escape key to close (refocuses trigger) and click-outside dismissal.
  • Popover — Sets role="dialog", aria-hidden. On the toggle: aria-haspopup="dialog", aria-controls, aria-expanded. Supports Escape key and click-outside dismissal.
  • Tabs — Sets role="tab" on toggles with tabindex for roving tabindex navigation. Sets role="tabpanel" on panels. Supports arrow-key navigation (horizontal) and Enter/Space activation. Sets aria-selected on the active toggle.
  • Accordion — Sets aria-controls on each toggle. Toggles aria-expanded on open/close. No built-in keyboard navigation (relies on native button focus).
  • Drawer — Sets role="dialog", aria-modal="true", aria-hidden. Creates a focus trap. Locks body scroll. Supports Escape key and backdrop-click to dismiss (configurable via data-bi-drawer-static and data-bi-drawer-keyboard).
  • Tooltip — Sets role="tooltip" on the generated tooltip element and aria-describedby on the trigger. Activates on mouse enter and focus. Removes on mouse leave and blur.
  • Toast — Sets role="status" on each toast. The toast container uses aria-live="polite" and aria-atomic="true". Toasts auto-dismiss after a configurable duration. Do not use toasts as the only indicator for critical validation errors.

Framework lifecycle examples

The runtime auto-initializes on DOMContentLoaded. If you are using a module bundler, you need to import and ensure init() runs. For frameworks like React and Vue that use @byteion/react or @byteion/vue, behavior initialization is handled internally by the framework packages. The examples below are only needed when using raw HTML with data-bi-* attributes inside a framework project.

Plain HTML

<script type="module">
  import ByteionCore from '@byteion/js';

  document.addEventListener('DOMContentLoaded', () => {
    ByteionCore.init();
  });
</script>

React (raw HTML usage)

import { useEffect, useRef } from 'react';
import ByteionCore from '@byteion/js';

function ModalContainer() {
  const ref = useRef(null);

  useEffect(() => {
    ByteionCore.init(ref.current); // Scope init to this container
  }, []);

  return (
    <div ref={ref}>
      <button data-bi-modal-toggle="my-modal">Open</button>
      <div id="my-modal" data-bi-modal class="...">...</div>
    </div>
  );
}

Vue (raw HTML usage)

<script setup>
import { onMounted, ref } from 'vue';
import ByteionCore from '@byteion/js';

const container = ref(null);

onMounted(() => {
  ByteionCore.init(container.value);
});
</script>

<template>
  <div ref="container">
    <button data-bi-modal-toggle="my-modal">Open</button>
    <div id="my-modal" data-bi-modal class="...">...</div>
  </div>
</template>

Do and Don't

Do

  • Call ByteionCore.init() after the DOM is ready
  • Use data-bi-* attributes for declarative behavior activation
  • Re-run init() after injecting dynamic HTML — or rely on the built-in MutationObserver
  • Listen to bi:* events for analytics, logging, and side effects
  • Keep styling in Tailwind/CSS — the JS runtime handles only behavior

Don't

  • Use JS to patch design tokens or component styles — use Tailwind/CSS variables
  • Initialize before the DOM exists — use DOMContentLoaded or framework lifecycle hooks
  • Depend on toast messages for critical validation errors — toasts auto-dismiss and are not persistent
  • Invent custom data-bi-* attributes that the runtime does not recognize — they will be ignored
  • Duplicate behavior manually (e.g., writing your own focus trap) when the runtime already manages it

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.

Explore Pro Components