import { Meta, Source, Markdown } from '@storybook/blocks';
import { accordionSnippets } from './framework-snippets';

<Meta title="Guides/Using across frameworks" />

# Using `@42` across frameworks

Every interactive component is a single **headless controller** in
`@42/core`. The controller owns state, ARIA and keyboard behaviour and attaches
to the markup you provide via `data-*` hooks — it never imposes styles. Because
the controller is plain TypeScript, the **same** class is reused by every
adapter; the framework packages are only thin glue.

<Markdown>{`
| You ship | Install | Wrapping |
| --- | --- | --- |
| Vanilla / Blade | @42/core (+ @42/styles) | none — new Controller(el) or @42/blade auto-mount |
| React | @42/react + @42/core | uses your app's React (peer dep) |
| Vue | @42/vue + @42/core | uses your app's Vue (peer dep) |
`}</Markdown>

> Adapter coverage today: **Accordion** and **Modal** have first-class React and
> Vue wrappers. Every other component works in vanilla and in any
> server-rendered app via `@42/blade` auto-mount, and gaining a React/Vue wrapper
> is ~5 lines of glue thanks to the generic binding.

---

## Vanilla (`@42/core`)

Instantiate the controller against existing DOM. The markup is the contract.

<Source code={accordionSnippets.vanilla} language="ts" dark />

## React (`@42/react`)

The wrapper creates the root node, instantiates the controller on mount, maps
its `CustomEvent`s to callbacks and calls `destroy()` on unmount. You provide the
same `data-*` markup as children.

<Source code={accordionSnippets.react} language="tsx" dark />

## Vue (`@42/vue`)

Identical idea via a composable (`useController`) under the hood: instantiate on
`onMounted`, re-create when reactive props change, `destroy()` on `onUnmounted`.

<Source code={accordionSnippets.vue} language="html" dark />

## Blade / server-rendered (`@42/blade`)

Your Blade views render the same `data-*` markup. One `mount()` call discovers
every registered root (`[data-accordion]`, `[data-modal]`, `[data-dropdown]`…)
and attaches the matching `@42/core` controller. A `MutationObserver` keeps DOM
injected by Livewire wired up automatically.

<Source code={accordionSnippets.blade} language="html" dark />

<Source code={`import { mount } from '@42/blade';

const app = mount(); // scans the document + observes runtime changes
// app.refresh();     // re-scan after a manual DOM update
// app.unmount();      // destroy every controller and stop observing`} language="ts" dark />

---

The four examples above drive the **exact same** `Accordion` controller from
`@42/core/accordion`. That is what "framework-agnostic" means in practice — see
the per-component pages (e.g. **Core/Accordion**, **Core/Modal**) for the same
breakdown next to a live example.
