Doc Page

Adapter Integration (Vanilla)

Bind current vanilla adapter APIs to predictable DOM contracts and cleanup-safe lifecycle flows.

Updated: 2026-05-30 · Owner: docs@nake-ui

Integration Rule

Adapters can change syntax, but they cannot change semantic truth. The same primitive should expose the same state transitions, slot names, ARIA relationships, keyboard behavior, and public data-* contract across React, Vanilla, Vue, Solid, and Svelte.

This rule is stricter than "the examples look similar." A menu item selected from a submenu should close the active menu tree in every adapter. A Select option clicked while already selected should still close the popup in every adapter. A portalled Select inside a Popover should still be treated as part of the active layer boundary.

Sources: shared/en/adapter-integration

Adapter Shapes

Vanilla integration is explicit:

const binding = bindSelect(elements, registrations, options);
binding.sync();
binding.destroy();

The host provides DOM nodes and data registrations. The adapter applies controller props, event handlers, ARIA, public data-*, and cleanup.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

Contract Mapping

Vanilla bindings must receive all required elements:

  • Disclosure: root, trigger, content.
  • Tabs: list, tab element map, panel element map.
  • Dialog: trigger, overlay, content, title, description, close where rendered.
  • Select/Listbox/Combobox/Menu: option or item registrations with stable IDs and labels.
  • Popover/Tooltip/Menu/Select/Combobox: anchor, positioner, content, and placement options where used.

The adapter writes public attributes; host code should not overwrite them after binding.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

API Surface Matrix

import {
  bindCombobox,
  bindDialog,
  bindDisclosure,
  bindListbox,
  bindMenu,
  bindPopover,
  bindSelect,
  bindTabs,
  bindTooltip,
  connectDisclosure
} from "@nake-ui/adapter-vanilla";

Supported canonical primitives:

  • bindDisclosure(elements, options) and connectDisclosure(elements, state)
  • bindTabs(elements, registrations, options)
  • bindDialog(elements, options)
  • bindPopover(elements, options)
  • bindMenu(elements, registrations, options)
  • bindListbox(elements, registrations, options)
  • bindSelect(elements, registrations, options)
  • bindCombobox(elements, registrations, options)
  • bindTooltip(elements, options)

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

SSR And Hydration

Vanilla can progressively enhance server-rendered HTML:

  • Render semantic fallback markup.
  • Include stable IDs for ARIA relationships.
  • Bind only after the relevant nodes exist.
  • Keep closed floating content hidden before activation.
  • Destroy bindings when the host route/view is removed.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

Parity Testing

Vanilla parity tests are especially important because the host owns lifecycle. Test:

  • Binding and teardown idempotence.
  • sync() after external updates.
  • Keyboard behavior after DOM registration.
  • Outside dismiss and focus handling.
  • Floating placement reflection.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

Pitfalls

  • Double-binding the same nodes.
  • Forgetting destroy() during route changes.
  • Using unstable option IDs.
  • Replacing aria-* and data-* after adapter sync.
  • Using floating content as its own anchor in nested layer trees.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration

Checklist

  • Binding handle is stored and cleaned up.
  • Registrations are stable.
  • Public DOM attributes are adapter-owned after bind.
  • sync() is called for external state changes.
  • Interaction tests include keyboard and outside dismiss.

Sources: shared/en/adapter-integration, vanilla/en/adapter-integration