# WildflowerJS > WildflowerJS is a reactive JavaScript framework that uses standard HTML, CSS, and JavaScript without a build step, virtual DOM, or custom syntax. It provides reactivity through data attributes (`data-bind`, `data-action`, `data-list`, etc.) and direct DOM manipulation. WildflowerJS was designed with a core philosophy: **trust the browser**. Instead of abstracting away the DOM with a virtual DOM layer, WildflowerJS works directly with native browser APIs that have decades of optimization. ## Core Concepts - [Components](/docs/components): Component registration, state, computed properties, and lifecycle methods - [State Management](/docs/state): Reactive state with automatic DOM updates - [Data Binding](/docs/binding): One-way binding with `data-bind` attribute - [Two-Way Binding](/docs/forms): Form handling with `data-model` attribute - [Event Handling](/docs/events): Actions with `data-action` attribute - [Lists](/docs/lists): Array rendering with `data-list` and templates - [Conditionals](/docs/conditionals): `data-show` (CSS toggle) and `data-render` (DOM insertion/removal) - [Computed Properties](/docs/computed): Derived values that auto-update - [Props](/docs/props): Parent-to-child data passing with `data-prop-*` attributes - [Configurable Templates](/docs/configurable-templates): Parent-defined templates for child rendering ## API Reference - [HTML Attributes](/api/attributes): Complete reference for all data-* attributes - [Component Definition](/api/component-definition): state, computed, props, types, watch, lifecycle hooks - [Store API](/api/stores): Global state management with `store()` and `getStore()` - [Router API](/api/router): Client-side routing with guards and nested routes - [Application API](/api/application): Top-level framework methods and configuration ## Advanced Features - [Advanced Lists](/docs/advanced-lists): Filtering, sorting, external data, and performance optimization - [Stores](/docs/basic-stores): Global reactive state shared across components - [Advanced Stores](/docs/advanced-stores): Store-to-store communication, persistence, API reference - [Routing](/docs/routing): Hash and history mode client-side routing - [SSR](/docs/ssr): Server-side rendering with hydration - [Portals](/docs/portals): Render content outside component hierarchy - [Transitions](/docs/transitions): CSS transition integration - [Plugins](/docs/basic-plugins): Extend framework functionality - [TypeScript](/docs/typescript): Full TypeScript support with type definitions - [CSS Patterns](/docs/css-patterns): Component-scoped CSS without build tools ## Quick Start ```html

Count: 0

``` ## Optimal Page Structure (Performance) **CRITICAL:** Always place scripts in `` with `defer` for best performance: ```html My App
``` **Why `defer` in `` is best:** - Scripts download in parallel with HTML parsing (not sequentially) - Execution order is preserved (framework loads before app.js) - No `DOMContentLoaded` wrapper needed for external scripts - Results in ~50% faster FCP and ~35% lower TBT vs scripts at end of body - Lighthouse score improvement of ~15 points **When an AI assistant generates a WildflowerJS app from a UAM manifest + idiom file, this structure is produced automatically.** **Preventing Flash of Unstyled Content (FOUC):** When using `defer` scripts, add this CSS to prevent modals from briefly flashing: ```css /* Hide cloaked elements until framework processes them */ [data-cloak] { display: none; } ``` Add the `data-cloak` attribute to elements that should be hidden until the framework initializes (e.g., `data-show` elements starting false, `data-portal` elements). The framework removes `data-cloak` after initialization. **Inline Scripts with Deferred Dependencies:** If inline scripts depend on deferred libraries, wrap them in `DOMContentLoaded`: ```javascript document.addEventListener('DOMContentLoaded', function() { // Deferred scripts are now loaded externalLibrary.init(); }); ``` AI-generated UAM pages handle both of these automatically. ## Component Definition Pattern ```javascript wildflower.component('component-name', { // Reactive state state: { property: 'value', nested: { data: true } }, // Derived values (cached, auto-update) computed: { derivedValue() { return this.state.property.toUpperCase(); } }, // Props from parent (via data-prop-* attributes) props: ['propName'], // Runtime type validation (dev builds) types: { property: 'string' }, // Watch specific state changes watch: { property(newVal, oldVal) { console.log('Changed:', oldVal, '->', newVal); }, // :immediate runs handler on init with current value, then on changes 'theme:immediate'(newVal, oldVal) { document.body.className = `theme-${newVal}`; } }, // Declarative store subscriptions (optional) subscribe: { 'store-name': ['path1', 'path2'] }, // Lifecycle: after mount init() { console.log(this.element); // DOM element console.log(this.state); // Reactive state console.log(this.props); // Read-only props }, // Lifecycle: before destroy destroy() { // Cleanup subscriptions, timers, etc. }, // Lifecycle: after any state change (no parameters - use watchers for specifics) onUpdate() { // React to state changes }, // Lifecycle: when subscribed store path changes onStoreUpdate(storeName, path, newValue, oldValue) { // React to store changes }, // Custom methods (bound to component) myMethod() { this.state.property = 'new value'; } }); ``` **Reserved method names** (framework-driven; do not use for action handlers or helpers): `init`, `beforeInit`, `destroy`, `beforeDestroy`, `onUpdate`, `beforeUpdate`, `onError`, `tick`. Most common trap: `tick` runs every animation frame for components in the pool loop, not on click. **Actions before init complete** are queued and replayed in order after `init()` returns. Handlers can assume init-set state is present when they execute. Replayed actions see the original event arg, but `event.preventDefault()` is a no-op by replay time; for forms that must reliably block submission, use `data-event-prevent` on the form element. **`wildflower.destroyComponent(id)` alone does NOT prevent re-initialization.** The framework treats any element with a stale `data-component-id` (instance no longer in `componentInstances`) as a fresh component pending initialization. On the next scan — triggered by any DOM mutation or explicit `wildflower.scan()` — the stale id is stripped, a new instance is created, and `init()` fires again. By design (lets third-party HTML caches like DataTables replay cached DOM). To truly tear down, remove the element from the DOM AND destroy the instance: ```javascript // BAD: auto-resurrect can re-init wildflower.destroyComponent(instance.id); // GOOD: both must happen, either order instance.element.remove(); wildflower.destroyComponent(instance.id); ``` For the common case (removing UI), you usually don't need `destroyComponent` at all — just remove the element and the framework cleans up via its mutation observer. ## DOM Helpers (this.$el) jQuery-like DOM manipulation scoped to the component element. Events are auto-cleaned on component destroy. ```javascript wildflower.component('my-component', { state: { count: 0 }, init() { // Select and chain operations this.$el('.message') .addClass('highlight') .css({ color: 'blue', fontWeight: 'bold' }) .text('Updated!'); // Get raw DOM element for third-party libraries const input = this.$el('.date-input').el; // Returns Element or null flatpickr(input, { dateFormat: 'Y-m-d' }); // Event binding (auto-cleanup when component is destroyed) this.$el('.btn').on('click', () => { this.state.count++; }); // Iterate over multiple elements this.$el('.item').each((el, index) => { console.log(`Item ${index}:`, el.textContent); }); // Form value with reactivity bridge (triggers data-model sync) this.$el('input').val('new value'); // Dispatches input event } }); ``` **Selection:** | Method | Returns | Description | |--------|---------|-------------| | `this.$el(selector)` | Wrapper | Select elements within component | | `this.$el()` | Wrapper | Select component root element | | `this.$el(element)` | Wrapper | Wrap raw DOM element | **Element Access:** | Method | Returns | Description | |--------|---------|-------------| | `.el` | Element/null | First raw DOM element (for third-party libs) | | `.get(i)` | Element | Element at index | | `.length` | Number | Count of matched elements | | `.each(fn)` | Wrapper | Iterate with `fn(el, index)`, `this` = component | **Manipulation (all chainable):** | Method | Description | |--------|-------------| | `.addClass(names)` | Add space-separated classes | | `.removeClass(names)` | Remove classes | | `.toggleClass(name)` | Toggle class | | `.css(prop, val)` | Set style | | `.css({...})` | Set multiple styles | | `.attr(name, val)` | Set attribute | | `.text(val)` | Set text content | | `.html(val)` | Set HTML (auto-scans for components) | | `.val(val)` | Set input value (triggers input event for data-model) | | `.show()` / `.hide()` | Toggle display | **Events:** | Method | Description | |--------|-------------| | `.on(event, fn)` | Attach handler (auto-cleanup on destroy) | | `.off(event, fn?)` | Remove handler(s) | | `.trigger(event)` | Dispatch custom event | **Traversal (boundary-enforced - can't escape component):** | Method | Description | |--------|-------------| | `.find(sel)` | Find descendants | | `.parent()` | Parent element (within component) | | `.closest(sel)` | Closest ancestor (within component) | | `.children()` | Direct children | ## Primitive Lists ($this) For lists of strings, numbers, or other primitives, use `$this` to bind the item value: ```html ``` `$this` refers to the current item itself. For object arrays, use property names directly (e.g., `data-bind="name"`). ## Components in Lists (this.listItem) When a component is rendered inside a `data-list` template, it can access the list item data via `this.listItem`: ```html
``` ```javascript wildflower.component('task-card', { state: { taskId: null }, beforeInit() { // this.listItem contains the task object for this list item if (this.listItem) { this.state.taskId = this.listItem.id; } } }); ``` **Key points:** - `this.listItem` is available in `beforeInit()`, `init()`, and all lifecycle methods - Returns `null` for components not in a list - Provides a live reference to the data object - For nested lists, returns the immediate parent list's item, not an ancestor's - Component boundaries block inheritance: inner components don't inherit outer component's `listItem` ## CSS Patterns (No Build Step) WildflowerJS is CSS-agnostic. For component-scoped styles without build tools, use the `:where()` pattern: ```css /* Zero-specificity scoping using data-component attribute */ :where([data-component="kanban-column"]) { .header { font-weight: bold; padding: 1rem; } .card { border-radius: 4px; margin-bottom: 0.5rem; } } ``` **Why `:where()`?** - Zero specificity: Global themes/utilities can still override easily - Zero framework overhead: Browser handles it natively, no JS processing - Modern browser support: Chrome 88+, Firefox 78+, Safari 14+ **CSS + Reactive Styles:** Use CSS for static structure, `data-bind-style` for dynamic values: ```html
``` ```css :where([data-component="kanban-column"]) { .header { padding: 1rem; border-radius: 8px; /* backgroundColor comes from data-bind-style */ } } ``` ## HTML Attributes Summary | Attribute | Purpose | Example | |-----------|---------|---------| | `data-component` | Define component root | `
` | | `data-bind` | Bind text content | `` | | `data-bind="computedName"` | Bind to computed (auto-detected, no prefix needed) | `` | | `data-bind-html` | Bind HTML content (XSS risk: use `setHtmlSanitizer()` with untrusted input) | `
` | | `data-bind-class` | Dynamic CSS classes | `
` | | `data-bind-style` | Dynamic inline styles | `
` | | `data-model` | Two-way form binding | `` | | `data-model-number` | Convert value to number | `` | | `data-model-trim` | Trim whitespace | `` | | `data-model-lazy` | Update only on blur | `` | | `data-action` | Event handler | `