WildflowerJS Reactive JS, No BS*

A no-build reactive JavaScript framework, rooted in the web platform.
No build step. No dependencies. No lock-in.

<script src="wildflower.min.js"></script> ...and start building.

Back to Basics

The code you write is 100% web standard code. HTML stays HTML. JavaScript stays JavaScript. CSS stays CSS. No JSX, no templating language, no custom syntax to learn. If you know the web platform, you already know how to use this.

WildflowerJS extends the web platform. It doesn't replace it.

Your Development Simplified

Because you develop with 100% web standards, every tool in your existing chain already understands the code: IDE, browser DevTools, linter, formatter, screen reader, SEO crawler. Nothing to install, no custom file types, no sourcemaps. Save the file, refresh, and your change is live.

Just be a web developer.

Batteries Included: One Mental Model

Router, SSR, stores, computed properties, two-way binding, event modifiers, data pools, and TypeScript types, all built in, all speaking the same language. Learn data-bind once and you know binding everywhere: lists, pools, stores, forms. There's no five-library stack to keep in sync.

One script tag. Everything you need.

<div data-component="counter">
  <span data-bind="count"></span>
  <button data-action="increment">
    +1
  </button>
</div>

<script>
wildflower.component('counter', {
  state: { count: 0 },
  increment() { this.count++ }
})
</script>

How It Works

data-bind connects state to the DOM.

data-action connects events to methods.

this.count++ triggers a precise DOM update.

Mutate state. The DOM updates.

Two Reactivity Modes

data-list for automatic reactivity: mutate state, DOM updates. data-pool for explicit control: plain objects, zero proxy overhead, you say what changed.

Same template syntax. Different performance profile. From interactive forms to per-frame particle systems. You choose the right tradeoff for the job.

Try it. Right-click, inspect this demo. Every dot is a real DOM element.

See full demo →

* Build Step

Zero Toolchain

Modern frameworks ask you to install a compiler, a bundler, a package manager, hundreds of fragile transitive dependencies, and a framework-specific file format, before you write a single line of your application.

WildflowerJS was built starting from a single principle: no build step, no tooling. Ever.

WildflowerJS asks you to add a script tag.

There's no CLI scaffolding step, no config files, no .vue/.jsx/.svelte source format. You don't debug through sourcemaps or wait on a build pipeline. Your project has zero dependencies.

Performance isn't a tradeoff. Build steps optimize bundle delivery, not the runtime work that follows it. WildflowerJS writes directly to the DOM, with no virtual DOM or reconciliation pass between state change and update, so it doesn't need a build step to be fast.

The framework is full-featured without the toolchain: router, SSR, stores, computed properties, transitions, pools. You don't need a toolchain to use any of it.

my-app/
  index.html
  app.js
  style.css
  wildflower.min.js

That's the entire project. No package.json.
No node_modules. No config files. Ship it.

Zero Install. Zero Attack Surface.

Every dependency you install is trust extended to a maintainer you've never met, running scripts on your dev machine and in your CI. A typical React + Vite + UI‑lib setup pulls in 300+ transitive packages before you write a feature.

Each one is a potential intrusion vector. NPM worms, OAuth chains compromising deploy platforms, postinstall hijacking: the supply chain is now where production code gets compromised, not the deploy. And signing isn't a backstop: Mini Shai‑Hulud (May 2026) compromised 170+ packages whose malicious versions carried valid SLSA Build Level 3 provenance, because the attestation came from build infrastructure the worm had already taken over.

WildflowerJS users don't have this attack surface, by construction. There is no npm install, no postinstall script, no transitive package graph. The framework is one file you copy or pin by hash.

As of v1.1, the same holds for building the framework itself. WildflowerJS bundles with a vendored rollup and terser pipeline pulled as three SHA‑512‑pinned tarballs: no npm install, no transitive packages, no postinstall scripts in the build path. The entire toolchain is three files you verify by hash.

Zero dependencies is the absence of a problem the rest of the industry has not properly addressed.

A typical React/Vue project:

  npm install
  ├── hundreds of packages
  ├── from hundreds of maintainers
  ├── postinstall scripts run on install
  └── tens to hundreds of MB of transitive code

WildflowerJS:

  <script src="wildflower.min.js"></script>
  └── 1 file.
      No transitive dependencies.

Zero Lock-in

WildflowerJS works with the DOM, not instead of it. There's no virtual DOM intercepting your code and no compiler rewriting your markup. The render cycle is yours.

That means Leaflet, DataTables, Chart.js, D3, Three.js, any library that touches the DOM, just works. No wrapper packages or framework-specific escape hatches required. Drop in a script tag and use it.

Because your code is standard HTML and JavaScript, you're never locked in. Your skills transfer and your code is more portable. If you outgrow the framework, your knowledge doesn't expire.

This also means your "ecosystem" is all of the world of vanilla JS. Without compromises or hacks.

<!-- Use any library directly -->
<div data-component="map-view">
  <div id="map" style="height: 400px"></div>
</div>
wildflower.component('map-view', {
  state: { lat: 51.505, lng: -0.09 },
  init() {
    // Leaflet works as-is. No wrappers.
    this._map = L.map('map')
      .setView([this.lat, this.lng], 13);
    L.tileLayer('https://{s}.tile.osm.org'
      + '/{z}/{x}/{y}.png').addTo(this._map);
  }
})

Precise Reactivity

When you write this.count++, WildflowerJS updates the single DOM node bound to count. Nothing else is touched. There's no tree diffing or reconciliation pass to figure that out.

This isn't a tradeoff. You get fine-grained updates and a simple mental model. Change a property, the bound element updates. That's the entire reactivity model.

Other frameworks ask you to learn signals, accessors, memos, effects, and subscription lifecycles to achieve what WildflowerJS does with a property assignment.

wildflower.component('dashboard', {
  state: {
    users: 1420,
    status: 'healthy'
  },
  computed: {
    summary() {
      return this.users + ' users, ' + this.status;
    }
  },
  refresh() {
    this.users = 1421;
    // Only the elements bound to 'users'
    // and 'summary' update. Everything
    // else on the page is untouched.
  }
})

One Reactivity Model. Everywhere.

Components, Stores, and Plugins all share the same reactive foundation. State, computed properties, and methods work identically no matter where they live. Learn it once, it works the same way in a UI component, a global store, or a framework plugin.

Other frameworks make you learn a different system for each layer. React components use hooks, but stores need Redux or Zustand, which are completely different APIs. Vue components use reactive data, but Pinia stores have their own patterns. Every layer is a new mental model.

In WildflowerJS, there's one model. A store is a component without a template. A plugin is an entity that extends the framework itself, adding directives, lifecycle hooks, and services. The same this.count++ triggers the same reactivity everywhere.

This unlocks patterns other frameworks can't express. A store can run headless physics simulations with tick(), feeding data into a component that renders it through a pool, all using the same reactive primitives, no glue code required.

// Component: reactive UI
wildflower.component('cart', {
  state: { items: [] },
  computed: {
    total() { return this.items.length; }
  }
})

// Store: global shared state
wildflower.store('user', {
  state: { name: '', role: 'guest' },
  computed: {
    isAdmin() { return this.role === 'admin'; }
  }
})

// Plugin: extends the framework
wildflower.plugin({
  name: 'notifications',
  state: { items: [], unreadCount: 0 },
  computed: {
    hasUnread() { return this.unreadCount > 0; }
  },
  add(msg) { this.items.push(msg); this.unreadCount++; }
})
// Access globally: wildflower.$notifications.add(...)

// Same state. Same computed. Same methods.

Data Pools

Every framework wraps collection items in reactive proxies, whether the item needs it or not. WildflowerJS gives you a choice: data-list for push reactivity (automatic), data-pool for pull reactivity (explicit control, zero proxy overhead).

Pools render plain objects with the same template syntax as lists. Mutate the object, call markDirty(), and only that item updates. Full CRUD, selection, bulk operations, all faster than the push-reactive path.

And because pools use pull-based rendering, they scale to simulations, games, particle systems, and data visualizations at native frame rate. Use cases that would choke a virtual DOM. No other framework has anything like this.

<div data-component="user-table">
  <tbody data-pool="users" data-key="id">
    <template>
      <tr>
        <td data-bind="name"></td>
        <td data-bind="status"
            data-bind-class="status === 'active'
              ? 'badge success'
              : 'badge inactive'"></td>
      </tr>
    </template>
  </tbody>
</div>
wildflower.component('user-table', {
  pools: { users: {} },

  init() {
    // Populate: plain objects, no proxies
    data.forEach(u => this.pools.users.add(u));
  },

  // Optional: add tick() and the same pool
  // renders every frame. Same template, same
  // data, different rendering frequency.
  // That's the only difference between a
  // display table and a particle system.
})

Built for AI-Assisted Development

Because WildflowerJS is standard HTML and JavaScript, AI code assistants already know how to write it. There's no custom syntax to hallucinate or compiler quirks to work around. The code an AI generates runs exactly as written, with no build step between generation and execution.

We go further. WildflowerJS ships an AI-optimized reference page with patterns, anti-patterns, and examples designed for code generation context windows. Our llms.txt file follows the llms.txt convention for machine-readable documentation.

And for structured app generation, our Universal App Manifest lets you describe an entire application as a JSON schema (components, state, computed properties, methods, templates) and have an AI generate the working code from the manifest, mediated through framework-specific idiom files.

You: "Build me a todo app with
WildflowerJS"

AI reads llms.txt or ai-assistant.html
     ↓
Generates standard HTML + JS
     ↓
<div data-component="todo-app">
  <input data-model="newItem">
  <button data-action="addItem">
    Add
  </button>
  <ul data-list="items">
    <template>
      <li data-bind="text"></li>
    </template>
  </ul>
</div>
     ↓
Open in your browser. It works, and you can read and understand the code.

Web Component Libraries & CSS Frameworks

WildflowerJS works with any CSS framework and most JavaScript component libraries out of the box. Because there's no virtual DOM and no build step, you can drop in Bootstrap, DaisyUI, Shoelace, or Chart.js the same way you would in a vanilla HTML page.

The WildflowerJS Advantage:
  • No wrappers needed: unlike React/Vue/Angular, you don't need framework-specific wrapper packages
  • No build step: link a CDN stylesheet or script tag and you're done
  • Real DOM: libraries that read or write the DOM work without conflict
  • Standard HTML: CSS frameworks style your elements directly, no JSX translation needed

Compatibility at a Glance

Third-party libraries fall into three categories based on how they integrate:

Tier Examples Setup Effort
CSS Frameworks
Pure styling, zero JavaScript
DaisyUI, Bootstrap, Bulma, Pico CSS, Open Props, Pure CSS, Beer CSS Link stylesheet Zero config
JS Libraries
DOM-manipulating libraries
Chart.js, SortableJS, Leaflet, FullCalendar, Flatpickr, Tippy.js, Prism.js Script tag + init() Minimal
Web Components
Shadow DOM
Web Awesome, Fluent UI, Nord Health, Shoelace, IBM Carbon Script tag + adapter One script tag

CSS Frameworks (Zero Config)

CSS-only frameworks are the simplest integration. They provide styled components via CSS classes, with no JavaScript runtime, no Shadow DOM, and no conflicts. WildflowerJS's data-bind-class and data-bind-style work perfectly with them.

Recommended: DaisyUI + Tailwind CSS

DaisyUI provides 65+ pre-styled components (buttons, cards, modals, tabs, drawers, etc.) as pure CSS classes built on Tailwind CSS. It has 40K+ GitHub stars, active development, and zero JavaScript overhead.

<!-- Add to your <head> -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>

<!-- That's it. Now use DaisyUI classes with WildflowerJS: -->
<div data-component="todo-app">
    <div class="card bg-base-100 shadow-xl">
        <div class="card-body">
            <h2 class="card-title" data-bind="title"></h2>

            <input type="text" class="input input-bordered w-full"
                   data-model="newItem" placeholder="Add a task...">
            <button class="btn btn-primary mt-2" data-action="addItem">
                Add Task
            </button>

            <div class="divider"></div>

            <div data-list="items">
                <template>
                    <div class="flex items-center gap-2 py-1">
                        <input type="checkbox" class="checkbox checkbox-sm"
                               data-action="toggleItem">
                        <span data-bind="text"
                              data-bind-class="done ? 'line-through opacity-50' : ''">
                        </span>
                        <button class="btn btn-ghost btn-xs ml-auto"
                                data-action="removeItem">
                            &times;
                        </button>
                    </div>
                </template>
            </div>

            <div class="stats shadow mt-4">
                <div class="stat">
                    <div class="stat-title">Remaining</div>
                    <div class="stat-value text-primary" data-bind="remaining"></div>
                </div>
            </div>
        </div>
    </div>
</div>
wildflower.component('todo-app', {
    state: {
        title: 'My Tasks',
        newItem: '',
        items: [
            { text: 'Learn WildflowerJS', done: true },
            { text: 'Try DaisyUI components', done: false },
            { text: 'Build something cool', done: false }
        ]
    },

    computed: {
        remaining() {
            return this.items.filter(i => !i.done).length;
        }
    },

    addItem() {
        if (!this.newItem.trim()) return;
        this.items = [
            ...this.items,
            { text: this.newItem, done: false }
        ];
        this.newItem = '';
    },

    toggleItem(event, element) {
        const index = element.closest('[data-list-index]')?.dataset.listIndex;
        if (index !== undefined) {
            const items = [...this.items];
            items[index] = { ...items[index], done: !items[index].done };
            this.items = items;
        }
    },

    removeItem(event, element) {
        const index = element.closest('[data-list-index]')?.dataset.listIndex;
        if (index !== undefined) {
            this.items = this.items.filter((_, i) => i !== parseInt(index));
        }
    }
});
Why DaisyUI works perfectly: It's pure CSS. The btn, card, input, checkbox, stats classes style standard HTML elements. WildflowerJS handles reactivity; DaisyUI handles appearance. No conflicts possible.

Other CSS Frameworks

Framework Components Approach CDN Size Notes
Bootstrap 5 30+ CSS classes + optional JS ~25 KB Most popular. JS components (dropdowns, modals) work alongside WildflowerJS.
Bulma 40+ Pure CSS ~26 KB Zero JavaScript. Flexbox-based. Clean, modern aesthetic.
Pico CSS 20+ Classless + semantic ~10 KB Styles native HTML elements automatically. Minimal classes needed.
Open Props N/A CSS custom properties ~5 KB Design tokens, not components. Pairs with any approach.
Pure CSS 15+ Pure CSS modules ~4 KB Yahoo's minimal CSS toolkit. Extremely lightweight.
Beer CSS 40+ CSS classes ~15 KB Material Design 3 aesthetic. No build tools required.

All of these work by adding a <link> tag to your page. No adapters, no configuration, no conflicts.

JavaScript Libraries (Minimal Setup)

DOM-manipulating JavaScript libraries — charting, mapping, drag-and-drop, date pickers, rich text editors — integrate naturally because WildflowerJS uses the real DOM. Initialize them in your component's init() method and sync state changes with watchers.

Full integration guide: See Third-Party Library Integration for detailed examples with SortableJS, Leaflet, FullCalendar, Chart.js, and the general integration pattern.

The General Pattern

wildflower.component('my-widget', {
    state: {
        libraryInstance: null,
        data: []
    },

    init() {
        // 1. Initialize the library on a DOM element
        this.libraryInstance = new SomeLibrary(
            this.element.querySelector('#target'),
            { data: this.data }
        );

        // 2. Library events → update WildflowerJS state
        this.libraryInstance.on('change', (newData) => {
            this.data = newData;
        });

        // 3. WildflowerJS state changes → update library
        this.watch = {
            data: () => this.libraryInstance.setData(this.data)
        };
    },

    // 4. Clean up on destroy
    destroy() {
        if (this.libraryInstance) {
            this.libraryInstance.destroy();
        }
    }
});

Libraries Known to Work Well

Library Purpose Integration Notes
SortableJS Drag-and-drop sorting Works with both data-list and manual rendering. See Third-Party Integration.
Leaflet Interactive maps Call wildflower.scan() after popup opens to initialize components inside map popups.
Chart.js Charts and graphs Canvas-based. State changes update chart via chart.update().
FullCalendar Calendar UI Use watchers to sync events between state and calendar instance.
Flatpickr Date/time picker Enhances a standard <input>. Works with data-model directly.
Tippy.js Tooltips & popovers Attaches to existing DOM elements. No conflicts with data bindings.
Quill / TinyMCE Rich text editors Initialize on a container element. Sync content via editor events.
Prism.js / Highlight.js Code highlighting Call after dynamic content loads. This documentation site uses Prism.js.

Web Component Libraries

Web Component libraries like Web Awesome, Fluent UI, Nord Health, Shoelace, and IBM Carbon provide rich, pre-built UI widgets as custom HTML elements with Shadow DOM encapsulation.

Smart Defaults: Most Libraries Work Automatically

WildflowerJS auto-detects custom elements and their capabilities. For any element with a hyphen in its tag name, the framework automatically:

  1. Detects the property: if the element has a boolean checked property, it uses checked; otherwise it uses value
  2. Listens for native events: listens for both input and change events, which covers text inputs, selects, and booleans
  3. Protects Shadow DOM: never sets textContent on custom elements, preventing light DOM destruction

These smart defaults handle simple cases automatically. For production use, load the library's adapter pack, a single script tag that adds precise ready-timing (Lit's updateComplete) and explicit property mapping for every widget in the library.

Custom Elements Everywhere: WildflowerJS passes the Custom Elements Everywhere tests, the community benchmark for framework compatibility with Web Components. The tests cover rendering custom elements, passing data via attributes and properties, and handling DOM events. Run our test suite →
Library Events Adapter Needed? Setup
Web Awesome 3.x Native input / change Yes Load library + adapter
Fluent UI v2 Native input / change Yes Load library + adapter
Nord Health Native input / change Yes Load library + adapter
Shoelace 2.x Prefixed: sl-input / sl-change Yes Load library + adapter
IBM Carbon v2 Custom: cds-*-changed / cds-*-selected Yes Load library + adapter
Rule of thumb: All Lit/FAST-based Web Component libraries need an adapter — even if they fire native events — because the adapter handles property mapping (e.g. checked vs value) and the async updateComplete lifecycle. Each adapter is a single script tag.

With Adapter (Web Awesome)

<!-- Load Web Awesome from CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aspect.me/webawesome@3/dist-cdn/styles/themes/default.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@aspect.me/webawesome@3/dist-cdn/webawesome.loader.js"></script>

<!-- Load WildflowerJS + adapter -->
<script src="wildflower.min.js"></script>
<script src="adapters/web-awesome.js"></script>

<!-- Use data-model as normal: -->
<div data-component="form-demo">
    <wa-input label="Name" data-model="name"></wa-input>
    <wa-select label="Role" data-model="role">
        <wa-option value="dev">Developer</wa-option>
        <wa-option value="design">Designer</wa-option>
    </wa-select>
    <wa-checkbox data-model="agreed">I agree</wa-checkbox>
    <p>Hello, <span data-bind="name"></span>!</p>
</div>
Web Component Example Web Awesome: Two-Way Binding, Multiple Widgets, Presets Open Full Example

With Adapter (Nord Health)

Nord Health is an enterprise design system with input, select, textarea, checkbox, toggle, and range components. Like Web Awesome, it uses an adapter pack for two-way binding.

Web Component Example Nord Health: Two-Way Binding, Multiple Widgets, Presets Open Full Example

With Adapter (Shoelace)

<!-- 1. Load the component library -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/cdn/themes/light.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/cdn/shoelace-autoloader.js"></script>

<!-- 2. Load WildflowerJS -->
<script src="wildflower.min.js"></script>

<!-- 3. Load the adapter pack -->
<script src="wildflower-adapters/shoelace.js"></script>

<!-- 4. Use Web Components with data-model, just like native inputs -->
<div data-component="contact-form">
    <sl-input label="Name" data-model="name"></sl-input>
    <sl-input label="Email" type="email" data-model="email"></sl-input>
    <sl-textarea label="Message" data-model="message"></sl-textarea>
    <sl-checkbox data-model="subscribe">Subscribe to newsletter</sl-checkbox>
    <sl-button variant="primary" data-action="submit">Send</sl-button>

    <p>Preview: <span data-bind="name"></span> (<span data-bind="email"></span>)</p>
</div>
wildflower.component('contact-form', {
    state: {
        name: '',
        email: '',
        message: '',
        subscribe: false
    },

    submit() {
        console.log('Form data:', {
            name: this.name,
            email: this.email,
            message: this.message,
            subscribe: this.subscribe
        });
    }
});
Notice: The component code is identical to what you'd write with native HTML inputs. The adapter handles the translation behind the scenes.

Registering Custom Adapters

For Web Components not covered by an existing adapter pack, register adapters manually:

// Register a single adapter
wildflower.registerAdapter('my-custom-input', {
    prop: 'value',        // The JS property to read/write
    event: 'value-changed' // The event that fires on user interaction
});

// Now data-model works with <my-custom-input>
// <my-custom-input data-model="username"></my-custom-input>

The data-model-event Override

For one-off situations where you need a specific event name without registering a full adapter:

<!-- Override the event name directly on the element -->
<fancy-datepicker data-model="selectedDate" data-model-event="date-selected">
</fancy-datepicker>

Resolution order: data-model-event attribute > registered adapter > smart default (native input + change).

Adapter Packs

WildflowerJS ships adapter packs for popular Web Component libraries. Each adapter maps the library's property names, event names, and async lifecycle (e.g. Lit's updateComplete). Drop in a single <script> tag after the framework.

Some libraries also use non-standard event names, which the adapter handles:

Library Prefix Why Adapter Needed Adapter File Components Covered
Shoelace 2.x sl-* Uses prefixed events (sl-input, sl-change) adapters/shoelace.js 9 (input, textarea, select, radio-group, checkbox, switch, range, rating, color-picker)
IBM Carbon v2 cds-* Per-component custom events (cds-dropdown-selected, cds-checkbox-changed, etc.) adapters/carbon.js 12 (text-input, textarea, password-input, search, number-input, select, dropdown, combo-box, checkbox, toggle, radio-button-group, slider)

CDN Drop-in Usage

<!-- Shoelace (needs adapter, prefixed events) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.1/cdn/themes/dark.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.1/cdn/shoelace-autoloader.js"></script>
<script src="wildflower.min.js"></script>
<script src="adapters/shoelace.js"></script>

<!-- IBM Carbon (needs adapter, per-component custom events) -->
<script type="module" src="https://1.www.s81c.com/common/carbon/web-components/tag/v2/latest/text-input.min.js"></script>
<script type="module" src="https://1.www.s81c.com/common/carbon/web-components/tag/v2/latest/dropdown.min.js"></script>
<script src="wildflower.min.js"></script>
<script src="adapters/carbon.js"></script>

<!-- Web Awesome -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@aspect.me/webawesome@3/dist-cdn/styles/themes/default.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@aspect.me/webawesome@3/dist-cdn/webawesome.loader.js"></script>
<script src="wildflower.min.js"></script>
<script src="adapters/web-awesome.js"></script>

<!-- Fluent UI -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@fluentui/web-components@2.6.1/dist/web-components.min.js"></script>
<script src="wildflower.min.js"></script>
<script src="adapters/fluent-ui.js"></script>

<!-- Nord Health -->
<link rel="stylesheet" href="https://nordcdn.net/ds/css/4.2.0/nord.min.css">
<link rel="stylesheet" href="https://nordcdn.net/ds/tokens/8.0.0/tokens.custom-properties.css">
<script type="module" src="https://nordcdn.net/ds/components/4.11.0/index.js"></script>
<script src="wildflower.min.js"></script>
<script src="adapters/nord.js"></script>

Manual Adapter Registration

If you encounter a Web Component not covered by an existing adapter pack, register an adapter manually:

// Register an adapter for a custom element
wildflower.registerAdapter('my-custom-input', {
    prop: 'value',         // JS property to read/write
    event: 'value-changed' // Event that fires on user interaction
});
// WildflowerJS auto-detects 'value' vs 'checked' and listens for both events.

Choosing the Right Approach

Use this decision tree to pick the right integration tier for your project:

Just need styled components? Use a CSS framework (DaisyUI, Bootstrap, Bulma).
Zero JavaScript overhead, zero conflicts, zero configuration.
Need rich interactive widgets? (charts, maps, drag-and-drop, date pickers)
Use vanilla JS libraries. Initialize in init(), sync with watchers.
See Third-Party JS Libraries for integration patterns.
Want a full design system with 50+ pre-built widgets? (like Vuetify for Vue)
Use Web Awesome, Fluent UI, Nord, Shoelace, or IBM Carbon. Each needs a one-line adapter script tag for two-way binding.

Mixing Approaches

These tiers are not mutually exclusive. A typical production app might use:

<!-- Tailwind/DaisyUI for layout and basic styling -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css" rel="stylesheet">

<!-- Shoelace for rich form widgets -->
<script type="module" src="https://cdn.jsdelivr.net/.../shoelace-autoloader.js"></script>

<!-- Chart.js for data visualization -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<!-- WildflowerJS ties it all together -->
<script src="wildflower.min.js"></script>
<script src="wildflower-adapters/shoelace.js"></script>

DaisyUI handles your card layouts and button styles. Shoelace provides a polished date picker and color picker. Chart.js renders your dashboard graphs. WildflowerJS manages the reactive state that drives all of them.