{"owner":"htmlstreamofficial","repo":"preline","hasSkills":true,"totalSkillsCount":39,"totalTokensCount":106091,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["skills/preline-mcp/SKILL.md","skills/preline-mcp/agents/openai.yaml","skills/preline-mcp/references/catalog-map.md","skills/preline-mcp/references/composite-layouts.md","skills/preline-mcp/references/mcp-tools-reference.md","skills/theme-generator/SKILL.md","skills/theme-generator/docs/final-output-style.md","skills/theme-generator/docs/palette-guidance.md","skills/theme-generator/docs/token-reference.md","skills/theme-generator/docs/validation-checklist.md","skills/theme-generator/docs/workflow.md","skills/theme-generator/examples.md","src/plugins/accordion/README.md","src/plugins/carousel/README.md","src/plugins/collapse/README.md","src/plugins/combobox/README.md","src/plugins/copy-markup/README.md","src/plugins/datatable/README.md","src/plugins/datepicker/README.md","src/plugins/dropdown/README.md","src/plugins/file-upload/README.md","src/plugins/input-number/README.md","src/plugins/layout-splitter/README.md","src/plugins/overlay/README.md","src/plugins/pin-input/README.md","src/plugins/range-slider/README.md","src/plugins/remove-element/README.md","src/plugins/scroll-nav/README.md","src/plugins/scrollspy/README.md","src/plugins/select/README.md","src/plugins/stepper/README.md","src/plugins/strong-password/README.md","src/plugins/tabs/README.md","src/plugins/textarea-auto-height/README.md","src/plugins/theme-switch/README.md","src/plugins/toggle-count/README.md","src/plugins/toggle-password/README.md","src/plugins/tooltip/README.md","src/plugins/tree-view/README.md"],"skills":{"skills/preline-mcp/SKILL.md":"---\nname: preline-mcp\ndescription: Add, integrate, or build Preline UI components and blocks into HTML files via the Preline MCP server. Use when the user mentions \"preline\", says \"/preline\", asks to add a UI component, or requests a Preline block (a ready-made page section) using Preline UI.\n---\n\n# Preline MCP\n\n## Intent\n\nUse this skill to integrate Preline UI components and blocks into HTML files via the Preline MCP server. The server exposes 6 tools that return integration-ready HTML, CSS, and JS.\n\nActivate when:\n- User says \"preline\", \"add preline\", \"/preline\", \"use preline\"\n- User asks for a Tailwind CSS component from the Preline library\n- User asks for a block (ready-made page section) or a starter page\n\n## Defaults\n\n- **Never guess slugs.** All section, component, and category identifiers are exact kebab-case strings. Always discover them via `components_list` or `blocks_categories` first.\n- **One at a time.** Retrieve one component or block, integrate it fully, then move to the next.\n- **Always read the target file** before inserting anything.\n- Call `components_list({ section: \"<inferred-slug>\" })` when the component type is known - this keeps the response small. Omit section only when unsure.\n\n## Decision: Component vs Block\n\n| User says | Use |\n|---|---|\n| \"component\" / \"components\" | Component workflow |\n| \"block\" / \"blocks\" (or \"example\" / \"examples\") | Block workflow |\n| Ambiguous | Use judgment; blocks give more complete results |\n\n## Clarifying Vague Requests\n\nAsk the user to pick instead of guessing in either of these cases:\n\n- **Abstract request.** Too abstract to confidently map onto one category/section, let alone one block/component - e.g. \"give me something for a pricing page\", \"I need a nice form\", \"show me a dashboard example\". Start at step 1 below.\n- **Tied candidates.** The request is concrete enough to reach a single category/section, but `blocks_in_category` / `components_list({ section })` still leaves 2+ candidates whose titles + descriptions satisfy every criterion the request named about equally well (same layout/feature/style class), with nothing in the request to break the tie. Skip straight to step 3 below with just the tied candidates.\n\nIf exactly one candidate matches everything the request names (\"a basic accordion\", \"the SaaS hero with tabs\"), skip straight to the normal workflow - don't interrogate the user for things you can already resolve, and don't ask just to be safe when one option is clearly the best fit.\n\nAll clarifying questions and lists are in **English by default** - switch to the user's language only once they write to you in it.\n\n**Blocks:**\n1. **Ask which category fits.** Call `blocks_categories()` and present the relevant `mainSection / subSection / category` paths with their titles and descriptions - the catalog carries a one-line description for every category, so present it directly rather than inventing your own summary.\n2. **Try to resolve in one round.** If the user's reply both names a category *and* describes the item distinctively enough to identify a single match (a specific layout, feature, or style), call `blocks_in_category` to confirm the exact ID and go straight to `single_block` - skip step 3 entirely.\n3. **Otherwise, ask which block fits best.** Call `blocks_in_category({ mainSection, subSection, category })`, list the block titles + descriptions - or just the tied candidates, for the case above - and ask the user to pick the one closest to their needs.\n4. **Fetch and integrate** the chosen block with `single_block`.\n\n**Components - same shape, sections instead of categories:**\n1. Ask which **section** fits (`components_list()` for the full list, or `components_list({ section })` once you can infer one).\n2. If the reply names a section *and* describes the component distinctively enough → resolve directly via `single_component` (lean on `relative` metadata - see Smart Component Selection - to land on the right default/variant).\n3. Otherwise list the components in that section (titles + descriptions, with `relative.category` groupings where present - or just the tied candidates, for the case above) and ask which fits best.\n4. Fetch and integrate with `single_component`.\n\nKeep each round to one focused question with a short, scannable list - that beats an open \"what do you want?\" and beats guessing a slug just to avoid asking.\n\n## Composite & Layout Requests\n\nWhen a request describes **more than a single component** - several pieces, a region, a full page, or an app shell - don't fetch ad-hoc. Plan the whole result first, then fetch and integrate one node at a time. The same steps apply to any shape: a dashboard, a settings page, a product page, a multi-step form, \"a card/section/layout with X, Y and Z\", etc.\n\n1. **Lock cross-cutting constraints first.** Anything that applies to the whole result - class system (theme tokens vs utilities → `isUtilityBased` / `theme`), shared surfaces/colors, spacing/density, repeated element states - decide once, up front. Keep it identical across every later fetch and edit.\n2. **Decompose into a tree:** containers → regions → leaf components. Write it down; each leaf is one fetch.\n3. **Discover every node - route, then confirm.** Use [references/catalog-map.md](references/catalog-map.md) to route: an abstract intent (a whole page/region/shell) → the right `blocks_categories` branch (reuse a ready-made block as the skeleton when one fits); each named element → the right `components_list({ section })`. The map only tells you *where to look* - always confirm the exact slug against the tool output before inserting.\n4. **Assemble outermost-first, one at a time.** The skeleton/outer container sets the shared surfaces and the script/init anchors; then fill inward region by region, integrating each fully (Integration Rules) before the next. Keep the returned classes, change **text only** - except the specific surfaces/colors/states the user asked to change. Apply every structural adaptation to the markup **before** writing it (see *Adapt before you write* in Integration Rules).\n5. **Verify** the finished result against the request, element by element.\n\nSee [references/composite-layouts.md](references/composite-layouts.md) for the expanded method and worked illustrations.\n\n## Workflow: Components\n\n```\n1. components_list({ section: \"<slug>\" })   → get valid component IDs\n2. single_component({ section, component }) → get HTML/CSS/JS\n3. Read target file\n4. Integrate HTML, CSS, scripts, init (see Integration Rules)\n5. Repeat from step 1 only after integration is complete\n```\n\n## Workflow: Blocks\n\n```\n1. blocks_categories()                                    → get hierarchy\n2. blocks_in_category({ mainSection, subSection, category }) → get block IDs\n3. single_block({ mainSection, subSection, category, block }) → get HTML/CSS/JS\n4. Read target file\n5. Integrate HTML, CSS, scripts, init (see Integration Rules)\n6. Repeat from step 1 only after integration is complete\n```\n\n## Integration Rules\n\n**HTML** - insert where the user needs it.\n\n**CSS** (`<!-- CSS -->` section):\n- MUST go inside `<head>`, immediately before `</head>`\n- NEVER place in `<body>` or near `</body>`\n\n**External scripts** (`<!-- Scripts -->` section):\n- The `<!-- Scripts -->` label is a response section marker - NOT a location in the target file\n- Placement algorithm: open target file, scan **upward** from `</body>`, skipping blank lines, comments, and non-structural tags (`</script>`, `</style>`, etc.)\n- The **first structural closing tag** you reach (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`) is the anchor\n- Insert `<script src>` tags after that anchor, ordered around the existing Preline core script - the loaded `<script src>` whose `src` contains `preline` (e.g. `…/preline/dist/index.js`; exact path varies by install):\n  - scripts whose `src` does **not** contain `preline` (third-party libs: lodash, apexcharts, …) → **before** the Preline core script\n  - scripts whose `src` **does** contain `preline` (Preline helpers, e.g. `hs-*-helpers.js`) → **after** the Preline core script\n  - if no Preline core script exists yet, keep order: scripts without `preline`, then scripts with `preline`\n  - inline init `<script>` always comes last, immediately before `</body>`\n\n**Init** (`<!-- Init -->` section):\n- Place immediately before `</body>`\n- Wrap in `window.addEventListener('load', () => { ... })` unless the block already contains `<script>` tags\n\n**Large responses (artifacts):** when a response is written to a temp scratch file, read it once with the Read tool (use offset/limit for big files), copy the needed blocks into the target, then delete the scratch file. Don't pull the whole artifact into context if you only need to place it.\n\n**Adapt before you write.** When the request differs from the fetched markup - regions the user didn't ask for (breadcrumbs, demo menus, placeholder logos), different blocks or labels - produce the final markup in memory first, then write it into the target in ONE edit per region. Never insert fetched markup wholesale and refactor it with a chain of follow-up edits: every such edit re-transfers large markup, bloats context, and desyncs file state.\n\n**Same token = same color.** Theme tokens (`bg-navbar`, `bg-sidebar`, `bg-layer`, …) are consistent across a theme - to give two surfaces the same color, give them the same token class. Never resolve tokens to raw colors by reading the project's CSS.\n\n**Trust the returned markup - do not re-verify it.** The classes, structure, and `data-hs-*` attributes the MCP returns are valid Preline by construction. This is the single biggest time-sink to avoid. Do NOT:\n- grep, parse, or scan the user's compiled CSS (e.g. `main.css`) to \"confirm\" a class exists or to resolve what color a design token produces - Preline classes resolve at the consumer's build step, so absence from any one stylesheet means nothing;\n- write HTML/DOM/AST validators (Python `HTMLParser`, tag-balance checkers, etc.) - SVG and void elements trip naive parsers and produce false errors;\n- re-read a placed artifact or re-open the edited file just to \"double-check\" the generated code.\n\nPlace the markup, change text only, move on. **Verify against the request** (is every element the user named present?), never by auditing the generated code or the project's CSS.\n\n## Smart Component Selection\n\n`components_list` may return a `relative` object per component:\n- `isSectionDefault: true` - recommended default for the section; prefer when the request is vague\n  - Exception: if the description says \"multiple variants\" and the user wants one, pick a single-variant component instead\n- `category` - logical group (e.g. `\"color-variants\"`, `\"states\"`)\n- `isCategoryDefault: true` - recommended default for its category; prefer when the request implies a style group\n\n## Available Themes\n\n`default` (blue) · `harvest` (amber) · `retro` (fuchsia) · `moon` (grayscale) · `ocean` (cyan) · `bubblegum` (pink) · `cashmere` (mauve) · `autumn` (orange) · `olive` (green)\n\nPass via `isUtilityBased: true, theme: \"<name>\"` on `single_component` or `single_block`.\n\n## Key References\n\n- [references/catalog-map.md](references/catalog-map.md) - routing map: block intent ontology (whole pages/shells) + all component sections grouped by purpose, JS-plugin marked. Open this to pick the right `section` / blocks branch before any discovery call.\n- [references/composite-layouts.md](references/composite-layouts.md) - full method for multi-component / page / shell requests, with worked illustrations\n- [references/mcp-tools-reference.md](references/mcp-tools-reference.md) - full parameter specs, placement algorithm detail, all 6 tools\n","skills/preline-mcp/agents/openai.yaml":"interface:\n  display_name: \"Preline MCP\"\n  short_description: \"Add Preline UI components and blocks to HTML files via the Preline MCP server\"\n  default_prompt: \"Use $preline-mcp to add Preline UI components or blocks to the target HTML file via the Preline MCP server. Call components_list first to discover valid section and component slugs, then single_component to get the HTML/CSS/JS, then integrate into the target file.\"\n","skills/preline-mcp/references/catalog-map.md":"# Catalog Map - where intents and elements live\n\n> Generated from the catalog (blocks.json, components.json, constants.ts). Regenerate with `node scripts/gen-catalog-map.mjs`.\n> This is a **routing map**, not authority - always confirm exact slugs against live `blocks_categories` / `components_list` output before inserting.\n\nUse during the **Discover** step (SKILL.md → Composite & Layout Requests): map an abstract intent to a blocks branch, and each named element to a component section.\n\n---\n\n## Blocks - intent ontology (for whole pages / regions / shells)\n\nAbstract request (\"a pricing page\", \"a login screen\", \"an admin shell\", \"a checkout flow\")? Route to a branch, then `blocks_in_category({ mainSection, subSection, category })`. Whole app shells live under **Navigation & Layout → Sidebars & Shells**.\n\n- **Marketing** - Hero, Banners & Landing Intros · Site Navigation & Headers · Trust & Social Proof · Content & Editorial · Pricing & Conversion · Website Foundations\n- **Data Display** - Analytics & KPIs · Operational & Activity Views · Tables & Records\n- **UI Components** - Menus & Selection · Actions, Status & Progress · Lists & Links\n- **Forms** - Core Inputs & Layouts · Creation & Management · Filters & Refinement · Billing, Feedback & Conversion\n- **Authentication** - Sign In & Account Access · Onboarding & Activation\n- **Ecommerce** - Product Discovery · Product Detail & Purchase · Orders & Commerce Utilities\n- **Communication** - Chat & Inbox · Calls & Media · Contacts & Presence\n- **Productivity** - Calendars & Scheduling · Planning Boards\n- **Blog & Articles** - Editorial Pages · Blog Discovery & Sections\n- **Application Pages** - Workspaces & Admin · Profiles & Accounts\n- **Billing & Payments** - Billing & Payments\n- **Feedback & States** - Alerts & Recovery · Actions & Engagement · Consent & Compliance\n- **Search & Command Palettes** - Search & Navigation · Actions & Shortcuts\n- **Navigation & Layout** - Application Headers & Navigation · Sidebars & Shells · Title Bars & Footers · Blank, Error & Utility Layouts\n\n---\n\n## Components - section index (for individual elements)\n\nPass the section to keep the list small: `components_list({ section: \"<slug>\" })`. 📖 = has a `component_documentation` entry (JS API). Any interactive component still needs the Preline core script wired once.\n\n**Form inputs & controls:** `input` · `input-group` · `input-number`📖 · `textarea` · `select` · `advanced-select`📖 · `combobox`📖 · `searchbox` · `checkbox` · `radio` · `switch` · `color-picker` · `range-slider` · `advanced-range-slider`📖 · `datepicker` · `advanced-datepicker`📖 · `time-picker` · `pin-input`📖 · `strong-password`📖 · `toggle-password`📖 · `file-input` · `file-upload`📖 · `file-uploading-progress-form`\n\n**Menus & overlays:** `dropdown`📖 · `mega-menu` · `context-menu` · `modal` · `offcanvas` · `popover` · `tooltip`📖\n\n**Navigation:** `navbar` · `navs` · `sidebar` · `breadcrumb` · `pagination` · `tabs`📖 · `stepper`📖 · `scrollspy`📖 · `tree-view`📖\n\n**Feedback & status:** `alerts` · `toasts` · `toast-notifications` · `badge` · `progress` · `ratings` · `spinners` · `skeleton` · `legend-indicator`\n\n**Data display:** `tables` · `datatables` · `charts` · `datamaps` · `maps` · `list-group` · `lists` · `timeline`\n\n**Content & media:** `card` · `avatar` · `avatar-group` · `blockquote` · `images` · `carousel`📖 · `marquee` · `devices` · `chat-bubbles` · `text-editor` · `typography` · `static-icons`\n\n**Actions & misc:** `buttons` · `button-group` · `links` · `dividers` · `kbd` · `clipboard` · `copy-markup`📖 · `toggle-count`📖 · `dark-mode` · `confetti` · `drag-and-drop` · `collapse`📖 · `accordion`📖\n\n**Layout:** `container` · `grid` · `columns` · `layout-splitter`📖 · `custom-scrollbar`\n\n","skills/preline-mcp/references/composite-layouts.md":"# Composite & Layout Requests - Detailed Guide\n\nHow to handle any request bigger than a single component - multiple pieces, a region, a full page, an app shell, a wizard. Companion to `SKILL.md` → \"Composite & Layout Requests\".\n\nThe point: **the same five steps apply regardless of domain.** A dashboard shell, an ecommerce product page, a settings screen, a multi-step form, a marketing section, or \"a card with X, Y and Z\" all decompose the same way. Apply the method and let the catalog be the source of truth.\n\nThe mistake to avoid: firing `single_component` / `single_block` ad-hoc and stitching markup as you go. Plan the whole layout first; fetch and integrate one node at a time against that plan.\n\n---\n\n## The method\n\n### 1. Lock cross-cutting constraints first\nResolve anything that applies to the **whole** result before any fetch, because it governs every fetch and edit:\n- **Class system** - design tokens vs utilities. Drives `isUtilityBased` / `theme` on every call; keep it identical across the whole build.\n- **Shared surfaces / color / spacing / density** - anything the request describes as uniform across regions.\n- **Repeated element states** - pinned, selected, active, disabled, collapsed, aligned, icon-only, etc., noted against the element they modify (they change which variant you pick).\n\nDeciding these once prevents inconsistent output halfway through.\n\n### 2. Decompose into a tree\nContainers → regions → leaf components. Write it down. Each leaf is one fetch; grouping leaves by region lets you integrate a region at a time. The shape of the method is identical whether the request is two nested elements or a fifty-element page.\n\n### 3. Discover every node - never guess\n- **Larger structures** (shells, navbars, headers, full sections, pages) → `blocks_categories()`. If a ready-made block already covers the structure, reuse it as the skeleton instead of hand-building.\n- **Individual pieces** → `components_list({ section })`, then `single_component`.\n- Resolve **each named element in the request** to a real slug from the tool output. The \"Where things tend to live\" table below is only a starting point for *which section to open* - the catalog is authoritative, so confirm against its output and never insert an unverified slug.\n\n### 4. Assemble in order - one fetch + integrate at a time\n1. **Outermost / skeleton first.** The shell or top container sets the shared surfaces and - importantly - the script/init placement anchors for everything inside it.\n2. **Fill inward, region by region, leaf by leaf.** Integrate each fully (HTML, CSS in `<head>`, external scripts at the structural anchor, init before `</body>` - see Integration Rules) before fetching the next.\n3. **Honor the locked constraints on every insert.** Keep the classes the MCP returns and change **text only** - except for the specific surfaces/colors/states the user explicitly asked to change.\n4. **Plugins once for the page.** Interactive components share the Preline core script - add it and the init per the placement rules a single time; never duplicate the core script. For a plugin's JS API, events, or options, call `component_documentation`.\n\n### 5. Verify\nRe-read the request and check every named element, state, and constraint is present. Composite requests are easy to under-deliver; the checklist is the guard. Verify **against the request, not by auditing the generated code** - do not grep the project's CSS to confirm token classes exist, and do not run HTML/AST validators over the result (see SKILL.md → Integration Rules). The markup the MCP returned is valid by construction; the only open question is whether you placed every element the user asked for.\n\n---\n\n## Where things live\n\nA request describes UI in plain language; you still discover the real slug. To route an abstract intent (a whole page/region/shell) to the right blocks branch, and each named element to the right component section, use the generated **[catalog-map.md](catalog-map.md)** - it lists the block intent ontology and all component sections grouped by purpose (JS-plugin marked). It's a starting-point map, not authority: always confirm the exact slug against live `blocks_categories` / `components_list` output before inserting.\n\n---\n\n## On surfaces and tokens\n\nWhen the user wants semantic / design-token classes (not raw utilities), keep `isUtilityBased: false` and **preserve the token classes the MCP output already uses** - Preline returns semantic surface tokens, not hardcoded colors. Translate plain-language surface words to the matching tokens you see in the returned markup; do not substitute `bg-white` / `bg-gray-*` / hex, and do not invent token names.\n\nSurface tokens you'll commonly see in the output (recognize and reuse them; exact names can vary by version - match what the catalog emits):\n\n| Plain wording | Token family to look for |\n|---|---|\n| page background | `bg-background` |\n| panel / \"one gray part\" shared by regions | a muted/secondary surface, e.g. `bg-muted` / `bg-secondary` |\n| card / \"layer\" - white with borders | `bg-layer` + `border border-layer-line` (text `text-layer-foreground`) |\n| inset / control surface | `bg-surface` |\n| separators between regions | the matching `*-line` border token |\n\n---\n\n## Worked illustration (one shape of many)\n\nThe five steps are domain-agnostic. The same decomposition covers, for example:\n- **App shell** - outer shell (shared surface) → navbar region(s) → sidebar region → content (distinct surface).\n- **Product page** - gallery → details (title, price, variant pickers, actions) → tabs / reviews.\n- **Settings page** - section nav → form sections → save bar.\n- **Multi-step form** - stepper → step panels → footer actions.\n\nA fuller pass for an app-shell request such as *\"a navbar with a logo, a sidebar toggle, project / team / account dropdowns and dividers, links and a theme switcher; a left sidebar with search and links; navbar + sidebar as one gray panel, content as a white bordered layer; use theme-token classes\":*\n\n**1. Constraints (locked first):**\n- \"theme-token classes\" → `isUtilityBased: false`, no theme passed - and the same setting on every later call.\n- \"navbar + sidebar one gray panel\" → both regions share one muted/secondary surface token; no off-color seams.\n- \"content as a white bordered layer\" → content region uses the layer surface tokens (`bg-layer` + border).\n\n**2. Tree:**\n```\nshell (navbar + left sidebar = shared surface) + content (layer surface)\n├─ navbar · left:  logo · sidebar-toggle · project-dropdown {★pinned A, ★pinned B, Add, Manage}\n│                  · divider · team-dropdown {Add team, ✓selected \"Marketing\"}\n├─ navbar · right: button(s) · text/icon links · divider\n│                  · account-dropdown (avatar trigger) {Upgrade CTA, theme switch, icon links}\n├─ sidebar (below navbar): search input · links · (bottom) icon links\n└─ content: layer surface\n```\n\n**3. Discover (resolve every slug from output):**\n1. `blocks_categories()` → look for an Application / Admin / Dashboard / Navigation shell that already pairs a navbar + sidebar + content. If one fits → `single_block(...)` → use as the skeleton.\n2. If none fits, build the skeleton from the `navbar` + `sidebar` sections.\n3. For each remaining leaf, `components_list({ section })` then `single_component` - sections per the orientation table (`dropdown`, `dividers`, `avatar`, `dark-mode`/`switch`, `input`/`searchbox`, `buttons`, `links`).\n\n**4. Assemble (one fetch + integrate at a time):**\n1. Skeleton → establishes the shared gray surface, the layer content container, and the script anchors.\n2. Navbar-left leaves → navbar-right leaves → sidebar leaves, integrating each fully before the next.\n3. Keep returned classes, edit **text only**; apply only the requested surface changes (shared gray shell, layer content). Set per-element states (pinned/selected) using the component's own selected/checkbox variant.\n4. Wire plugin scripts/init **once** (sidebar toggle, dropdowns, theme switch all need the Preline core script - don't duplicate it). Use `component_documentation` for any plugin's JS API.\n\n**5. Verify** against the request: every named element, every state (pinned, selected), and both surface rules (one gray shell, layer content) present.\n","skills/preline-mcp/references/mcp-tools-reference.md":"# Preline MCP - Tools Reference\n\nFull parameter specs and placement rules for all 6 MCP tools.\n\n---\n\n## Tool 1: `components_list`\n\nBrowse available component sections and their component IDs.\n\n**Input:**\n- `section` (optional string) - pass the inferred section slug to get only that section. Omit to get all sections.\n\n**When to pass section:** whenever the component type is known (e.g. `components_list({ section: \"buttons\" })`). Omitting section loads 300k+ characters - avoid unless you genuinely don't know the section.\n\n**Returns:** JSON with section IDs, component IDs, titles, descriptions, and optional `relative` metadata.\n\n---\n\n## Tool 2: `single_component`\n\nGet HTML/CSS/JS for one Preline component.\n\n**Input:**\n- `section` (required) - exact section slug from `components_list`. Never guess.\n- `component` (required) - exact component ID from `components_list`. Never guess.\n- `isUtilityBased` (optional, default: `false`) - convert design tokens to Tailwind utility classes\n- `theme` (optional, default: `\"default\"`) - theme for utility conversion\n- `parameters` (optional) - template-specific overrides\n\n**Available themes:**\n\n| Slug | Color |\n|---|---|\n| `default` | Blue |\n| `harvest` | Amber |\n| `retro` | Fuchsia |\n| `moon` | Grayscale |\n| `ocean` | Cyan |\n| `bubblegum` | Pink |\n| `cashmere` | Mauve |\n| `autumn` | Amber/Orange |\n| `olive` | Green |\n\n**Examples:**\n```\nsingle_component({ section: \"accordion\", component: \"accordion-basic-usage-example\" })\nsingle_component({ section: \"alerts\", component: \"alerts-solid-color-variants\", isUtilityBased: true })\nsingle_component({ section: \"datatables\", component: \"datatables-basic-usage-example\" })\n```\n\n**Response sections:**\n- `=== COMPONENT HTML ===` - main markup\n- `<!-- CSS -->` - `<link>` and `<style>` tags\n- `<!-- Scripts -->` - `<script src>` tags\n- `<!-- Init -->` - initialization code\n- Large responses are written to a temp file; read via the Read tool\n\n---\n\n## Tool 3: `component_documentation`\n\nGet JavaScript API docs for plugin components.\n\n**Input:**\n- `component` (required) - plugin component name from the documented list\n\n**Available documented components:**\naccordion, advanced-datepicker, advanced-range-slider, advanced-select, carousel, collapse, combobox, copy-markup, datatable, dropdown, file-upload, input-number, layout-splitter, overlay, pin-input, remove-element, scroll-nav, scrollspy, stepper, strong-password, tabs, textarea-autoheight, theme-switch, toggle-count, toggle-password, tooltip, tree-view\n\n**Returns:** API methods, events, data attributes, configuration options, integration examples.\n\n---\n\n## Tool 4: `blocks_categories`\n\nList all block categories in a hierarchy.\n\n**Input:** none\n\n**Returns:** `mainSection → subSection → category` tree with block counts per category.\n\n**Call first** when the user asks for blocks (ready-made page sections; users may also say \"examples\") or references a broad category.\n\n---\n\n## Tool 5: `blocks_in_category`\n\nList block IDs within one category.\n\n**Input:**\n- `mainSection` (required) - top-level slug from `blocks_categories`\n- `subSection` (required) - subsection slug from `blocks_categories`\n- `category` (required) - category slug from `blocks_categories`\n\n**Returns:** stable block IDs and slug/file mappings. Use IDs to call `single_block`.\n\n---\n\n## Tool 6: `single_block`\n\nGet complete integration-ready HTML/CSS/JS for one block.\n\n**Input:**\n- `mainSection` (required)\n- `subSection` (required)\n- `category` (required)\n- `block` (required) - exact ID from `blocks_in_category`. Accepts comma/newline-separated list (first is returned, rest marked as pending).\n- `context` (optional) - insertion hint\n- `isUtilityBased` (optional, default: `false`)\n- `theme` (optional, default: `\"default\"`)\n\n**Returns:** full block HTML with CSS/JS sections.\n\n---\n\n## Integration Placement Rules\n\n### CSS (`<!-- CSS -->`)\n\n```\n✅ CORRECT\n<head>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← immediately before </head>\n</head>\n\n❌ WRONG\n<body>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← NEVER in <body>\n</body>\n```\n\n### External Scripts (`<!-- Scripts -->`)\n\nThe `<!-- Scripts -->` label is a **response section marker only** - it does NOT indicate where to insert in the target file.\n\n**Placement algorithm:**\n\n1. Open the target file\n2. Starting from `</body>`, scan **upward** line by line\n3. Skip: blank lines, HTML comments (`<!-- ... -->`), non-structural tags (`</script>`, `</style>`, `</link>`, `</noscript>`), any non-HTML syntax\n4. The **first structural closing tag** reached (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`, `</header>`, `</aside>`) is the **anchor**\n5. Insert ALL `<script src>` tags on the line **immediately after** that anchor - nothing between the anchor and the scripts\n\n```html\n<!-- ✅ CORRECT - anchor is </main> -->\n</main>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ✅ CORRECT - anchor is </section> -->\n</section>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ❌ WRONG - non-structural content between anchor and scripts -->\n</main>\n<!-- any comment -->\n<script src=\"lib.js\"></script>\n</body>\n```\n\n### Init (`<!-- Init -->`)\n\n```html\n<!-- Place immediately before </body> -->\n<!-- Wrap in window.addEventListener unless block already contains <script> tags -->\n\n<script>\nwindow.addEventListener('load', () => {\n  // init code here\n});\n</script>\n</body>\n```\n\nIf `<!-- Init -->` already contains `<script>` tags - insert as-is, do NOT wrap again.\n\n---\n\n## Smart Component Selection\n\n`components_list` may include a `relative` object per component:\n\n| Field | Meaning | When to prefer |\n|---|---|---|\n| `isSectionDefault: true` | Recommended showcase for the whole section | User request is vague, no specific variant mentioned |\n| `category: \"<slug>\"` | Logical group (e.g. `\"color-variants\"`, `\"states\"`) | Use to filter by style group |\n| `isCategoryDefault: true` | Recommended default within its category | Request implies a style group (e.g. \"icon button\", \"loading state\") |\n\n**Exception for `isSectionDefault`:** if the component description mentions \"multiple variants side by side\" and the user wants a single element - skip it and pick a single-variant component. If no single-variant exists, use the default but extract only the one matching variant from the HTML.\n\n---\n\n## General Rules\n\n- Customize **text content only** - never CSS classes or HTML structure\n- Remove HTML comments before inserting into the target file\n- For complex components (accordion, table, tree), insert the full structure\n- For simple elements (button, input), insert one instance unless multiple are explicitly requested\n","skills/theme-generator/SKILL.md":"---\nname: preline-theme-generator\ndescription: Generate, preview, save, or validate Preline theme CSS using the bundled local generator in this skill. Use when the user asks to create a new Preline theme, match a brand color, translate a mood into a theme, generate light and dark theme CSS, preview theme output without writing files, or review and fix generated theme tokens. Do not use for generic non-Preline CSS work or arbitrary command execution.\n---\n\n# Preline Theme Generator\n\nUse the bundled local scripts. Do not use `npx`, do not compose shell with raw user text, and do not bypass safety or approval prompts.\n\n## Read Order\n\n1. Read `docs/workflow.md`.\n2. Read `docs/palette-guidance.md`.\n3. Read `docs/final-output-style.md` before writing a final theme file.\n4. Read `docs/validation-checklist.md` before closing the task.\n5. Read `examples.md` only if the user wants stylistic guidance or sample outputs.\n6. Read `docs/token-reference.md` only if you need to inspect token coverage in detail.\n\n## Workflow\n\nFollow `docs/workflow.md` for the full operating path.\n\nKey requirements:\n\n1. Identify whether the task is preview, write, or validate.\n2. Normalize the request into explicit generator inputs.\n3. Resolve the target directory with `scripts/find-themes-dir.js`.\n4. Generate CSS only through `scripts/run-theme-generator.js`.\n5. Create or edit only the new theme file.\n6. Validate with `docs/validation-checklist.md`.\n7. Return the result in the mode-appropriate format.\n\n## Security Constraints\n\n- Never use wording that attempts to bypass tool or approval safeguards.\n- Never use `npx` or any network-fetched package for this skill.\n- Never interpolate raw user text directly into shell commands.\n- Never use broad `find .` traversal for path discovery; use `scripts/find-themes-dir.js` or a user-confirmed path.\n- Never write outside a confirmed theme directory.\n- Use `scripts/run-theme-generator.js` as the only execution entry point for generation.\n- Never create or edit additional project files as part of theme generation.\n\n## Common Prompts\n\n- \"Create a sunset theme.\"\n- \"Match this brand color: #2F6BFF.\"\n- \"Generate a cyberpunk dark theme with matching dark mode.\"\n- \"Preview the CSS before writing a file.\"\n- \"Review this generated theme and fix token coverage.\"\n\n## Key References\n\n- `docs/workflow.md`\n- `docs/palette-guidance.md`\n- `docs/final-output-style.md`\n- `docs/validation-checklist.md`\n- `examples.md`\n- `docs/token-reference.md`\n","skills/theme-generator/docs/final-output-style.md":"# Final Output Style\n\nUse this guidance whenever the user wants a final `<name>.css` file for a Preline-based project.\n\n## Core rule\n\nThe deliverable is one file only: `<name>.css`.\n\nDo not create or edit any other project files.\n\n## What the final theme file should look like\n\n- A standalone `<name>.css` file that can live in the project's themes folder.\n- Theme activation via `data-theme=\"theme-<name>\"` and optional `.dark`.\n- Semantic tokens should prefer assigned variables after the theme palette has been mapped.\n\n## Required conventions\n\n- Do not assume project-specific paths other than the discovered themes directory.\n- Do not assume the project already has other custom theme files.\n- Do not force users to change HTML utility classes.\n- Do not create or edit a shared `theme.css`, docs page, or any other file.\n- Keep semantic tokens in the theme selector blocks, not inside `@theme`.\n- Put any custom brand/gray palette needed for this theme inside `@theme theme-<name> inline { }` in the new theme file.\n- If the repo already exposes shared aliases like `--color-primary-*` from its base theme, do not redefine them inside the custom theme file. Assign `--primary-*` directly from the theme-local palette instead.\n\n## Variable strategy\n\nPrefer assigned variables such as:\n\n- `--color-primary-*`\n- `--color-secondary-*`\n- `--background-*`, `--foreground`, `--border`, `--surface-*`, `--muted-*`\n- Tailwind palette vars like `--color-fuchsia-*`, `--color-orange-*`, `--color-neutral-*`\n- shared Preline palette vars exposed by the project's base theme, if available\n\nAllowed in the new theme file:\n\n- theme-local palette vars like `--color-<theme-name>-*`\n- theme-local gray palette vars like `--color-<theme-name>-gray-*`\n\nBut keep them inside `@theme theme-<name> inline { }` and avoid scattering raw palette references when an assigned semantic variable already exists.\n\nAvoid in semantic, chart, and map tokens:\n\n- raw `oklch(...)` assignments\n- literal hex values\n\n## If a unique palette is needed\n\nDefine it inside `@theme theme-<name> inline { }` in the new theme file.\n\nDo not edit any shared/base theme file.\n\n## Structural pattern\n\nUse this order:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector:\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector:\n   - `[data-theme=\"theme-<name>\"].dark`\n\n## Practical workflow\n\n1. Discover the themes folder.\n2. Generate one new `<name>.css` file.\n3. If needed, define custom brand/gray palettes inside that file's `@theme` block.\n4. Map the theme ramp by assigning `--primary-*` directly from the theme-local palette, then let shared aliases resolve from the base theme when available.\n5. Keep chart and map tokens variable-based.\n6. Re-run the validation checklist before returning.\n","skills/theme-generator/docs/palette-guidance.md":"# Palette guidance\n\nUse this file for color-direction heuristics only.\n\nImportant: references below to custom palettes like `--color-<name>-*` or `--color-<name>-gray-*` describe draft palette exploration and shared-palette design patterns. They do NOT override `docs/final-output-style.md`, which defines how the final `<name>.css` file should be structured.\n\n## Contents\n\n- [Hue reference](#hue-reference-for-workflow-normalization)\n- [Mood and palette guidance](#mood-and-palette-guidance)\n- [Accessibility and contrast guidelines](#accessibility-and-contrast-guidelines-wcag)\n- [Palette types](#palette-types)\n- [Custom gray palettes](#custom-gray-palettes-neutral-matching)\n\n## Hue reference (for workflow normalization)\n\n| Color | Hue Range | Tailwind Gray |\n|-------|-----------|---------------|\n| Red/Coral | 0-30 | neutral |\n| Orange/Amber | 30-60 | stone |\n| Yellow/Gold | 60-90 | stone |\n| Green/Lime/Avocado | 90-150 | stone |\n| Teal/Cyan | 150-200 | zinc |\n| Blue/Azure | 200-260 | slate |\n| Violet/Purple | 260-300 | zinc |\n| Magenta/Pink/Rose | 300-360 | neutral |\n\n## Mood and palette guidance\n\n### Color psychology reference\n\n| Mood | Primary Hues | Neutral Family | Characteristics |\n|------|--------------|----------------|-----------------|\n| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |\n| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |\n| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |\n| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |\n| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |\n| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |\n| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |\n| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |\n| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |\n| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |\n\n### Building cohesive palettes\n\n1. Pick a primary: the hero color for buttons, links, CTAs\n2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate\n3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast\n4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family\n\n### Accessibility and contrast guidelines (WCAG)\n\nEnsure themes meet WCAG 2.1 AA contrast requirements:\n\n| Element | Minimum Ratio | OKLCH Rule of Thumb |\n|---------|---------------|---------------------|\n| Body text on background | 4.5:1 | Lightness difference >= 50% |\n| Large text (18pt+) | 3:1 | Lightness difference >= 40% |\n| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |\n| Focus indicators | 3:1 | Use distinct color + lightness |\n\nOKLCH lightness thresholds:\n\n| Context | Light Mode | Dark Mode |\n|---------|------------|-----------|\n| Background | L >= 95% | L <= 25% |\n| Text on light bg | L <= 45% | -- |\n| Text on dark bg | -- | L >= 85% |\n| Primary button text | Check against primary-500/600 | Check against primary-400 |\n\nReadable primary foreground:\n- Do not decide from hue alone.\n- Compare candidate text colors against the actual generated primary shades used by the theme.\n- For light mode, check the generated `primary-600` and `primary-700` states.\n- For dark mode, check the generated `primary-400` and `primary-500` states.\n- Choose the text color with the stronger worst-case contrast across those states.\n- White usually wins for medium or dark primaries; dark gray should only be used when the generated brand states are genuinely light.\n- In dark mode, it is valid to shift the primary button one step deeper (for example `500/600` instead of `400/500`) when that keeps white text readable and better matches the design system.\n\nReadable inverse and decorative accents:\n- `--foreground-inverse` is not just for neutral inverse surfaces. In Preline it is also used on vivid utility colors such as red/orange badges and chips.\n- In dark mode, prefer keeping `--foreground-inverse` white unless contrast checks prove a darker value is still safe across those saturated utility colors.\n- Do not let dark mode inherit very deep brand steps for decorative tokens such as `--chart-5` when those tokens are likely to appear in gradient text or accent UI.\n- For dark decorative/chart tokens, prefer mid-to-light brand steps that stay clearly visible on dark backgrounds.\n\nQuick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.\n\n## Palette types\n\nWhen generating custom themes, there are TWO distinct types of custom color palettes:\n\n| Palette Type | Example Variable | Purpose | Chroma Level |\n|--------------|------------------|---------|--------------|\n| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |\n| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |\n\n### When to create each palette\n\nALWAYS create TWO palettes in `@theme inline { }`:\n1. Brand palette (`--color-<name>-*`) for primary/accent colors\n2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders\n\nBoth palettes are used in light mode for a cohesive feel.\n\nDark mode behavior depends on user request:\n\n| User Request | Light Mode Uses | Dark Mode Uses |\n|--------------|-----------------|----------------|\n| \"soft rose theme\" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |\n| \"soft rose theme with matching dark mode\" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |\n\nTrigger phrases for custom dark mode palette:\n- \"matching dark mode\"\n- \"cohesive dark mode\"\n- \"consistent dark colors\"\n- \"unified light and dark\"\n- \"matching neutrals\"\n\nIf none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.\n\n### Brand color style (vibrant vs soft)\n\nBrand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:\n\n| User Says | Brand Palette Style | Chroma Level |\n|-----------|---------------------|--------------|\n| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |\n| \"soft\", \"muted\", \"ash\", \"pastel\", \"dusty\", \"desaturated\" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |\n\nExample comparison:\n\n```css\n/* Vibrant brand (default) */\n--color-<name>-500: oklch(55% 0.14 <hue>);\n\n/* Soft/muted brand (if requested) */\n--color-<name>-500: oklch(55% 0.04 <hue>);\n```\n\n## Custom gray palettes (neutral matching)\n\nWhen a theme benefits from unique neutrals, generate a custom neutral palette that harmonizes with the primary color. These are NOT pure grays; they have subtle warmth or coolness.\n\n### Palette characteristics\n\n| Palette Type | Hue Range | Chroma Range | Use With |\n|--------------|-----------|--------------|----------|\n| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |\n| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |\n| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |\n| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |\n\n### OKLCH pattern for gray palettes (bell curve chroma)\n\nGray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:\n- Light mode backgrounds (50-200) look clean with subtle tint\n- Dark mode backgrounds (800-950) look sophisticated, not muddy\n\n```css\n--color-<name>-gray-50:  oklch(98%   0.002 <hue>);  /* Almost neutral */\n--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);\n--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);\n--color-<name>-gray-300: oklch(82.7% 0.012 <hue>);  /* Building up */\n--color-<name>-gray-400: oklch(73%   0.018 <hue>);  /* Approaching peak */\n--color-<name>-gray-500: oklch(62.5% 0.020 <hue>);  /* PEAK chroma */\n--color-<name>-gray-600: oklch(52.8% 0.016 <hue>);  /* Starting to fade */\n--color-<name>-gray-700: oklch(41.4% 0.012 <hue>);  /* Fading */\n--color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */\n--color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */\n--color-<name>-gray-950: oklch(20.1% 0.003 <hue>);  /* Nearly pure dark */\n```\n\nCritical: Dark mode backgrounds (800-950) need very low chroma:\n\n| Shade | Chroma | Why |\n|-------|--------|-----|\n| 950 | 0.003 | Dark backgrounds must be nearly neutral |\n| 900 | 0.005 | Avoids muddy/colored appearance |\n| 800 | 0.008 | Just a hint of warmth/coolness |\n| 500 | 0.020 | Peak, midtones carry color identity |\n| 50 | 0.002 | Light backgrounds stay clean |\n\nKey principles:\n- Chroma peaks at mid-tones (400-600), very low at BOTH extremes\n- Dark end (800-950) must have lower chroma than light end for clean dark mode\n- Hue stays consistent across the scale (±5 degrees variation is acceptable)\n- Lightness follows standard 50-950 scale\n\n### Placement of custom palettes\n\nCustom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:\n\n```css\n@theme theme-<name> inline {\n  /* Custom neutral palette for this theme */\n  --color-<name>-50: oklch(98% 0.003 88);\n  --color-<name>-100: oklch(95.5% 0.005 88);\n  /* ... full 50-950 scale ... */\n}\n```\n\nThen reference these in the theme selector blocks using `var()`:\n\n```css\n[data-theme=\"theme-<name>\"] {\n  --background: var(--color-<name>-50);\n  --background-1: var(--color-<name>-100);\n  /* ... */\n}\n\n[data-theme=\"theme-<name>\"].dark {\n  --background: var(--color-<name>-950);\n  --background-1: var(--color-<name>-900);\n  /* ... */\n}\n```\n\n### When to create custom palettes\n\n| Scenario | Action |\n|----------|--------|\n| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |\n| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |\n| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |\n| User explicitly requests | Always create to match their vision |\n| Theme needs distinctive personality | Create for cohesion |\n\n### Using custom palettes in both modes\n\nSoft gray palettes work seamlessly across light and dark modes:\n- Light mode: use 50-300 for backgrounds, 600-950 for text\n- Dark mode: use 800-950 for backgrounds, 50-300 for text\n\nThis creates consistent warmth/coolness across modes while maintaining readability.\n","skills/theme-generator/docs/token-reference.md":"# Preline Theme Token Reference\n\nThis document provides the complete reference for all semantic tokens available in Preline themes. Use this when generating themes to ensure comprehensive coverage.\n\n---\n\n## Contents\n\n- [Overview](#overview)\n- [Core token groups](#core-token-groups)\n- [Component preset patterns](#component-preset-patterns)\n- [Full token reference by category](#full-token-reference-by-category)\n- [Custom color palettes](#custom-color-palettes)\n- [Default values reference](#default-values-reference)\n- [Theme-scoped overrides](#theme-scoped-overrides)\n- [Color format guidelines](#color-format-guidelines)\n- [Quick token count summary](#quick-token-count-summary)\n\n## Overview\n\nPreline themes use CSS custom properties (variables) as semantic tokens. These tokens are:\n- Defined in theme files under `:root` (light) and `.dark` selectors\n- Mapped to Tailwind utilities via `@theme inline` in the base theme\n- Consumed by Preline components for consistent styling\n\nImportant: Generated themes override token values only. Never redefine the Tailwind mappings.\n\n---\n\n## Core token groups\n\n| Group | Tokens | Used for |\n|-------|--------|----------|\n| Brand | `--primary`, `--primary-*` | Primary actions (buttons, links) |\n| Secondary | `--secondary`, `--secondary-*` | Secondary emphasis |\n| Muted | `--muted`, `--muted-*` | Muted/subdued elements |\n| Destructive | `--destructive`, `--destructive-*` | Destructive/danger actions |\n| Background | `--background`, `--background-*` | App surfaces |\n| Foreground | `--foreground`, `--foreground-*` | Default typography colors |\n| Inverse | `--inverse` | Inverted color scheme |\n| Border | `--border` | Default border color |\n| Border line | `--border-line-*` | Border colors for different shades |\n| Layer | `--layer`, `--layer-*` | Layered elements (e.g. white buttons on white panel) |\n| Surface | `--surface`, `--surface-*` | Elevated surfaces |\n| Navbar | `--navbar`, `--navbar-*` | Navigation bar (background, border, divider) |\n| Navbar nav | `--navbar-nav-*` | Navigation bar items (foreground, hover, active) |\n| Sidebar | `--sidebar`, `--sidebar-*` | Sidebar (background, border, divider) |\n| Sidebar nav | `--sidebar-nav-*` | Sidebar items (foreground, hover, active) |\n| Card | `--card`, `--card-*` | Card component (border, divider, header, footer) |\n| Dropdown | `--dropdown`, `--dropdown-*` | Dropdown menus (items, hover, active) |\n| Select | `--select`, `--select-*` | Select component (items, hover, active) |\n| Overlay | `--overlay`, `--overlay-*` | Modal/overlay backgrounds |\n| Popover | `--popover`, `--popover-*` | Popover component |\n| Tooltip | `--tooltip`, `--tooltip-*` | Tooltip component |\n| Table | `--table-line` | Table borders |\n| Switch | `--switch` | Switch/toggle component |\n| Footer | `--footer`, `--footer-*` | Footer component |\n| Scrollbar | `--scrollbar-*` | Custom scrollbar (track, thumb) |\n| Chart | `--chart-*` | Chart colors (Apexcharts) |\n| Map | `--map-colors-*` | Map colors (jsvectormap) |\n\n---\n\n## Component preset patterns\n\nPreline uses numbered variants for component groups to offer different ready-made looks:\n\n| Pattern | Meaning |\n|---------|---------|\n| `--navbar-*` | Default navbar preset (base style) |\n| `--navbar-1-*` | Variant 1 preset (typically subtle/tinted surface) |\n| `--navbar-2-*` | Variant 2 preset (typically stronger tint or different border) |\n| `*` (no suffix) | Background/surface color of the component |\n| `*-line` | Border color for the component container |\n| `*-divider` | Divider lines inside the component |\n| `*-nav-*` | Nav item colors (foreground, hover, focus, active) |\n| `*-nav-list-divider` | Divider used in nav dropdown/list layouts |\n| `*-inverse` | Inverted/contrast variant |\n\nThis pattern applies to: navbar, sidebar, and similar navigation components.\n\n---\n\n## Full token reference by category\n\n### 1. Global surfaces + text\n\n```css\n/* Background tokens */\n--background          /* Main app background */\n--background-1        /* Secondary background (slightly tinted) */\n--background-2        /* Tertiary background (more tinted) */\n--background-plain    /* Plain white/black background */\n\n/* Foreground tokens */\n--foreground          /* Default text color */\n--foreground-inverse  /* Inverted text color (for dark backgrounds) */\n\n/* Inverse */\n--inverse             /* Inverted surface color */\n```\n\n### 2. Borders (full scale)\n\n```css\n--border              /* Default border color */\n--border-line-inverse /* Inverse border color */\n--border-line-1       /* Lightest border */\n--border-line-2\n--border-line-3\n--border-line-4\n--border-line-5\n--border-line-6\n--border-line-7\n--border-line-8       /* Darkest border */\n```\n\n### 3. Primary ramp + states\n\n```css\n/* Full color ramp (11 shades) */\n--primary-50\n--primary-100\n--primary-200\n--primary-300\n--primary-400\n--primary-500\n--primary-600\n--primary-700\n--primary-800\n--primary-900\n--primary-950\n\n/* State tokens */\n--primary             /* Base primary color */\n--primary-line        /* Primary border (usually transparent) */\n--primary-foreground  /* Text on primary background */\n--primary-hover       /* Hover state */\n--primary-focus       /* Focus state */\n--primary-active      /* Active/pressed state */\n--primary-checked     /* Checked state (checkboxes, radios) */\n```\n\n### 4. Secondary\n\n```css\n--secondary            /* Base secondary color */\n--secondary-line       /* Secondary border */\n--secondary-foreground /* Text on secondary background */\n--secondary-hover      /* Hover state */\n--secondary-focus      /* Focus state */\n--secondary-active     /* Active state */\n```\n\n### 5. Layer\n\n```css\n--layer               /* Layer background */\n--layer-line          /* Layer border */\n--layer-foreground    /* Text on layer */\n--layer-hover         /* Hover state */\n--layer-focus         /* Focus state */\n--layer-active        /* Active state */\n```\n\n### 6. Surface\n\n```css\n--surface             /* Base surface */\n--surface-1           /* Surface variant 1 */\n--surface-2           /* Surface variant 2 */\n--surface-3           /* Surface variant 3 */\n--surface-4           /* Surface variant 4 */\n--surface-5           /* Surface variant 5 */\n--surface-line        /* Surface border */\n--surface-foreground  /* Text on surface */\n--surface-hover       /* Hover state */\n--surface-focus       /* Focus state */\n--surface-active      /* Active state */\n```\n\n### 7. Muted\n\n```css\n--muted               /* Muted background */\n--muted-foreground    /* Muted text (lightest) */\n--muted-foreground-1  /* Muted text variant 1 */\n--muted-foreground-2  /* Muted text variant 2 (darkest) */\n--muted-hover         /* Hover state */\n--muted-focus         /* Focus state */\n--muted-active        /* Active state */\n```\n\n### 8. Destructive\n\n```css\n--destructive            /* Destructive/danger color */\n--destructive-foreground /* Text on destructive background */\n--destructive-hover      /* Hover state */\n--destructive-focus      /* Focus state */\n```\n\n### 9. Navbar (base + 2 variants)\n\n```css\n/* Base navbar */\n--navbar                /* Navbar background */\n--navbar-line           /* Navbar border */\n--navbar-divider        /* Navbar divider */\n--navbar-nav-foreground /* Nav item text */\n--navbar-nav-hover      /* Nav item hover */\n--navbar-nav-focus      /* Nav item focus */\n--navbar-nav-active     /* Nav item active */\n--navbar-nav-list-divider /* Nav list divider */\n--navbar-inverse        /* Inverse navbar background */\n\n/* Navbar variant 1 (subtle/tinted) */\n--navbar-1\n--navbar-1-line\n--navbar-1-divider\n--navbar-1-nav-foreground\n--navbar-1-nav-hover\n--navbar-1-nav-focus\n--navbar-1-nav-active\n--navbar-1-nav-list-divider\n\n/* Navbar variant 2 (stronger tint) */\n--navbar-2\n--navbar-2-line\n--navbar-2-divider\n--navbar-2-nav-foreground\n--navbar-2-nav-hover\n--navbar-2-nav-focus\n--navbar-2-nav-active\n--navbar-2-nav-list-divider\n```\n\n### 10. Sidebar (base + 2 variants)\n\n```css\n/* Base sidebar */\n--sidebar\n--sidebar-line\n--sidebar-divider\n--sidebar-nav-foreground\n--sidebar-nav-hover\n--sidebar-nav-focus\n--sidebar-nav-active\n--sidebar-nav-list-divider\n--sidebar-inverse\n\n/* Sidebar variant 1 */\n--sidebar-1\n--sidebar-1-line\n--sidebar-1-divider\n--sidebar-1-nav-foreground\n--sidebar-1-nav-hover\n--sidebar-1-nav-focus\n--sidebar-1-nav-active\n--sidebar-1-nav-list-divider\n\n/* Sidebar variant 2 */\n--sidebar-2\n--sidebar-2-line\n--sidebar-2-divider\n--sidebar-2-nav-foreground\n--sidebar-2-nav-hover\n--sidebar-2-nav-focus\n--sidebar-2-nav-active\n--sidebar-2-nav-list-divider\n```\n\n### 11. Card\n\n```css\n--card                /* Card background */\n--card-line           /* Card border */\n--card-divider        /* Card divider */\n--card-header         /* Card header background */\n--card-footer         /* Card footer background */\n--card-inverse        /* Inverse card background */\n```\n\n### 12. Dropdown\n\n```css\n--dropdown                /* Dropdown background */\n--dropdown-1              /* Dropdown variant background */\n--dropdown-line           /* Dropdown border */\n--dropdown-divider        /* Dropdown divider */\n--dropdown-header         /* Dropdown header background */\n--dropdown-footer         /* Dropdown footer background */\n--dropdown-item-foreground /* Item text */\n--dropdown-item-hover     /* Item hover */\n--dropdown-item-focus     /* Item focus */\n--dropdown-item-active    /* Item active */\n--dropdown-inverse        /* Inverse dropdown */\n```\n\n### 13. Select\n\n```css\n--select                /* Select background */\n--select-1              /* Select variant background */\n--select-line           /* Select border */\n--select-item-foreground /* Item text */\n--select-item-hover     /* Item hover */\n--select-item-focus     /* Item focus */\n--select-item-active    /* Item active */\n--select-inverse        /* Inverse select */\n```\n\n### 14. Overlay\n\n```css\n--overlay             /* Overlay/modal background */\n--overlay-line        /* Overlay border */\n--overlay-divider     /* Overlay divider */\n--overlay-header      /* Overlay header background */\n--overlay-footer      /* Overlay footer background */\n--overlay-inverse     /* Inverse overlay */\n```\n\n### 15. Popover, tooltip, table, switch\n\n```css\n/* Popover */\n--popover             /* Popover background */\n--popover-line        /* Popover border */\n\n/* Tooltip */\n--tooltip             /* Tooltip background */\n--tooltip-foreground  /* Tooltip text */\n--tooltip-line        /* Tooltip border */\n\n/* Table */\n--table-line          /* Table border */\n\n/* Switch */\n--switch              /* Switch knob color */\n```\n\n### 16. Footer\n\n```css\n--footer              /* Footer background */\n--footer-line         /* Footer border */\n--footer-inverse      /* Inverse footer */\n```\n\n### 17. Scrollbar\n\n```css\n--scrollbar-track         /* Scrollbar track */\n--scrollbar-thumb         /* Scrollbar thumb */\n--scrollbar-track-inverse /* Inverse track */\n--scrollbar-thumb-inverse /* Inverse thumb */\n```\n\n### 18. Charts (Apexcharts)\n\nImportant: keep chart `-hex` tokens variable-based in generated theme source. Do not hardcode literal hex or oklch values into the final theme file.\n\n```css\n/* Primary chart color */\n--chart-primary\n--chart-colors-primary\n--chart-colors-primary-inverse\n--chart-colors-primary-hex\n--chart-colors-primary-hex-inverse\n\n/* Chart palette (1-10) */\n--chart-1 through --chart-10\n--chart-colors-chart-1 through --chart-colors-chart-10\n--chart-colors-chart-1-inverse through --chart-colors-chart-10-inverse\n--chart-colors-chart-1-hex through --chart-colors-chart-10-hex\n--chart-colors-chart-1-hex-inverse through --chart-colors-chart-10-hex-inverse\n\n/* Chart backgrounds */\n--chart-colors-background\n--chart-colors-background-inverse\n--chart-colors-chart-inverse\n\n/* Chart foregrounds */\n--chart-colors-foreground\n--chart-colors-foreground-inverse\n\n/* Chart labels */\n--chart-colors-labels\n--chart-colors-labels-inverse\n--chart-colors-xaxis-labels\n--chart-colors-xaxis-labels-inverse\n--chart-colors-yaxis-labels\n--chart-colors-yaxis-labels-inverse\n\n/* Chart grid */\n--chart-colors-grid-border\n--chart-colors-grid-border-inverse\n\n/* Chart special */\n--chart-colors-bar-ranges\n--chart-colors-bar-ranges-inverse\n--chart-colors-candlestick-upward\n--chart-colors-candlestick-upward-inverse\n--chart-colors-candlestick-downward\n--chart-colors-candlestick-downward-inverse\n```\n\n### 19. Maps (jsvectormap)\n\n```css\n--map-colors-primary\n--map-colors-primary-inverse\n--map-colors-default\n--map-colors-default-inverse\n--map-colors-highlight\n--map-colors-highlight-inverse\n--map-colors-border\n--map-colors-border-inverse\n```\n\n---\n\n## Custom color palettes\n\nThe base theme defines custom color palettes that themes can use:\n\n### Khaki (earthy/warm neutral)\n\n```css\n--color-khaki-50 through --color-khaki-950\n```\n\n### Lavender (soft purple/mauve neutral)\n\n```css\n--color-lavender-50 through --color-lavender-950\n```\n\nThese are useful for creating warm or soft-toned themes instead of using standard gray/neutral.\n\n---\n\n## Default values reference\n\n### Light mode defaults (:root)\n\n```css\n:root {\n  /* Backgrounds */\n  --background: var(--color-white);\n  --background-1: var(--color-gray-50);\n  --background-2: var(--color-gray-100);\n  --background-plain: var(--color-white);\n\n  /* Foregrounds */\n  --foreground: var(--color-gray-800);\n  --foreground-inverse: var(--color-white);\n  --inverse: var(--color-gray-800);\n\n  /* Borders */\n  --border: var(--color-gray-200);\n  --border-line-inverse: var(--color-white);\n  --border-line-1: var(--color-gray-100);\n  --border-line-2: var(--color-gray-200);\n  --border-line-3: var(--color-gray-300);\n  --border-line-4: var(--color-gray-400);\n  --border-line-5: var(--color-gray-500);\n  --border-line-6: var(--color-gray-600);\n  --border-line-7: var(--color-gray-700);\n  --border-line-8: var(--color-gray-800);\n\n  /* Primary (blue by default) */\n  --primary-50: var(--color-blue-50);\n  --primary-100: var(--color-blue-100);\n  --primary-200: var(--color-blue-200);\n  --primary-300: var(--color-blue-300);\n  --primary-400: var(--color-blue-400);\n  --primary-500: var(--color-blue-500);\n  --primary-600: var(--color-blue-600);\n  --primary-700: var(--color-blue-700);\n  --primary-800: var(--color-blue-800);\n  --primary-900: var(--color-blue-900);\n  --primary-950: var(--color-blue-950);\n\n  --primary: var(--color-primary-600);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  --primary-focus: var(--color-primary-700);\n  --primary-active: var(--color-primary-700);\n  --primary-checked: var(--color-primary-600);\n\n  /* Secondary */\n  --secondary: var(--color-gray-900);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-white);\n  --secondary-hover: var(--color-gray-800);\n  --secondary-focus: var(--color-gray-800);\n  --secondary-active: var(--color-gray-800);\n\n  /* Layer */\n  --layer: var(--color-white);\n  --layer-line: var(--color-gray-200);\n  --layer-foreground: var(--color-gray-800);\n  --layer-hover: var(--color-gray-50);\n  --layer-focus: var(--color-gray-50);\n  --layer-active: var(--color-gray-50);\n\n  /* Surface */\n  --surface: var(--color-gray-100);\n  --surface-1: var(--color-gray-200);\n  --surface-2: var(--color-gray-300);\n  --surface-3: var(--color-gray-400);\n  --surface-4: var(--color-gray-500);\n  --surface-5: var(--color-gray-600);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-gray-800);\n  --surface-hover: var(--color-gray-200);\n  --surface-focus: var(--color-gray-200);\n  --surface-active: var(--color-gray-200);\n\n  /* Muted */\n  --muted: var(--color-gray-50);\n  --muted-foreground: var(--color-gray-400);\n  --muted-foreground-1: var(--color-gray-500);\n  --muted-foreground-2: var(--color-gray-600);\n  --muted-hover: var(--color-gray-100);\n  --muted-focus: var(--color-gray-100);\n  --muted-active: var(--color-gray-100);\n\n  /* Destructive */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Navbar (base) */\n  --navbar: var(--color-white);\n  --navbar-line: var(--color-gray-200);\n  --navbar-divider: var(--color-gray-200);\n  --navbar-nav-foreground: var(--color-gray-800);\n  --navbar-nav-hover: var(--color-gray-100);\n  --navbar-nav-focus: var(--color-gray-100);\n  --navbar-nav-active: var(--color-gray-100);\n  --navbar-nav-list-divider: var(--color-gray-200);\n  --navbar-inverse: var(--color-primary-950);\n\n  /* Navbar-1 */\n  --navbar-1: var(--color-gray-50);\n  --navbar-1-line: var(--color-gray-200);\n  --navbar-1-divider: var(--color-gray-200);\n  --navbar-1-nav-foreground: var(--color-gray-800);\n  --navbar-1-nav-hover: var(--color-gray-200);\n  --navbar-1-nav-focus: var(--color-gray-200);\n  --navbar-1-nav-active: var(--color-gray-200);\n  --navbar-1-nav-list-divider: var(--color-gray-200);\n\n  /* Navbar-2 */\n  --navbar-2: var(--color-gray-100);\n  --navbar-2-line: transparent;\n  --navbar-2-divider: var(--color-gray-300);\n  --navbar-2-nav-foreground: var(--color-gray-800);\n  --navbar-2-nav-hover: var(--color-gray-200);\n  --navbar-2-nav-focus: var(--color-gray-200);\n  --navbar-2-nav-active: var(--color-gray-200);\n  --navbar-2-nav-list-divider: var(--color-gray-200);\n\n  /* Sidebar follows same pattern as navbar */\n  /* Card, Dropdown, Select, Overlay, Popover, Tooltip, Table, Switch, Footer, Scrollbar */\n  /* See the project's existing base Preline theme source for complete default values */\n}\n```\n\n### Dark mode defaults (.dark)\n\n```css\n.dark {\n  /* Backgrounds */\n  --background: var(--color-neutral-800);\n  --background-1: var(--color-neutral-900);\n  --background-2: var(--color-neutral-900);\n  --foreground: var(--color-neutral-200);\n  --inverse: var(--color-neutral-950);\n\n  /* Borders */\n  --border: var(--color-neutral-700);\n  --border-line-1: var(--color-neutral-800);\n  --border-line-2: var(--color-neutral-700);\n  --border-line-3: var(--color-neutral-600);\n  --border-line-4: var(--color-neutral-500);\n  --border-line-5: var(--color-neutral-400);\n  --border-line-6: var(--color-neutral-300);\n  --border-line-7: var(--color-neutral-200);\n  --border-line-8: var(--color-neutral-100);\n\n  /* Primary (shifts to 500 in dark mode) */\n  --primary: var(--color-primary-500);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-600);\n  --primary-focus: var(--color-primary-600);\n  --primary-active: var(--color-primary-600);\n  --primary-checked: var(--color-primary-500);\n\n  /* Secondary */\n  --secondary: var(--color-white);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-neutral-800);\n  --secondary-hover: var(--color-neutral-100);\n  --secondary-focus: var(--color-neutral-100);\n  --secondary-active: var(--color-neutral-100);\n\n  /* Layer */\n  --layer: var(--color-neutral-800);\n  --layer-line: var(--color-neutral-700);\n  --layer-foreground: var(--color-white);\n  --layer-hover: var(--color-neutral-700);\n  --layer-focus: var(--color-neutral-700);\n  --layer-active: var(--color-neutral-700);\n\n  /* Surface */\n  --surface: var(--color-neutral-700);\n  --surface-1: var(--color-neutral-600);\n  --surface-2: var(--color-neutral-500);\n  --surface-3: var(--color-neutral-600);\n  --surface-4: var(--color-neutral-500);\n  --surface-5: var(--color-neutral-400);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-neutral-200);\n  --surface-hover: var(--color-neutral-600);\n  --surface-focus: var(--color-neutral-600);\n  --surface-active: var(--color-neutral-600);\n\n  /* Muted */\n  --muted: var(--color-neutral-800);\n  --muted-foreground: var(--color-neutral-500);\n  --muted-foreground-1: var(--color-neutral-400);\n  --muted-foreground-2: var(--color-neutral-300);\n  --muted-hover: var(--color-neutral-700);\n  --muted-focus: var(--color-neutral-700);\n  --muted-active: var(--color-neutral-700);\n\n  /* Destructive (same red, readable in dark) */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Components shift to neutral-800/900 backgrounds */\n  /* Nav items shift to neutral-700 hover states */\n  /* See the project's existing base Preline theme source for complete dark mode values */\n}\n```\n\n---\n\n## Theme-scoped overrides\n\nWhen creating a theme, override values under theme-scoped selectors:\n\n```css\n/* Light mode */\n:root[data-theme=\"theme-<name>\"],\n[data-theme=\"theme-<name>\"] {\n  /* Override tokens here */\n}\n\n/* Dark mode */\n[data-theme=\"theme-<name>\"].dark {\n  /* Override dark-specific tokens here */\n}\n```\n\n---\n\n## Color format guidelines\n\n- For most tokens: use Tailwind color variables `var(--color-<color>-<shade>)`\n- For oklch colors: acceptable for standard tokens\n- For chart `-hex` tokens: keep them as stable variable references in generated source\n- For custom colors: can use hex, rgb, hsl, or oklch\n\n---\n\n## Quick token count summary\n\n| Category | Token Count |\n|----------|-------------|\n| Global surfaces + text | 7 |\n| Borders | 10 |\n| Primary (ramp + states) | 18 |\n| Secondary | 6 |\n| Layer | 6 |\n| Surface | 11 |\n| Muted | 7 |\n| Destructive | 4 |\n| Navbar (3 variants) | 27 |\n| Sidebar (3 variants) | 27 |\n| Card | 6 |\n| Dropdown | 11 |\n| Select | 8 |\n| Overlay | 6 |\n| Popover | 2 |\n| Tooltip | 3 |\n| Table, Switch, Footer | 5 |\n| Scrollbar | 4 |\n| Charts | ~50+ |\n| Maps | 8 |\n| Total | ~220+ tokens\n","skills/theme-generator/docs/validation-checklist.md":"# Validation checklist (must self-check before returning)\n\n## Final source style\n- [ ] Only one project file is created or edited: `<name>.css`\n- [ ] The final file is a production-style `<name>.css` file\n- [ ] `@theme theme-<name> inline { }` is present\n- [ ] Any custom brand/gray palette lives inside `@theme theme-<name> inline { }` in the new theme file\n- [ ] Semantic tokens prefer assigned vars such as `--color-primary-*`, `--background-*`, `--foreground`, `--border`, `--surface-*`, or shared palette vars when available\n- [ ] The custom theme does not redefine shared aliases like `--color-primary-*` inside `@theme` when the repo base theme already owns them\n- [ ] No raw `oklch(...)` or literal hex values remain in semantic, chart, or map token assignments in the final theme file\n\n## Structure\n- [ ] Theme key consistent: `theme-<name>` everywhere\n- [ ] Light selector uses `:root[data-theme=...]`, `[data-theme=...]`\n- [ ] Dark selector is `[data-theme=\"theme-<name>\"].dark`\n- [ ] Semantic token definitions are in selector blocks (NOT inside @theme block)\n- [ ] No requirement to change HTML utility classes (only `data-theme`, optional `.dark`)\n\n## Palette strategy\n- [ ] Primary ramp maps `--primary-*` directly to theme-local palette vars\n- [ ] Neutral/background tokens use assigned palette vars\n- [ ] If a new custom palette is needed, it is declared inside the new theme file's `@theme` block, not by editing another file\n\n## Token coverage\n- [ ] Full coverage present across global tokens, states, and major component groups\n- [ ] CSS syntax valid (balanced braces, no broken comments)\n- [ ] If behavior overrides exist, they are theme-scoped only\n- [ ] Chart/map tokens use variable references matching shipped theme patterns\n\n## Dark mode consistency\n- [ ] Dark mode uses CSS variables (`var(--color-*)`) not hardcoded color literals\n- [ ] Dark mode uses one consistent grayscale family unless there is a deliberate, reference-backed reason not to\n- [ ] Hover/focus/active states remain readable and distinguishable\n- [ ] `--foreground-inverse` remains readable on vivid utility colors in dark mode (for example red badges/chips)\n- [ ] Dark-mode chart/decorative tokens avoid very deep brand steps when those tokens are used in gradient text or accent UI\n","skills/theme-generator/docs/workflow.md":"# Workflow\n\nUse this file as the default operating guide for the active theme skill.\n\n## Task modes\n\nClassify the request before doing any work:\n\n- `preview`: generate CSS and return it without writing a project file\n- `write`: create one new `<name>.css` theme file\n- `validate`: review or fix an existing generated theme file\n\n## Normalize the request\n\nConvert natural language into the generator's small, explicit input set:\n\n| Input | Required | Notes |\n|------|----------|-------|\n| `name` | yes | kebab-case theme name |\n| `hue` or `primaryColor` | yes | use `primaryColor` only when user gave a brand hex |\n| `style` | no | `vibrant` by default, `soft` for muted/pastel/desaturated requests |\n| `useCustomDarkGray` | no | true only when user explicitly wants matching/cohesive dark neutrals |\n| `tailwindGray` | no | choose `stone`, `neutral`, `zinc`, `slate`, or `gray` based on brand warmth |\n| `fontSans` / `fontSerif` / `fontMono` | no | include only when requested |\n\nUse `docs/palette-guidance.md` for hue ranges, mood mapping, contrast heuristics, and gray-family decisions.\n\n### Natural-language interpretation\n\nUse the user's language to infer the palette direction:\n\n| User language | Default interpretation |\n|--------------|------------------------|\n| warm sunset, cozy, inviting | orange/amber primary, warm neutrals, medium contrast |\n| professional, clean, SaaS | blue/indigo primary, slate or zinc neutrals |\n| playful, candy, youthful | pink/purple/cyan primary, higher contrast |\n| earthy, organic, calm | green/teal primary, stone neutrals |\n| soft, muted, ash, dusty | `style: soft` |\n| matching dark mode, cohesive dark, unified neutrals | `useCustomDarkGray: true` |\n\nWhen the request is vague:\n\n1. Make an opinionated, coherent palette choice.\n2. Keep contrast readable.\n3. Prefer a consistent system over a flashy but uneven palette.\n\nForeground rule:\n- When mapping text or icon colors onto the primary brand color, choose the foreground by contrast against the generated primary states, not by hue bucket alone.\n- Treat `--foreground-inverse` separately from primary foregrounds. In Preline it is reused on hardcoded vivid utility backgrounds such as red badges, so dark mode should keep it readable on saturated colors instead of tinting it into a dark neutral.\n- Treat chart tokens as decorative UI tokens, not just data-series tokens. Dark mode must override any chart shades that would become too dark for gradient text, badges, or chart accents on dark surfaces.\n\n## Discover the target directory\n\nUse the bundled resolver instead of broad repo traversal:\n\n```bash\nnode scripts/find-themes-dir.js\n```\n\nTrusted theme directories are checked in this order:\n\n- `src/assets/css/themes/`\n- `src/css/themes/`\n- `src/styles/themes/`\n- `assets/css/themes/`\n- `styles/themes/`\n- `dist/assets/css/themes/`\n- `npm/preline/css/themes/`\n\nRules:\n\n- Prefer source theme directories over generated outputs.\n- Do not use repo-wide `find .` traversal for this task.\n- If no trusted theme directory is found and the user did not provide a path, ask where the file should be written.\n\n## Generate CSS\n\nUse the local wrapper as the only execution entry point:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --stdout\n```\n\nWrite a theme file with:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --output src/assets/css/themes/seafoam.css\n```\n\nRules:\n\n- Do not use `npx`.\n- Do not interpolate raw user text into shell commands.\n- Do not chain temp-file cleanup or other unrelated shell work into the command.\n- Do not create or edit any project file other than the new theme file.\n\n## Theme structure expectations\n\nThe wrapper should emit a production-style standalone `<name>.css` file:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector\n   - `[data-theme=\"theme-<name>\"].dark`\n\nKeep custom brand and gray palettes inside the file's `@theme` block. Keep semantic tokens in the selector blocks.\n\nSee `docs/final-output-style.md` for the final file rules and `docs/token-reference.md` only when detailed token coverage inspection is necessary.\n\n## Dark-mode decision rule\n\nAlways generate both palettes in `@theme theme-<name> inline { }`:\n\n- `--color-<name>-*` for the brand ramp\n- `--color-<name>-gray-*` for the theme's neutral ramp\n\nDark-mode neutrals depend on user intent:\n\n- Explicit matching/cohesive dark request: use the theme's custom gray palette in dark mode.\n- Default path: keep light mode custom and use a Tailwind gray family in dark mode.\n\nPrimary foreground decision:\n- Use the generated brand shades to determine whether white or dark text is more readable on the primary color in each mode.\n- In dark mode, prefer a deeper primary step with white text when that profile remains readable; only fall back to lighter primary steps with dark text when white would fail.\n\nUse CSS variables in semantic tokens and dark-mode overrides. Do not hardcode raw `oklch(...)` or hex values in semantic, chart, or map tokens.\n\n## Validation focus\n\nBefore returning:\n\n- confirm only one project file was created or edited\n- confirm the theme key is `theme-<name>` everywhere\n- confirm the file has full light and dark token coverage\n- confirm chart and map tokens remain variable-based\n- confirm dark-mode states remain readable\n\nUse `docs/validation-checklist.md` as the final gate.\n\n## Response shape\n\nMatch the response to the mode:\n\n- Preview: return the CSS first, then the enable snippet if useful\n- Write: return the created path, a short summary, and the enable snippet\n- Validate: list findings first, then the applied or recommended fix\n\nEnable snippet:\n\n```html\n<html data-theme=\"theme-<theme-name>\">\n```\n","skills/theme-generator/examples.md":"# Examples — Preline Theme Generator\n\nThese examples are palette-and-structure references, not literal final-output templates.\n\nImportant: examples below are structural references. Final delivered theme files should follow `docs/final-output-style.md` and should never require edits to any companion project file.\n\nThese examples define what \"good output\" looks like.\n\n## Contents\n\n- [Example 1 — Calm teal SaaS theme](#example-1--calm-teal-saas-theme-full-theme-clean)\n- [Example 2 — Retro sharp corners](#example-2--retro-sharp-corners-behavior-change-must-be-scoped)\n- [Example 3 — Theme-scoped font token](#example-3--theme-scoped-font-token-no-mapping-changes)\n- [Example 4 — Brand match with exact primary color](#example-4--brand-match-with-exact-primary-color)\n\n## Example 1 — Calm teal SaaS theme (full theme, clean)\n\n**Input**\n- themeName: `seafoam`\n- mood: calm, modern SaaS\n- primaryHue: teal (180)\n- contrast: medium\n- style: vibrant\n- darkNeutralFamily: zinc\n\n**Expected Structure**\n- `@theme theme-seafoam inline { }` contains brand + gray palettes\n- Light mode selector: `:root[data-theme=\"theme-seafoam\"], [data-theme=\"theme-seafoam\"]`\n- Dark mode selector: `[data-theme=\"theme-seafoam\"].dark`\n- Full coverage: global surfaces, borders, primary ramp, states, components\n- Dark mode uses Tailwind zinc for neutrals (no matching dark requested)\n\n### Output (truncated)\n\n```css\n/* ------------------------------ */\n/* -------- Seafoam ------------- */\n/* ------------------------------ */\n\n@theme theme-seafoam inline {\n  /* BRAND PALETTE (vibrant) */\n  --color-seafoam-50: oklch(97% 0.08 180);\n  --color-seafoam-100: oklch(94% 0.10 180);\n  --color-seafoam-200: oklch(88% 0.12 180);\n  --color-seafoam-300: oklch(80% 0.14 180);\n  --color-seafoam-400: oklch(70% 0.16 180);\n  --color-seafoam-500: oklch(60% 0.14 180);\n  --color-seafoam-600: oklch(52% 0.12 180);\n  --color-seafoam-700: oklch(44% 0.10 180);\n  --color-seafoam-800: oklch(36% 0.08 180);\n  --color-seafoam-900: oklch(28% 0.06 180);\n  --color-seafoam-950: oklch(20% 0.05 180);\n  \n  /* GRAY PALETTE (bell curve chroma) */\n  --color-seafoam-gray-50: oklch(98% 0.002 180);\n  --color-seafoam-gray-100: oklch(95.5% 0.004 180);\n  --color-seafoam-gray-200: oklch(89.7% 0.008 180);\n  /* ... 300-700 ... */\n  --color-seafoam-gray-800: oklch(34.6% 0.008 180);\n  --color-seafoam-gray-900: oklch(30.6% 0.005 180);\n  --color-seafoam-gray-950: oklch(20.1% 0.003 180);\n\n}\n\n:root[data-theme=\"theme-seafoam\"],\n[data-theme=\"theme-seafoam\"] {\n  /* Global */\n  --background: var(--color-white);\n  --background-1: var(--color-seafoam-gray-50);\n  --foreground: var(--color-seafoam-gray-800);\n  \n  /* Primary ramp */\n  --primary-50: var(--color-seafoam-50);\n  --primary-100: var(--color-seafoam-100);\n  /* ... full ramp ... */\n  \n  /* Primary states */\n  --primary: var(--color-primary-600);\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  \n  /* Components prefer assigned semantic tokens */\n  --navbar: var(--background-plain);\n  --navbar-line: var(--border);\n  /* ... */\n}\n\n[data-theme=\"theme-seafoam\"].dark {\n  /* Dark mode uses Tailwind zinc */\n  --background: var(--color-zinc-800);\n  --background-1: var(--color-zinc-900);\n  --foreground: var(--color-zinc-200);\n  \n  --primary: var(--color-primary-400);\n  --primary-foreground: var(--color-zinc-900); /* Contrast-selected because the generated light-brand states are too light for white */\n  /* ... */\n}\n```\n\n---\n\n## Example 2 — Retro sharp corners (behavior change must be scoped)\n\n**Input**\n- themeName: `arcade`\n- mood: retro 90s\n- primaryHue: magenta (320)\n- contrast: high\n- radiusStyle: `retro-sharp`\n\n**Expected Structure**\n- Full token coverage\n- Theme sets radius tokens in theme selector (NOT in @theme block)\n- Any \"rounded\" behavior override inside `@layer utilities` scoped to theme\n\n### Output (radius section only)\n\n```css\n:root[data-theme=\"theme-arcade\"],\n[data-theme=\"theme-arcade\"] {\n  /* RETRO SHARP CORNERS */\n  --radius-sm: 0px;\n  --radius-md: 2px;\n  --radius-lg: 4px;\n  --radius-xl: 4px;\n  --radius-2xl: 6px;\n  --radius-3xl: 6px;\n  \n  /* ... rest of tokens ... */\n}\n\n/* Optional: Override rounded utility if needed */\n@layer utilities {\n  :is(:root[data-theme=\"theme-arcade\"], [data-theme=\"theme-arcade\"]) .rounded {\n    border-radius: var(--radius-md);\n  }\n}\n```\n\n---\n\n## Example 3 — Theme-scoped font token (no mapping changes)\n\n**Input**\n- themeName: `leafy-modern`\n- mood: organic, clean\n- primaryHue: emerald (145)\n- fontSans: `\"Roboto\", ui-sans-serif, system-ui, sans-serif`\n\n**Expected Structure**\n- Theme sets `--font-sans` inside theme selector (NOT in @theme block)\n- Theme does NOT import Google Fonts (font loading is separate)\n- User enables via `data-theme=\"theme-leafy-modern\"` only\n\n### Output (font section only)\n\n```css\n:root[data-theme=\"theme-leafy-modern\"],\n[data-theme=\"theme-leafy-modern\"] {\n  /* TYPOGRAPHY */\n  --font-sans: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n  \n  /* ... rest of tokens ... */\n}\n```\n\n---\n\n## Example 4 — Brand match with exact primary color\n\n**Input**\n- themeName: `brand-x`\n- primaryColor: `#2F6BFF` (converts to hue ~225)\n- mood: confident, high clarity\n- contrast: medium-high\n\n**Expected Structure**\n- Script converts hex to hue automatically\n- Primary ramp derived coherently from the brand hue\n- `--primary-foreground` readable (white on this blue)\n- Chart `-hex` tokens remain variable-based placeholders in generated source\n\n### Output (chart section showing variable-based chart tokens)\n\n```css\n:root[data-theme=\"theme-brand-x\"],\n[data-theme=\"theme-brand-x\"] {\n  /* ... other tokens ... */\n  \n  /* CHARTS */\n  --chart-primary: var(--color-primary-500);\n  --chart-colors-primary: var(--color-primary-500);\n  --chart-colors-primary-hex: var(--color-primary-500);\n  --chart-colors-primary-hex-inverse: var(--color-primary-300);\n  \n  --chart-1: var(--color-primary-500);\n  --chart-colors-chart-1-hex: var(--color-primary-500);\n  /* ... */\n}\n```\n","src/plugins/accordion/README.md":"# Accordion\n\nBuild vertically collapsing accordions in combination with Accordion JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/accordion) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/accordion.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Accordion component allows you to create collapsible content sections that can be expanded or collapsed by clicking on toggle buttons. It supports multiple accordion items within a group, with options for keeping items open, ensuring at least one item stays open, and treeview mode for hierarchical structures.\n\n**Key Features:**\n- Multiple accordion items in a single group\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Treeview mode support\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Accordion plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n@import \"./node_modules/@preline/accordion/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n\n  new HSAccordion(document.querySelector(\"#accordion\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/accordion` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/accordion\";\n```\n\n`@preline/accordion/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\n\nHSAccordion.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#accordion\");\nif (el) new HSAccordion(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an accordion component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three accordion items within a group, with the first item open by default.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion active\" id=\"hs-unstyled-heading-one\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-unstyled-collapse-one\">\n      Accordion #1\n    </button>\n    <div id=\"hs-unstyled-collapse-one\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-one\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-two\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-two\">\n      Accordion #2\n    </button>\n    <div id=\"hs-unstyled-collapse-two\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-two\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-three\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-three\">\n      Accordion #3\n    </button>\n    <div id=\"hs-unstyled-collapse-three\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-three\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-accordion-group`: Required wrapper container for all accordion items\n- `hs-accordion`: Required container for each accordion item\n- `hs-accordion-toggle`: Required button element that triggers expand/collapse\n- `hs-accordion-content`: Required container for collapsible content\n- Unique `id` attributes for each accordion item and its content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- To have an accordion item open by default, add the `active` class to the `hs-accordion` element\n- For closed items, add the `hidden` class to the `hs-accordion-content` element\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure accordion behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-accordion-always-open` | `hs-accordion-group` | boolean attribute | `false` (absent) | When present (boolean attribute), accordion items stay open when another item is opened. By default, opening one item closes others. Presence of attribute = `true`, absence = `false`. |\n\n**Example:**\n```html\n<!-- Boolean attribute: presence = true, absence = false -->\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <!-- Multiple items can be open simultaneously -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `--stop-propagation:*` | `hs-accordion-toggle` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, interrupts event propagation when toggle button is clicked. Useful when accordion is nested inside clickable elements. |\n| `--keep-one-open:*` | `hs-accordion-group` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, ensures at least one accordion item remains open at all times. Prevents closing the last open item. |\n\n**Example:**\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <!-- At least one item will always be open -->\n</div>\n\n<button class=\"hs-accordion-toggle --stop-propagation:true\">\n  <!-- Click event won't bubble up -->\n</button>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the accordion to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-group` | Container wrapper | Groups multiple accordion items together |\n| `hs-accordion` | Each accordion item container | Identifies an individual accordion item |\n| `hs-accordion-toggle` | Button element | The clickable element that expands/collapses content |\n| `hs-accordion-content` | Content container | The collapsible content area |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-treeview-root` | Parent container | When present, activates treeview mode for hierarchical accordion structures |\n| `hs-accordion-selectable` | Elements inside treeview | When treeview mode is active, elements with this class become selectable |\n\n**Treeview Mode Example:**\n```html\n<div class=\"hs-accordion-treeview-root\">\n  <div class=\"hs-accordion-group\">\n    <div class=\"hs-accordion\">\n      <button class=\"hs-accordion-toggle\">Parent</button>\n      <div class=\"hs-accordion-content\">\n        <div class=\"hs-accordion-selectable\">Selectable item</div>\n      </div>\n    </div>\n  </div>\n</div>\n```\n\n## JavaScript API\n\nThe `HSAccordion` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an accordion instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed accordion item. Returns `false` if item is already open, `true` otherwise. If other items are open and `always-open` is not set, they will be closed. |\n| `hide()` | None | `boolean` | Collapses the currently open accordion item. Returns `false` if item is already closed or if `keep-one-open` is enabled and this is the last open item, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the accordion instance, removes all generated markup, classes, and event listeners. Use when removing accordion from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSAccordion` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSAccordion.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSAccordion } \\| HTMLElement \\| null` | Returns the accordion instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSAccordion` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if accordion instance is not found. |\n| `HSAccordion.show(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. Only opens if item is not already open. |\n| `HSAccordion.hide(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. Only closes if item is open and `keep-one-open` allows it. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new accordion instance\nconst accordion = new HSAccordion(document.querySelector('#hs-accordion'));\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion programmatically\nshowBtn.addEventListener('click', () => {\n  accordion.show();\n});\n\n// Close accordion programmatically\nhideBtn.addEventListener('click', () => {\n  accordion.hide();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSAccordion.show('#hs-accordion');\n});\n\n// Close accordion using static method\nhideBtn.addEventListener('click', () => {\n  HSAccordion.hide('#hs-accordion');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSAccordion instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeAccordion() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying accordion instance**\n```javascript\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-accordion').remove();\n  });\n}\n```\n\n## Events\n\nAccordion instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeOpen` | Before accordion item opens | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts opening. Can be used to cancel or modify behavior. |\n| `on:open` | After accordion item opens | `HTMLElement` (current accordion element) | Fires after the accordion item has fully opened. Use for post-open actions like loading content. |\n| `on:beforeClose` | Before accordion item closes | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts closing. Can be used to cancel or modify behavior. |\n| `on:close` | After accordion item closes | `HTMLElement` (current accordion element) | Fires after the accordion item has fully closed. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (accordionElement) => {\n    console.log('Accordion opened:', accordionElement);\n    // Perform actions after accordion opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (accordionElement) => {\n    console.log('Accordion closed:', accordionElement);\n    // Perform cleanup or state updates\n  });\n\n  // Listen to beforeOpen event (can be used to cancel)\n  element.on('beforeOpen', (accordionElement) => {\n    console.log('About to open:', accordionElement);\n    // Can add conditional logic here\n  });\n\n  // Listen to beforeClose event (can be used to cancel)\n  element.on('beforeClose', (accordionElement) => {\n    console.log('About to close:', accordionElement);\n    // Can add conditional logic here\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Multiple Open Items\n\nAllow multiple accordion items to be open simultaneously.\n\n```html\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <div class=\"hs-accordion\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content hidden\">Content 1</div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: Always Keep One Open\n\nEnsure at least one item is always open.\n\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <div class=\"hs-accordion active\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content\">Content 1</div>\n  </div>\n  \n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl accordion from external buttons.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion\" id=\"hs-accordion-first\">\n    <button class=\"hs-accordion-toggle\">Content</button>\n    <div class=\"hs-accordion-content hidden\">Content here</div>\n  </div>\n</div>\n\n<button id=\"hs-open-all\">Open All</button>\n<button id=\"hs-close-all\">Close All</button>\n\n<script>\n  const instance = HSAccordion.getInstance('#hs-accordion-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-all').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-close-all').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/carousel/README.md":"# Carousel\n\nBuild carousels and sliders with Carousel JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/carousel) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/carousel.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Carousel component provides a flexible, feature-rich slider for displaying multiple items in a scrollable container. It supports autoplay, infinite loop, drag gestures, responsive breakpoints, and multiple display modes.\n\n**Key Features:**\n- Multiple slides in a scrollable container\n- Autoplay and infinite loop support\n- Drag/swipe gestures for navigation\n- Responsive breakpoints for different screen sizes\n- Multiple display modes (default, snap, bounded)\n- Programmatic control via JavaScript API\n- Keyboard navigation support\n- Customizable pagination dots\n\n## Installation\n\nTo get started, install Carousel plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/carousel\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/carousel */\n@source \"../node_modules/@preline/carousel/*.js\";\n@import \"./node_modules/@preline/carousel/variants.css\";\n@import \"./node_modules/@preline/carousel/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/carousel/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCarousel from \"@preline/carousel/non-auto.mjs\";\n\n  new HSCarousel(document.querySelector(\"#carousel\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/carousel` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/carousel\";\n```\n\n`@preline/carousel/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCarousel from \"@preline/carousel/non-auto\";\n\nHSCarousel.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#carousel\");\nif (el) new HSCarousel(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a carousel component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The carousel displays three slides with previous/next navigation buttons.\n\n```html\n<div data-hs-carousel='{\n    \"loadingClasses\": \"opacity-0\"\n  }' class=\"relative\">\n  <div class=\"hs-carousel relative overflow-hidden w-full min-h-64\">\n    <div class=\"hs-carousel-body absolute top-0 bottom-0 start-0 flex flex-nowrap transition-transform duration-700 opacity-0\">\n      <div class=\"hs-carousel-slide\">\n        1\n      </div>\n      <div class=\"hs-carousel-slide\">\n        2\n      </div>\n      <div class=\"hs-carousel-slide\">\n        3\n      </div>\n    </div>\n  </div>\n\n  <button type=\"button\" class=\"hs-carousel-prev\">\n    <span aria-hidden=\"true\">«</span>\n    <span class=\"sr-only\">Previous</span>\n  </button>\n  <button type=\"button\" class=\"hs-carousel-next\">\n    <span class=\"sr-only\">Next</span>\n    <span aria-hidden=\"true\">»</span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-carousel`: Required on the container element, contains configuration options\n- `hs-carousel`: Required wrapper for slides collection, must contain `overflow-hidden` class\n- `hs-carousel-body`: Required slides container\n- `hs-carousel-slide`: Required for each slide element\n- `hs-carousel-prev`: Optional previous slide button\n- `hs-carousel-next`: Optional next slide button\n- `hs-carousel-pagination`: Optional pagination container\n\n**Initial State:**\n- Carousel body typically starts with `opacity-0` which is removed after loading\n- First slide is active by default (index 0)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-carousel` attribute as a JSON object.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `currentIndex` | number | `0` | Specifies the index of the current slide initially (from 0 to slides quantity). |\n| `loadingClasses` | string | `\"opacity-0\"` | Specifies which classes should be removed after the carousel is loaded. CSS classes should be separated with space. |\n| `dotsItemClasses` | string | - | Specifies which classes will be added to the point elements (which are generated automatically). CSS classes should be separated with space. |\n| `isAutoHeight` | boolean | `false` | Sets the height of the carousel to the height of the current slide. |\n| `isAutoPlay` | boolean | `false` | Enables autoplay. Slides advance automatically at the interval specified by `speed`. |\n| `isInfiniteLoop` | boolean | `true` | Enables infinite loop. When reaching the last slide, it wraps to the first slide. |\n| `isCentered` | boolean | `false` | Enables centered mode. This mode adds space on the sides to center the slides. |\n| `isSnap` | boolean | `false` | Enables a mode in which you can scroll the contents of the slider using the scroll along the x-axis, while the active slider is centered relative to the slider. |\n| `isScrollBlocked` | boolean | `false` | Prevents the user from scrolling the slider. Useful when you need to prevent scrolling while the carousel is active. |\n| `isDraggable` | boolean | `false` | Adds the ability to change slides using dragging. Requires adding `hs-carousel:dragging:transition-none` class to the `hs-carousel-body`. Not working with `isSnap: true` option. |\n| `isRTL` | boolean | `false` | Turns on RTL (right-to-left) mode. |\n| `hasSnapSpacers` | boolean | `true` | Adds additional elements to create a constant centering effect. |\n| `slidesQty` | object \\| number | `1` | Allows to set the number of slides to display at a certain screen resolution if passed as an object (e.g., `{\"xs\": 1, \"md\": 2, \"lg\": 3}`). If passed as a number, the specified number of slides will be displayed for all screen resolutions. |\n| `slideBy` | object \\| number | `1` | Specifies how many slides to move when navigating through the carousel. Can be a number for all screen sizes or an object with breakpoint-specific values. |\n| `speed` | number | `4000` | Autoplay animation speed in milliseconds. Available if `isAutoplay: true`. |\n| `updateDelay` | number | `0` | Allows you to delay the update function when resizing a window. Suitable for a slider with images, for more accurate calculation of the size of the images. |\n| `mode` | `\"default\"` \\| `\"snap\"` \\| `\"bounded\"` | `\"default\"` | Defines the carousel's behavior mode. `default` for standard sliding, `snap` for scroll-based navigation with snap points, and `bounded` for a fixed-width container with responsive slides. |\n| `boundedOptions` | object | `null` | Configuration options for the bounded mode carousel. Includes `maxWidth` to set the maximum width of the carousel container, `slidesGap` to define spacing between slides, and `spacersWidth` to control the width of side spacers. When `spacersWidth` is set to `\"auto\"`, it automatically calculates equal spacing on both sides. |\n\n**Example:**\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 3000,\n  \"slidesQty\": {\"xs\": 1, \"md\": 2, \"lg\": 3},\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the carousel to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel` | Wrapper container | A wrapper for a collection of slides. Must contain the `overflow-hidden` class for correct functionality. |\n| `hs-carousel-body` | Slides container | Container for all slide elements. |\n| `hs-carousel-slide` | Each slide element | Identifies an individual slide. |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel-prev` | Previous button | Previous slide navigation button. |\n| `hs-carousel-next` | Next button | Next slide navigation button. |\n| `hs-carousel-pagination` | Pagination container | Pagination container. If it doesn't contain nested elements, dots are generated automatically. If it contains elements with `hs-carousel-pagination-item` class, they will act as dots. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-carousel-active:*` | A modifier that allows you to set Tailwind classes when the active slide was shown. |\n| `hs-carousel-disabled:*` | A modifier that allows you to set Tailwind classes for arrow buttons when they are `disabled`. |\n| `hs-carousel:dragging:*` | A modifier that allows you to set Tailwind classes for `hs-carousel-body` when dragging. |\n\n## JavaScript API\n\nThe `HSCarousel` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a carousel instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `goToPrev()` | None | `void` | Navigates to the previous slide. |\n| `goToNext()` | None | `void` | Navigates to the next slide. |\n| `goTo(index)` | `index`: `number` | `void` | Navigates to the slide at the specified index. Index starts from 0. |\n| `recalculateWidth()` | None | `void` | Recalculates the width of the carousel. Useful after dynamically adding or removing slides, or when window is resized. |\n| `destroy()` | None | `void` | Destroys the carousel instance, removes all generated markup, classes, and event listeners. Use when removing carousel from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCarousel` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCarousel.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCarousel \\| { id: string \\| number, element: HSCarousel } \\| null` | Returns the carousel instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCarousel` instance. If `isInstance` is `false` or omitted, returns the `HSCarousel` instance directly. Returns `null` if carousel instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new carousel instance\nconst carousel = new HSCarousel(document.querySelector('#hs-carousel'));\nconst goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n\n// Navigate to slide by index\ngoToSecondBtn.addEventListener('click', () => {\n  carousel.goTo(1); // Go to second slide (index 1)\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCarousel instance\n  const goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n  const prevBtn = document.querySelector('#hs-prev-btn');\n  const nextBtn = document.querySelector('#hs-next-btn');\n\n  // Navigate to specific slide\n  goToSecondBtn.addEventListener('click', () => {\n    element.goTo(1);\n  });\n\n  // Navigate to previous slide\n  prevBtn.addEventListener('click', () => {\n    element.goToPrev();\n  });\n\n  // Navigate to next slide\n  nextBtn.addEventListener('click', () => {\n    element.goToNext();\n  });\n\n  // Recalculate width after dynamic changes\n  function updateCarousel() {\n    element.recalculateWidth();\n  }\n\n  // Clean up when removing from DOM\n  function removeCarousel() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying carousel instance**\n```javascript\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-carousel').remove();\n  });\n}\n```\n\n## Events\n\nCarousel instances emit events that can be listened to for slide lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | After the active slide changes | `number` (zero-based index of the current slide) | Fires after navigating to the next or previous slide, or after `goTo()` is called. |\n\n### Event Usage Example\n\n```javascript\n// Get carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for slide changes\n  element.on('update', (currentIndex) => {\n    console.log('Active slide:', currentIndex);\n    // Update external indicators, counters, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Responsive Carousel\n\nDisplay different number of slides at different breakpoints.\n\n```html\n<div data-hs-carousel='{\n  \"slidesQty\": {\"xs\": 1, \"sm\": 2, \"md\": 3, \"lg\": 4}\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 2: Autoplay Carousel\n\nAutomatically advance slides at regular intervals.\n\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 5000,\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl carousel from external buttons.\n\n```html\n<div id=\"hs-carousel-first\" data-hs-carousel>\n  <!-- Carousel content -->\n</div>\n\n<button id=\"hs-go-to-first\">Go to First</button>\n<button id=\"hs-go-to-last\">Go to Last</button>\n\n<script>\n  const instance = HSCarousel.getInstance('#hs-carousel-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-go-to-first').addEventListener('click', () => {\n      element.goTo(0);\n    });\n\n    document.querySelector('#hs-go-to-last').addEventListener('click', () => {\n      // Assuming 5 slides (indices 0-4)\n      element.goTo(4);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/collapse/README.md":"# Collapse\n\nToggle the visibility of content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/collapse) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/collapse.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Collapse component allows you to show and hide content with smooth transitions. It's a simple, lightweight solution for creating expandable/collapsible sections without the complexity of accordion groups.\n\n**Key Features:**\n- Simple show/hide functionality\n- Smooth CSS transitions\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Collapse plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/collapse\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/collapse */\n@source \"../node_modules/@preline/collapse/*.js\";\n@import \"./node_modules/@preline/collapse/variants.css\";\n@import \"./node_modules/@preline/collapse/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/collapse/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCollapse from \"@preline/collapse/non-auto.mjs\";\n\n  new HSCollapse(document.querySelector(\"#collapse\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/collapse` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/collapse\";\n```\n\n`@preline/collapse/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCollapse from \"@preline/collapse/non-auto\";\n\nHSCollapse.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#collapse\");\nif (el) new HSCollapse(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a collapse component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The content is hidden by default and expands when the button is clicked.\n\n```html\n<button type=\"button\" class=\"hs-collapse-toggle\" id=\"hs-unstyled-collapse\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-heading\" data-hs-collapse=\"#hs-unstyled-collapse-heading\" role=\"region\">\n  Collapse\n</button>\n<div id=\"hs-unstyled-collapse-heading\" class=\"hs-collapse hidden w-full overflow-hidden transition-[height] duration-300\" aria-labelledby=\"hs-unstyled-collapse\">\n  This is a collapse body. The content expands and collapses when the toggle button is clicked, with smooth height transitions.\n</div>\n```\n\n**Structure Requirements:**\n- `hs-collapse-toggle`: Required button element that triggers the collapse\n- `hs-collapse`: Required container for collapsible content\n- `data-hs-collapse`: Required attribute on the toggle button, must be a valid CSS selector pointing to the collapse content\n- Unique `id` attributes for the toggle button and collapse content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the collapse content initially\n- Content visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure collapse behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-collapse` | `hs-collapse-toggle` (button) | string (CSS selector) | `null` | Collapse container selector. Must be a valid CSS selector pointing to the `hs-collapse` element. |\n\n**Example:**\n```html\n<button data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the collapse to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-collapse` | Content container | Identifies the collapsible content area |\n| `hs-collapse-toggle` | Button element | The clickable element that expands/collapses content |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-collapse-open:*` | A modifier that allows you to set Tailwind classes when the collapse is open. |\n\n## JavaScript API\n\nThe `HSCollapse` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a collapse instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed content. Returns `false` if content is already open, `true` otherwise. |\n| `hide()` | None | `boolean` | Collapses the currently open content. Returns `false` if content is already closed, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the collapse instance, removes all generated markup, classes, and event listeners. Use when removing collapse from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCollapse` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCollapse.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSCollapse } \\| null` | Returns the collapse instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCollapse` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if collapse instance is not found. |\n| `HSCollapse.show(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSCollapse.hide(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new collapse instance\nconst collapse = new HSCollapse(document.querySelector('#hs-collapse'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse programmatically\nshowBtn.addEventListener('click', () => {\n  collapse.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSCollapse.show('#hs-collapse');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCollapse instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeCollapse() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying collapse instance**\n```javascript\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-collapse').remove();\n  });\n}\n```\n\n## Events\n\nCollapse instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After collapse content is opened | `HTMLElement` (collapse element) | Fires after the collapse content has been displayed. Use for post-open actions like loading content. |\n| `on:hide` | After collapse content is closed | `HTMLElement` (collapse element) | Fires after the collapse content has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (collapseElement) => {\n    console.log('Collapse opened:', collapseElement);\n    // Perform actions after collapse opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to hide event\n  element.on('hide', (collapseElement) => {\n    console.log('Collapse closed:', collapseElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Programmatic Control\n\nControl collapse from external buttons.\n\n```html\n<button class=\"hs-collapse-toggle\" data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n\n<button id=\"hs-show-collapse\">Show</button>\n<button id=\"hs-hide-collapse\">Hide</button>\n\n<script>\n  const instance = HSCollapse.getInstance('#hs-collapse-content', true);\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-collapse').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-collapse').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/combobox/README.md":"# ComboBox\n\nDynamic suggestions as users type in an input field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/combobox) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/combobox.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe ComboBox component provides an autocomplete input field with dynamic suggestions. It supports both client-side filtering and API-based data fetching, customizable templates, and flexible data rendering options.\n\n**Key Features:**\n- Dynamic autocomplete suggestions\n- Client-side and API-based data fetching\n- Customizable item templates\n- Keyboard navigation support\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install ComboBox plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/combobox\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/combobox */\n@source \"../node_modules/@preline/combobox/*.js\";\n@import \"./node_modules/@preline/combobox/variants.css\";\n@import \"./node_modules/@preline/combobox/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/combobox/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCombobox from \"@preline/combobox/non-auto.mjs\";\n\n  new HSCombobox(document.querySelector(\"#combobox\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/combobox` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/combobox\";\n```\n\n`@preline/combobox/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCombobox from \"@preline/combobox/non-auto\";\n\nHSCombobox.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#combobox\");\nif (el) new HSCombobox(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a combobox component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The combobox shows suggestions as the user types.\n\n```html\n<div class=\"inline-flex relative\" data-hs-combo-box>\n  <div class=\"relative\">\n    <input type=\"text\" role=\"combobox\" aria-expanded=\"false\" value=\"\" data-hs-combo-box-input />\n  </div>\n  \n  <div class=\"absolute z-50\" style=\"display: none;\" data-hs-combo-box-output>\n    <div class=\"cursor-pointer\" tabindex=\"0\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"1\" data-hs-combo-box-value>1</span>\n    </div>\n    <div class=\"cursor-pointer\" tabindex=\"1\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"2\" data-hs-combo-box-value>2</span>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-combo-box`: Required on the container element\n- `data-hs-combo-box-input`: Required on the input element\n- `data-hs-combo-box-output`: Required on the dropdown container\n- `data-hs-combo-box-output-item`: Required on each suggestion item\n- `data-hs-combo-box-search-text`: Required on the searchable text element within each item\n- Proper ARIA attributes (`role=\"combobox\"`, `aria-expanded`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the input initially\n- Add `style=\"display: none;\"` to the output container initially\n- Output visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-combo-box` attribute and various data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-combo-box` | Container | - | - | Activate a ComboBox by specifying on an element. Should be added to the container. |\n| `:gap` | Inside `data-hs-combo-box` | number | `5` | Determines the indent from the input field to the dropdown list in pixels. |\n| `:viewport` | Inside `data-hs-combo-box` | string \\| HTMLElement | `null` | Determines which element will be the parent when calculating the position of the dropdown list. By default it is calculated relative to the document. |\n| `:minSearchLength` | Inside `data-hs-combo-box` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:apiUrl` | Inside `data-hs-combo-box` | string \\| null | `null` | Specifies the API address for fetching suggestions. |\n| `:apiDataPart` | Inside `data-hs-combo-box` | string \\| null | `null` | Determines which part of the returned data will be taken as the main data array. If this parameter is empty, then the root is taken as a basis. |\n| `:apiQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request parameters, such as limit. |\n| `:apiSearchQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines the name of the search parameter. |\n| `:apiHeaders` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request headers. |\n| `:apiSearchPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that specifies the endpoint path for the API request, excluding the base URL. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:apiSearchDefaultPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that defines the default API endpoint path to be used when the input field is empty. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:outputItemTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for a single item in a dropdown list. |\n| `:outputEmptyTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for an empty placeholder. |\n| `:outputLoaderTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for the loader when loading data. |\n| `:preventAutoPosition` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it disables dropdown auto-positioning. |\n| `:preventClientFiltering` | Inside `data-hs-combo-box` | boolean | `true` (if `:apiSearchQuery` is not empty) | If `true`, it disables client-side filtering. |\n| `:keepOriginalOrder` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it keeps the original order of items. |\n| `:preserveSelectionOnEmpty` | Inside `data-hs-combo-box` | boolean | `true` | If `true`, it preserves the selected value when the input is empty. |\n| `data-hs-combo-box-input` | Input element | - | - | Determines which element (input) inside the initialized container will be responsible for entering data. |\n| `data-hs-combo-box-output` | Output container | - | - | Determines which element inside the initialized container will be responsible for data output. |\n| `data-hs-combo-box-toggle` | Toggle element | - | - | Defines an element inside the initialized container that will be responsible for hiding/showing the dropdown list. |\n| `data-hs-combo-box-close` | Close element | - | - | Defines an element inside the initialized container that will be responsible for hiding the dropdown list. |\n| `data-hs-combo-box-open` | Open element | - | - | Defines an element inside the initialized container that will be responsible for showing the dropdown list. |\n| `data-hs-combo-box-output-item` | Item element | - | - | Defines an element inside the initialized container that will be responsible for displaying each individual data element. |\n| `data-hs-combo-box-search-text` | Search text element | - | - | Defines the element within which text will be searched. |\n| `data-hs-combo-box-value` | Value element | - | - | Defines an element whose text will be set as a value when selected. |\n| `data-hs-combo-box-output-item-field` | Item field | string \\| string[] | - | Defines a data field that will be taken to fill the element with text. |\n| `data-hs-combo-box-output-item-attr` | Item attribute | - | - | Defines the attributes that will be filled with the corresponding data. For example, the src attribute of an image. |\n| `:valueFrom` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the field from which data will be taken. |\n| `:attr` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the attribute where the data will be supplied. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-combo-box-active:*` | A modifier that allows you to set Tailwind classes when ComboBox has been opened. |\n| `hs-combo-box-has-value:*` | A modifier that allows you to set Tailwind classes when ComboBox's input has value. |\n| `hs-combo-box-selected:*` | A modifier that allows you to set Tailwind classes when `data-hs-combo-box-output-item` has been selected. |\n| `hs-combo-box-tab-active:*` | A modifier that allows you to set Tailwind classes to the active group item (tab) when `groupingType` is `tabs`. |\n\n## JavaScript API\n\nThe `HSComboBox` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a combobox instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentData()` | None | `object \\| object[]` | Returns the data of the selected element as an object. This data must first be assigned as a value to the data parameter `data-hs-combo-box-item-stored-data` when standard rendering. When rendering via the API, the data is automatically substituted into the data attribute. |\n| `setCurrent()` | None | `void` | Sets the current selection based on the input value. |\n| `open(val)` | `val`: `string` (optional) | `boolean` | Opens the autosuggestion list. Optionally accepts a value to set in the input. Returns `true` if opened successfully, `false` otherwise. |\n| `close(val, data)` | `val`: `string \\| null` (optional)<br>`data`: `object \\| null` (optional) | `boolean` | Closes the autosuggestion list. Optionally accepts a value and data to set. Returns `true` if closed successfully, `false` otherwise. |\n| `recalculateDirection()` | None | `void` | Forces recalculation of autosuggestion list positions. Useful after window resize or layout changes. |\n| `destroy()` | None | `void` | Destroys the combobox instance, removes all generated markup, classes, and event listeners. Use when removing combobox from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSComboBox` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSComboBox.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSComboBox \\| { id: string \\| number, element: HSComboBox } \\| null` | Returns the combobox instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSComboBox` instance. If `isInstance` is `false` or omitted, returns the `HSComboBox` instance directly. Returns `null` if combobox instance is not found. |\n| `HSComboBox.autoInit()` | None | `void` | Reinitializes all ComboBoxes on the page. Useful after dynamically adding comboboxes to DOM. |\n| `HSComboBox.close(target)` | `target`: `HTMLElement \\| string` (CSS selector) | `void` | Closes the combobox identified by target. Accepts a DOM element or CSS selector string. |\n| `HSComboBox.closeCurrentlyOpened()` | None | `void` | Closes an already open ComboBox. Useful for closing any open combobox before opening a new one. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new combobox instance\nconst comboBox = new HSComboBox(document.querySelector('#hs-combo-box'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open combobox programmatically\nopenBtn.addEventListener('click', () => {\n  comboBox.open();\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the combobox instance\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSComboBox instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Get selected data\n  const selectedData = element.getCurrentData();\n  console.log('Selected:', selectedData);\n\n  // Clean up when removing from DOM\n  function removeComboBox() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying combobox instance**\n```javascript\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-combo-box').remove();\n  });\n}\n```\n\n## Events\n\nCombobox instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:select` | When an item is selected from the dropdown | `object` (the selected item data object) | Fires after the user clicks an option in the combobox dropdown. |\n\n### Event Usage Example\n\n```javascript\n// Get combobox instance\nconst instance = HSCombobox.getInstance('#hs-combobox', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for item selection\n  element.on('select', (data) => {\n    console.log('Selected item:', data);\n    // Use selected data to update other parts of the UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: API-based ComboBox\n\nFetch suggestions from an API endpoint.\n\n```html\n<div data-hs-combo-box='{\n  \"apiUrl\": \"https://api.example.com/search\",\n  \"apiSearchQuery\": \"q\",\n  \"minSearchLength\": 2\n}'>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl combobox from external buttons.\n\n```html\n<div id=\"hs-combo-box-first\" data-hs-combo-box>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n\n<button id=\"hs-open-combo-box\">Open</button>\n<button id=\"hs-close-combo-box\">Close</button>\n\n<script>\n  const instance = HSComboBox.getInstance('#hs-combo-box-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-combo-box').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-combo-box').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/copy-markup/README.md":"# Copy Markup\n\nCopy input, select or other markups.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/copy-markup) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/copy-markup.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field.\n\n**Key Features:**\n- One-click element duplication\n- Configurable copy limits\n- Automatic element management\n- Programmatic control via JavaScript API\n- Event system for copy/delete tracking\n- Support for any HTML markup\n\n## Installation\n\nTo get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/copy-markup\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/copy-markup */\n@import \"./node_modules/@preline/copy-markup/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/copy-markup/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCopyMarkup from \"@preline/copy-markup/non-auto.mjs\";\n\n  new HSCopyMarkup(document.querySelector(\"#copy-markup\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/copy-markup` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/copy-markup\";\n```\n\n`@preline/copy-markup/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCopyMarkup from \"@preline/copy-markup/non-auto\";\n\nHSCopyMarkup.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#copy-markup\");\nif (el) new HSCopyMarkup(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element.\n\n```html\n<div id=\"hs-wrapper-for-copy-first\">\n  <input id=\"hs-content-for-copy-first\" type=\"text\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500\" placeholder=\"Enter Name\">\n</div>\n\n<button type=\"button\" data-hs-copy-markup='{\n    \"targetSelector\": \"#hs-content-for-copy-first\",\n    \"wrapperSelector\": \"#hs-wrapper-for-copy-first\",\n    \"limit\": 3\n  }' id=\"hs-copy-content-first\">\n  Add Name\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-copy-markup`: Required on the trigger button, contains configuration options as JSON\n- `targetSelector`: Required, must be a valid CSS selector pointing to the element to copy\n- `wrapperSelector`: Required, must be a valid CSS selector pointing to the container where copies will be added\n- Target element must exist in the DOM\n\n**Initial State:**\n- One instance of the target element exists\n- Copy button is enabled (unless limit is reached)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-copy-markup` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-copy-markup` | Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). |\n| `:targetSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. |\n| `:wrapperSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. |\n| `:limit` | Inside `data-hs-copy-markup` | number | `null` | Specifies how many items you can copy until the button is disabled. `null` means no limit. |\n\n**Example:**\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-input-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field\n</button>\n```\n\n## JavaScript API\n\nThe `HSCopyMarkup` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a copy markup instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `delete(target)` | `target`: `HTMLElement` | `void` | Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. |\n| `destroy()` | None | `void` | Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCopyMarkup` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCopyMarkup.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCopyMarkup \\| { id: string \\| number, element: HSCopyMarkup } \\| null` | Returns the copy markup instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCopyMarkup` instance. If `isInstance` is `false` or omitted, returns the `HSCopyMarkup` instance directly. Returns `null` if copy markup instance is not found. |\n\n### Usage Examples\n\n**Example 1: Deleting copied elements**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const deleteBtn = document.querySelector('#hs-delete-btn');\n\n  deleteBtn.addEventListener('click', () => {\n    // Delete a specific copied element\n    const copiedElement = document.querySelector('#hs-copy-markup-item-first');\n\n    if (copiedElement) {\n      element.delete(copiedElement);\n    }\n  });\n}\n```\n\n**Example 2: Using instance methods (recommended pattern)**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Items:', element.items);\n  console.log('Limit:', element.limit);\n\n  // Delete a copied element programmatically\n  function removeCopiedItem(itemElement) {\n    element.delete(itemElement);\n  }\n\n  // Clean up when removing from DOM\n  function removeCopyMarkup() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying copy markup instance**\n```javascript\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-copy-content').remove();\n  });\n}\n```\n\n## Events\n\nCopy markup instances emit events that can be listened to for copy/delete tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:copy` | When target element was copied | `HTMLElement` (copied element) | Fires when a new copy of the target element is created. Returns the newly created element. |\n| `on:delete` | When target element was deleted | `HTMLElement` (deleted element) | Fires when a copied element is deleted. Returns the deleted element. |\n\n### Event Usage Example\n\n```javascript\n// Get copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to copy event\n  element.on('copy', (copiedElement) => {\n    console.log('Element copied:', copiedElement);\n    // Perform actions after element is copied\n    // e.g., initialize other plugins on the new element, update counters\n  });\n\n  // Listen to delete event\n  element.on('delete', (deletedElement) => {\n    console.log('Element deleted:', deletedElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Limited Copies\n\nLimit the number of copies that can be created.\n\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field (Max 5)\n</button>\n```\n\n### Pattern 2: Dynamic Form Fields\n\nCreate dynamic form fields with copy functionality.\n\n```html\n<div id=\"hs-fields-wrapper\">\n  <input id=\"hs-field-template\" type=\"text\" placeholder=\"Field name\">\n</div>\n\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field-template\",\n  \"wrapperSelector\": \"#hs-fields-wrapper\"\n}'>\n  Add Another Field\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/datatable/README.md":"# Datatables\n\nDatatables solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datatable) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/datatables.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [CSS](#css)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Datatables component provides a powerful data table interface with sorting, searching, pagination, and row selection capabilities. It integrates with DataTables.net for advanced table functionality.\n\n**Key Features:**\n- Column sorting\n- Search functionality\n- Pagination\n- Row selection (single and multiple)\n- Customizable paging controls\n- Integration with DataTables.net\n- Programmatic control via JavaScript API\n- Event system for table interactions\n\n## Installation\n\nTo get started, install DataTable plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i jquery datatables.net-dt @preline/datatable\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/datatable */\n@source \"../node_modules/@preline/datatable/*.js\";\n@import \"./node_modules/@preline/datatable/variants.css\";\n@import \"./node_modules/@preline/datatable/theme.css\";\n\n.dt-layout-row:has(.dt-search),\n.dt-layout-row:has(.dt-length),\n.dt-layout-row:has(.dt-paging) {\n  display: none !important;\n}\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script src=\"./node_modules/@preline/datatable/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script type=\"module\">\n  import HSDataTable from \"@preline/datatable/non-auto.mjs\";\n\n  new HSDataTable(document.querySelector(\"#datatable\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `jQuery` and `DataTable` are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/datatable` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport \"@preline/datatable\";\n```\n\n`@preline/datatable/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport HSDataTable from \"@preline/datatable/non-auto\";\n\nHSDataTable.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datatable\");\nif (el) new HSDataTable(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a datatable component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The table supports sorting, searching, and pagination.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\">\n          Name\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\">\n          Age\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\" class=\"--exclude-from-ordering\">\n          Action\n        </th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>John Brown</td>\n        <td>45</td>\n        <td>\n          <button type=\"button\">Delete</button>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n\n  <div style=\"display: none;\" data-hs-datatable-paging>\n    <button type=\"button\" data-hs-datatable-paging-prev>\n      <span aria-hidden=\"true\">«</span>\n      <span class=\"sr-only\">Previous</span>\n    </button>\n    <div data-hs-datatable-paging-pages></div>\n    <button type=\"button\" data-hs-datatable-paging-next>\n      <span class=\"sr-only\">Next</span>\n      <span aria-hidden=\"true\">»</span>\n    </button>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-datatable`: Required on the container element\n- Standard HTML `<table>` structure with `<thead>` and `<tbody>`\n- Optional paging controls with `data-hs-datatable-paging`\n- Optional search input with `data-hs-datatable-search`\n- Optional row selection checkboxes\n\n**Initial State:**\n- Table displays all data\n- Sorting is enabled on columns (unless excluded)\n- Pagination controls are hidden if not needed\n\n## CSS\n\nPlease add the following CSS to the head. This is necessary because Datatables.net does not allow to add custom markup with options disabled, such as `paging: false` or `searching: false`.\n\n```html\n<style>\n  .dt-layout-row:has(.dt-search),\n  .dt-layout-row:has(.dt-length),\n  .dt-layout-row:has(.dt-paging) {\n    display: none !important;\n  }\n</style>\n```\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-datatable` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datatable` | Container | - | - | Activate a Datatable by specifying on an element. Should be added to the container. |\n| `:pageBtnClasses` | Inside `data-hs-datatable` | string | `\"-\"` | Sets the specified classes to each paging element inside the `data-hs-datatable-paging-pages`. |\n| `:rowSelectingOptions.selectAllSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-all]\"` | A valid CSS selector (the element must be a checkbox or radio button), when the element is changed, all elements specified by `rowSelectingOptions.individualSelector` (or, if not specified, then `data-hs-datatable-row-selecting-individual`) on the current page will be changed. |\n| `:rowSelectingOptions.individualSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-individual]\"` | A valid CSS selector (the element must be a checkbox or radio button), affects the change of the element specified in `rowSelectingOptions.selectAllSelector`, if not all elements are selected on the current page, then it switches to `unchecked` state, if vice versa, then in `checked`. |\n\n**Example:**\n```html\n<div data-hs-datatable='{\n  \"pageBtnClasses\": \"px-3 py-1 border rounded\",\n  \"rowSelectingOptions\": {\n    \"selectAllSelector\": \"#hs-select-all\",\n    \"individualSelector\": \".hs-row-checkbox\"\n  }\n}'>\n  <!-- Table structure -->\n</div>\n```\n\n### Class Options\n\nCSS class modifiers use `--` prefix to control datatable behavior.\n\n| Class Modifier | Target Element | Purpose |\n| --- | --- | --- |\n| `--exclude-from-ordering` | Table header (`<th>`) | A modifier that allows you to disable the ordering functionality in a column. Should be added to the thead column. |\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for specific features to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-datatable-search` | Input element | Defines which element will be responsible for searching in the datatable (must be an `input`). |\n| `data-hs-datatable-page-entities` | Select element | Determines which element will be responsible for switching the number of elements on the page (must be a `select`, depends on the change event). |\n| `data-hs-datatable-paging` | Container element | Defines a paging container, that contains pagination, next and previous buttons. |\n| `data-hs-datatable-paging-prev` | Button element | Defines a previous button element. |\n| `data-hs-datatable-paging-next` | Button element | Defines a next button element. |\n| `data-hs-datatable-row-selecting-all` | Checkbox/Radio element | Defines a \"Select all\" checkbox/radio, if it's not specified in the `rowSelectingOptions.selectAllSelector` parameter. |\n| `data-hs-datatable-row-selecting-individual` | Checkbox/Radio element | Defines a checkbox/radio that will be responsible for selecting a specific row if it is not specified in the `rowSelectingOptions.individualSelector` parameter. |\n| `data-hs-datatable-info` | Container element | Defines an info container. |\n| `data-hs-datatable-info-from` | Element | Defines an element that showing `start` number. |\n| `data-hs-datatable-info-to` | Element | Defines an element that showing `end` number. |\n| `data-hs-datatable-info-length` | Element | Defines an element that showing `recordsTotal` number. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-datatable-ordering-asc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `asc`. |\n| `hs-datatable-ordering-desc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `desc`. |\n\n## JavaScript API\n\nThe `HSDataTable` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a datatable instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the datatable instance, removes all generated markup, classes, and event listeners. Use when removing datatable from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDataTable` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDataTable.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDataTable } \\| null` | Returns the datatable instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDataTable` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datatable instance is not found. |\n\n### Usage Examples\n\n**Example 1: Accessing DataTables.net instance**\n```javascript\n// Get the datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying DataTables.net instance\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Use DataTables.net methods\n    dataTable.search('query').draw();\n  }\n}\n```\n\n**Example 2: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 3: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-datatable').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most events available in the plugin are available in our wrapper.\n\nDatatable instances emit events through the underlying DataTables.net instance. Access the DataTables.net instance via `element.dataTable` to listen to events.\n\n**Common DataTables.net Events:**\n- `draw` - When table is redrawn\n- `order` - When column ordering changes\n- `search` - When search is performed\n- `page` - When page changes\n- `select` - When row is selected\n- `deselect` - When row is deselected\n\n### Event Usage Example\n\n```javascript\n// Get datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Listen to draw event\n    dataTable.on('draw.dt', () => {\n      console.log('Table redrawn!');\n      // Perform actions after table redraws\n      // e.g., update UI, reinitialize plugins\n    });\n\n    // Listen to order event\n    dataTable.on('order.dt', () => {\n      console.log('Column ordering changed!');\n    });\n\n    // Listen to search event\n    dataTable.on('search.dt', () => {\n      console.log('Search performed!');\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Search\n\nAdd search functionality to the datatable.\n\n```html\n<input type=\"text\" data-hs-datatable-search placeholder=\"Search...\">\n\n<div data-hs-datatable>\n  <!-- Table structure -->\n</div>\n```\n\n### Pattern 2: With Row Selection\n\nEnable row selection with checkboxes.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-all>\n        </th>\n        <!-- Other columns -->\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-individual>\n        </td>\n        <!-- Other cells -->\n      </tr>\n    </tbody>\n  </table>\n</div>\n```\n\n### Pattern 3: Exclude Column from Ordering\n\nDisable sorting on specific columns.\n\n```html\n<th scope=\"col\" class=\"--exclude-from-ordering\">\n  Actions\n</th>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/datepicker/README.md":"# Advanced Datepicker\n\nCreate visually appealing and interactive datepickers in combination with the Datepicker JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datepicker) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-datepicker.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Datepicker component provides a comprehensive date selection interface with support for custom templates, multiple date formats, time selection, and integration with Vanilla Calendar Pro. It offers flexible configuration options for styling and behavior.\n\n**Key Features:**\n- Date and time selection\n- Custom select inputs for months, years, and time\n- Multiple date formats and locales\n- Custom templates for arrows and time display\n- Integration with Vanilla Calendar Pro\n- Programmatic control via JavaScript API\n- Utility classes support\n\n## Installation\n\nTo get started, install Datepicker plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select @preline/datepicker\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\nUse `styles.css` with `theme.css` for the default themed appearance:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles.css\";\n@import \"./node_modules/@preline/datepicker/theme.css\";\n```\n\nUse `styles-utility.css` instead when you plan to style the datepicker entirely with Tailwind utility classes. In this case `theme.css` is not needed:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles-utility.css\";\n```\n\n`@preline/select` is required for styling the embedded select dropdowns used in `custom-select` mode.\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/datepicker/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDatepicker from \"@preline/datepicker/non-auto.mjs\";\n\n  new HSDatepicker(document.querySelector(\"#datepicker\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `vanilla-calendar-pro` and the Advanced Select plugin are bundled inside `@preline/datepicker`, so no separate imports are needed.\n\n`@preline/datepicker` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/datepicker\";\n```\n\n`@preline/datepicker/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDatepicker from \"@preline/datepicker/non-auto\";\n\nHSDatepicker.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datepicker\");\nif (el) new HSDatepicker(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced datepicker component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The datepicker appears when the input is focused.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\" placeholder=\"\" \n  readonly\n  data-hs-datepicker='{\n    \"type\": \"default\",\n    \"dateMax\": \"2050-12-31\",\n    \"styles\": {\n      \"week\": \"vc-week\",\n      \"weekDay\": \"vc-week__day\",\n      \"dates\": \"vc-dates\",\n      \"date\": \"vc-date\",\n      \"arrowPrev\": \"vc-arrow vc-arrow_prev\",\n      \"arrowNext\": \"vc-arrow vc-arrow_next\"\n    },\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n**Structure Requirements:**\n- `hs-datepicker`: Required class on the input element\n- `data-hs-datepicker`: Required attribute containing configuration options as JSON\n- `readonly` attribute on the input to prevent manual editing\n- `type=\"text\"` on the input element\n\n**Initial State:**\n- Input field is empty by default\n- Datepicker appears when input is focused or clicked\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most options available in the plugin are available in our wrapper. Check [Vanilla Calendar Pro Options](https://vanilla-calendar.pro/docs/reference/settings/) for more details.\n\nData options are specified in the `data-hs-datepicker` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datepicker` | Input element | - | - | Activate a Datepicker by specifying on an element. Should be added to the element itself. |\n| `:mode` | Inside `data-hs-datepicker` | `\"default\"` \\| `\"custom-select\"` | `\"default\"` | This option allows you to specify the operational mode of the datepicker. `default` uses standard layout provided by Vanilla Calendar Pro. `custom-select` activates custom select inputs for months, years and time elements. |\n| `:applyUtilityClasses` | Inside `data-hs-datepicker` | boolean | `false` | Specifies whether to apply utility classes to the datepicker. If set to `true`, the datepicker receives utility classes based on the `primary` theme. |\n| `:inputModeOptions.dateSeparator` | Inside `data-hs-datepicker` | string | `\".\"` | Specifies the character used to separate the year, month, and day in the date format displayed in the input field. E.g., `/` produces a date format like `2024/01/22`. |\n| `:inputModeOptions.itemsSeparator` | Inside `data-hs-datepicker` | string | `\", \"` | Specifies the character or string used to separate multiple date entries in the input field when multiple dates are selected. E.g., `\\|` produces a format like `2024-01-22 \\| 2024-01-23`. |\n| `:styles.customSelect.shared` | Inside `data-hs-datepicker` | object | - | Defines shared styling configurations for Advanced Custom Select components. These styles are consistently applied to all instances of the Advanced Custom Select within the wrapper. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.months` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"months\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.years` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"years\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.hours` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"hours\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.minutes` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"minutes\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.meridiem` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"meridiem\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:templates.time` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for rendering the time selection interface within the datepicker. |\n| `:templates.arrowPrev` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"previous\" navigation arrow in the datepicker. |\n| `:templates.arrowNext` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"next\" navigation arrow in the datepicker. |\n| `:dateFormat` | Inside `data-hs-datepicker` | string | - | Allows to define a custom date format. |\n| `:dateLocale` | Inside `data-hs-datepicker` | string | - | Allows to define the locale for the datepicker. |\n| `:replaceTodayWithText` | Inside `data-hs-datepicker` | boolean | - | Replace the current day name with the text \"Today\". |\n\n**Example:**\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\",\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"dateLocale\": \"en\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-vc-date-today:*` | A modifier that applies Tailwind classes to the current date (marked as \"today\") using the attribute `data-vc-date-today`. |\n| `hs-vc-date-selected:*` | A modifier that applies Tailwind classes to the selected dates using the attribute `data-vc-date-selected`. |\n| `hs-vc-date-weekend:*` | A modifier that applies Tailwind classes to weekend dates using the attribute `data-vc-date-weekend`. |\n| `hs-vc-week-day-off:*` | A modifier that applies Tailwind classes to weekdays marked as \"day off\" using the attribute `data-vc-week-day-off`. |\n| `hs-vc-date-month-prev:*` | A modifier that applies Tailwind classes to dates belonging to the previous month using the class `data-vc-date-month=\"prev\"`. |\n| `hs-vc-date-month-next:*` | A modifier that applies Tailwind classes to dates belonging to the next month using the attribute `data-vc-date-month=\"next\"`. |\n| `hs-vc-calendar-selected-middle:*` | A modifier that applies Tailwind classes to dates that are in the middle of a selected range using the attribute `data-vc-date-selected=\"middle\"`. |\n| `hs-vc-calendar-selected-first:*` | A modifier that applies Tailwind classes to the first date in a selected range using the attribute `data-vc-date-selected=\"first\"`. |\n| `hs-vc-calendar-selected-last:*` | A modifier that applies Tailwind classes to the last date in a selected range using the attribute `data-vc-date-selected=\"last\"`. |\n| `hs-vc-months-month-selected:*` | A modifier that applies Tailwind classes to the currently selected month using the attribute `data-vc-months-month-selected`. |\n| `hs-vc-years-year-selected:*` | A modifier that applies Tailwind classes to the currently selected year using the attribute `data-vc-years-year-selected`. |\n\n## JavaScript API\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most methods available in the plugin are available through our wrapper. Check [Vanilla Calendar Pro Methods](https://vanilla-calendar.pro/docs/reference/methods/) for more details.\n\nThe `HSDatepicker` object is available in the global `window` object after the plugin is loaded. Most methods from Vanilla Calendar Pro are available through the wrapper.\n\n### Instance Methods\n\nThese methods are called on a datepicker instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `{ selectedDates: DatesArr, selectedTime: string }` | Returns the current state of the datepicker including selected dates and time. |\n| `formatDate(date, format)` | `date`: `string \\| number \\| Date`<br>`format`: `string` (optional) | `string` | Formats a date according to the specified format or the datepicker's default format. |\n| `destroy()` | None | `void` | Destroys the datepicker instance, removes all generated markup, classes, and event listeners. Use when removing datepicker from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDatepicker` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDatepicker.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDatepicker } \\| null` | Returns the datepicker instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDatepicker` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datepicker instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('Selected dates:', state.selectedDates);\n  console.log('Selected time:', state.selectedTime);\n}\n```\n\n**Example 2: Formatting dates**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Format a date\n  const formatted = element.formatDate(new Date(), 'YYYY-MM-DD');\n\n  console.log('Formatted date:', formatted);\n}\n```\n\n**Example 3: Destroying datepicker instance**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('.hs-datepicker').remove();\n  });\n}\n```\n\n## Events\n\nDatepicker instances emit events that can be listened to for date selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected date or time changes | `{ selectedDates: string[], selectedTime: string }` (current selection state) | Fires after the user picks a date or changes the time. The payload matches the shape returned by `getCurrentState()`. |\n\n### Event Usage Example\n\n```javascript\n// Get datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for date or time changes\n  element.on('change', ({ selectedDates, selectedTime }) => {\n    console.log('Selected dates:', selectedDates);\n    console.log('Selected time:', selectedTime);\n    // Update dependent fields, validate form, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Select Mode\n\nUse custom select inputs for months, years, and time.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n### Pattern 2: Custom Date Format\n\nSet a custom date format and separator.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/dropdown/README.md":"# Dropdown\n\nA dropdown menu displays a list of actions and more with JavaScript dropdown plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/dropdown) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/dropdown.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (click, hover, contextmenu)\n- Floating UI integration for smart positioning\n- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/dropdown\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/dropdown */\n@source \"../node_modules/@preline/dropdown/*.js\";\n@import \"./node_modules/@preline/dropdown/variants.css\";\n@import \"./node_modules/@preline/dropdown/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/dropdown/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDropdown from \"@preline/dropdown/non-auto.mjs\";\n\n  new HSDropdown(document.querySelector(\"#dropdown\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/dropdown` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/dropdown\";\n```\n\n`@preline/dropdown/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDropdown from \"@preline/dropdown/non-auto\";\n\nHSDropdown.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#dropdown\");\nif (el) new HSDropdown(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.\n\n```html\n<div class=\"hs-dropdown relative inline-flex\">\n  <button id=\"hs-dropdown-unstyled\" type=\"button\" class=\"hs-dropdown-toggle inline-flex justify-center items-center gap-x-2\" aria-expanded=\"false\" aria-label=\"Menu\">\n    Actions\n  </button>\n\n  <div class=\"hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white\" role=\"menu\" aria-labelledby=\"hs-dropdown-unstyled\">\n    <a class=\"block\" href=\"#\">Newsletter</a>\n    <a class=\"block\" href=\"#\">Purchases</a>\n    <a class=\"block\" href=\"#\">Downloads</a>\n    <a class=\"block\" href=\"#\">Team Account</a>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-dropdown`: Required container wrapper\n- `hs-dropdown-toggle`: Required button element that triggers the dropdown\n- `hs-dropdown-menu`: Required container for dropdown menu items\n- Proper ARIA attributes (`aria-expanded`, `aria-label`, `role=\"menu\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the dropdown menu initially\n- Menu visibility is controlled by `hs-dropdown-open:*` modifier classes\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| Enter | Activates the selected menu item. |\n| ArrowUp / ArrowDown | Focuses previous/next non-disabled item. |\n| Home / End | Focuses first/last non-disabled item. |\n| Esc | Closes any open dropdown menus. |\n| A-Z / a-z | Focuses first item that matches keyboard input. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure dropdown behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-dropdown-transition` | `hs-dropdown` (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control dropdown behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--placement:*]` | `hs-dropdown` (container) | `\"auto\"` \\| `\"auto-start\"` \\| `\"auto-end\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-start\"` \\| `\"right-end\"` \\| `\"left\"` \\| `\"left-start\"` \\| `\"left-end\"` | `\"bottom\"` | Specifies the position of the menu when opened relative to the toggle button. |\n| `[--scope:*]` | `hs-dropdown` (container) | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `[--auto-close:*]` | `hs-dropdown` (container) | `\"inside\"` \\| `\"outside\"` \\| `\"false\"` \\| `\"true\"` | `\"true\"` | Specifies the zone, when clicked, which will close the menu. `inside` closes only when clicking inside, `outside` closes only when clicking outside, `true` closes on any click, `false` prevents auto-close. |\n| `[--has-autofocus:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening a dropdown. Set to `\"false\"` to disable autofocus. |\n| `[--strategy:*]` | `hs-dropdown` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"fixed\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--adaptive:*]` | `hs-dropdown` (container) | `\"none\"` \\| `\"adaptive\"` | `\"adaptive\"` | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |\n| `[--gpu-acceleration:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Disable/enable position calculation using the transform property. Set to `\"true\"` to enable GPU acceleration. |\n| `[--offset:*]` | `hs-dropdown` (container) | number | `10` | Sets the dropdown's bottom or top offset in pixels. |\n| `[--flip:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Flips the menu's placement when it starts to overlap its reference element. Set to `\"false\"` to disable flipping. |\n| `[--trigger:*]` | `hs-dropdown` (container) | `\"hover\"` \\| `\"click\"` \\| `\"contextmenu\"` | `\"click\"` | Event to trigger a dropdown. `hover` shows on mouse enter, `click` shows on click, `contextmenu` shows on right-click. |\n\n**Example:**\n```html\n<div class=\"hs-dropdown --trigger:hover --placement:top --auto-close:false\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the dropdown to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown` | Container wrapper | Groups the toggle and menu elements together |\n| `hs-dropdown-toggle` | Button element | The clickable element that activates the dropdown |\n| `hs-dropdown-menu` | Menu container | The dropdown menu that appears on trigger |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown-toggle-wrapper` | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a \"+\" button inside an existing Dropdown toggle button that opens a modal. |\n| `hs-dropdown-close` | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dropdown-open:*` | The modifier that allows you to set Tailwind classes when the dropdown menu is open. |\n\n## JavaScript API\n\nThe `HSDropdown` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a dropdown instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(target, openedViaKeyboard)` | `target`: `VirtualElement \\| HTMLElement` (optional)<br>`openedViaKeyboard`: `boolean` (optional) | `boolean` | Forces the dropdown menu to open programmatically. Returns `true` if opened successfully, `false` otherwise. |\n| `close(isAnimated)` | `isAnimated`: `boolean` (optional) | `boolean` | Forces the dropdown menu to close programmatically. `isAnimated` indicates whether the dropdown menu is closed with animation. Returns `true` if closed successfully, `false` otherwise. |\n| `forceClearState()` | None | `void` | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |\n| `destroy()` | None | `void` | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDropdown` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDropdown.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSDropdown \\| { id: string \\| number, element: HSDropdown } \\| null` | Returns the dropdown instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDropdown` instance. If `isInstance` is `false` or omitted, returns the `HSDropdown` instance directly. Returns `null` if dropdown instance is not found. |\n| `HSDropdown.open(target, openedViaKeyboard)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector)<br>`openedViaKeyboard`: `boolean` (optional) | `void` | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSDropdown.close(target)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new dropdown instance\nconst dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown programmatically\nopenBtn.addEventListener('click', () => {\n  dropdown.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSDropdown.open('#hs-dropdown');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSDropdown instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Clean up when removing from DOM\n  function removeDropdown() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying dropdown instance**\n```javascript\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-dropdown').remove();\n  });\n}\n```\n\n## Events\n\nDropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After dropdown menu is opened | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |\n| `on:close` | After dropdown menu is closed | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (dropdownInstance) => {\n    console.log('Dropdown opened:', dropdownInstance);\n    // Perform actions after dropdown opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (dropdownInstance) => {\n    console.log('Dropdown closed:', dropdownInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Trigger\n\nShow dropdown on hover instead of click.\n\n```html\n<div class=\"hs-dropdown --trigger:hover\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl dropdown from external buttons.\n\n```html\n<div class=\"hs-dropdown\" id=\"hs-dropdown-first\">\n  <button class=\"hs-dropdown-toggle\">Actions</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n\n<button id=\"hs-open-dropdown\">Open Dropdown</button>\n<button id=\"hs-close-dropdown\">Close Dropdown</button>\n\n<script>\n  const instance = HSDropdown.getInstance('#hs-dropdown-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-dropdown').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-dropdown').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/file-upload/README.md":"# File Upload\n\nTailwind CSS File Upload plugin using Dropzone library to enable drag-and-drop functionality, previews, and progress bars.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/file-upload) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/file-upload.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe File Upload component provides a drag-and-drop file upload interface with preview functionality, progress tracking, and integration with Dropzone.js. It supports multiple file uploads, file type validation, and customizable preview templates.\n\n**Key Features:**\n- Drag-and-drop file upload\n- File previews with thumbnails\n- Upload progress tracking\n- Multiple file support\n- File type and extension validation\n- Integration with Dropzone.js\n- Programmatic control via JavaScript API\n- Event system for upload tracking\n\n## Installation\n\nTo get started, install File Upload plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i lodash dropzone @preline/file-upload\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/file-upload */\n@source \"../node_modules/@preline/file-upload/*.js\";\n@import \"./node_modules/@preline/file-upload/variants.css\";\n@import \"./node_modules/@preline/file-upload/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script src=\"./node_modules/@preline/file-upload/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script type=\"module\">\n  import HSFileUpload from \"@preline/file-upload/non-auto.mjs\";\n\n  new HSFileUpload(document.querySelector(\"#file-upload\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `Dropzone` and `_` (Lodash) are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/file-upload` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport \"@preline/file-upload\";\n```\n\n`@preline/file-upload/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport HSFileUpload from \"@preline/file-upload/non-auto\";\n\nHSFileUpload.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#file-upload\");\nif (el) new HSFileUpload(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a file upload component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component supports drag-and-drop and file previews.\n\n```html\n<div id=\"hs-file-upload-first\" data-hs-file-upload='{\n  \"url\": \"/upload\"\n}'>\n  <template data-hs-file-upload-preview>\n    <button type=\"button\" data-hs-file-upload-remove>\n      Delete!\n    </button>\n    <span data-hs-file-upload-file-icon>\n      <img class=\"hidden\" data-dz-thumbnail />\n    </span>\n    <div>\n      <span data-hs-file-upload-file-name></span>.<span data-hs-file-upload-file-ext></span>\n    </div>\n    <div data-hs-file-upload-file-size></div>\n    <div role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" data-hs-file-upload-progress-bar>\n      <div style=\"width: 0\" data-hs-file-upload-progress-bar-pane></div>\n    </div>\n    <span data-hs-file-upload-progress-bar-value>0</span>%\n  </template>\n\n  <div class=\"cursor-pointer\" data-hs-file-upload-trigger>\n    Drop your file here or browse\n  </div>\n\n  <div data-hs-file-upload-previews></div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-file-upload`: Required on the container element, contains configuration options\n- `data-hs-file-upload-preview`: Required template element for file previews (should be a `<template>` element)\n- `data-hs-file-upload-trigger`: Required clickable element for file selection\n- `data-hs-file-upload-previews`: Required container for file preview items\n- Various data attributes for preview elements (file name, extension, size, progress, etc.)\n\n**Initial State:**\n- Upload trigger is visible\n- Previews container is empty\n- Template is hidden (it's a template element)\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-file-upload` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-file-upload` | Container | - | - | Activate a File Upload by specifying on an element. Should be added to the container. |\n| `:url` | Inside `data-hs-file-upload` | string | - | The URL where files will be uploaded. Required for file upload functionality. |\n| `:extensions` | Inside `data-hs-file-upload` | object | Includes markup. See the code. | A list of extensions and associated icons that will be added inside the container with the `data-hs-file-upload-file-icon` selector if they match the extension name. |\n| `:autoHideTrigger` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then `data-hs-file-upload-trigger` will disappear if at least one item is added to the upload and will appear if more than one item is not in the upload. |\n| `:singleton` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then the plugin will remove any previously uploaded file. It needs for emulate simple input file behavior (only one file at a time). |\n\n**Example:**\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/api/upload\",\n  \"autoHideTrigger\": true,\n  \"singleton\": false\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the file upload to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-trigger` | Clickable element | Determines which element inside the initialized element will act as a clickable element for file selection. |\n| `data-hs-file-upload-previews` | Container element | Determines which element inside the initialized element will act as a wrapper for uploaded items. |\n| `data-hs-file-upload-preview` | Template element | Determines which element inside the initialized element will act as a template for the uploading item. Should be a `template` HTML element. |\n\n### Optional CSS Classes / Data Attributes\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-clear` | Button element | Determines which element inside the initialized element will act as a button that deletes all files. |\n| `data-hs-file-upload-file-icon` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file icon defined in the `extensions` property. |\n| `data-hs-file-upload-file-name` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file name. |\n| `data-hs-file-upload-file-ext` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file extension. |\n| `data-hs-file-upload-file-size` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file size. |\n| `data-hs-file-upload-remove` | Button element inside preview | Determines which element inside the uploading item will act as a remove button. |\n| `data-hs-file-upload-reload` | Button element inside preview | Determines which element inside the uploading item will act as a re-upload button. |\n| `data-hs-file-upload-progress-bar` | Element inside preview | Determines which element inside the uploading item will act as a progressbar. |\n| `data-hs-file-upload-progress-bar-pane` | Element inside preview | Determines which element inside the uploading item will act as a progressbar pane, which width will dynamically increase during the uploading process. |\n| `data-hs-file-upload-progress-bar-value` | Element inside preview | Determines which element inside the uploading item will act as a current progress value, which will dynamically increase during the uploading process. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-file-upload-complete:*` | A modifier that allows you to set Tailwind classes to items that successfully uploaded. |\n\n## JavaScript API\n\nThe `HSFileUpload` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a file upload instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the file upload instance, removes all generated markup, classes, and event listeners. Use when removing file upload from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSFileUpload` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSFileUpload.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSFileUpload } \\| null` | Returns the file upload instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSFileUpload` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if file upload instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Accessing Dropzone instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying Dropzone instance\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Use Dropzone methods\n    dropzone.removeAllFiles();\n  }\n}\n```\n\n**Example 3: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-file-upload').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most events available in the plugin are available in our wrapper.\n\nFile upload instances emit events through the underlying Dropzone instance. Access the Dropzone instance via `element.dropzone` to listen to events.\n\n**Common Dropzone Events:**\n- `addedfile` - When a file is added\n- `removedfile` - When a file is removed\n- `uploadprogress` - During file upload\n- `success` - When file upload succeeds\n- `error` - When file upload fails\n- `complete` - When upload completes (success or error)\n\n### Event Usage Example\n\n```javascript\n// Get file upload instance\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Listen to complete event\n    dropzone.on('complete', (file) => {\n      console.log('File uploaded:', file);\n      // Perform actions after file upload completes\n      // e.g., update UI, show success message\n    });\n\n    // Listen to error event\n    dropzone.on('error', (file, errorMessage) => {\n      console.log('Upload error:', errorMessage);\n      // Handle upload errors\n    });\n\n    // Listen to success event\n    dropzone.on('success', (file, response) => {\n      console.log('Upload successful:', response);\n      // Handle successful upload\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Single File Upload\n\nAllow only one file at a time.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"singleton\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Pattern 2: Auto-hide Trigger\n\nHide trigger after files are added.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"autoHideTrigger\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/input-number/README.md":"# Input Number\n\nInput Number - input idea for Shop systems or so.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/input-number) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/input-number.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Input Number component provides a numeric input field with increment and decrement buttons. It's ideal for quantity selectors, cart systems, or any interface requiring numeric input with step controls.\n\n**Key Features:**\n- Numeric input with step controls\n- Increment/decrement buttons\n- Min/max value constraints\n- Custom step values\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Input Number plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/input-number\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/input-number */\n@source \"../node_modules/@preline/input-number/*.js\";\n@import \"./node_modules/@preline/input-number/variants.css\";\n@import \"./node_modules/@preline/input-number/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/input-number/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSInputNumber from \"@preline/input-number/non-auto.mjs\";\n\n  new HSInputNumber(document.querySelector(\"#input-number\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/input-number` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/input-number\";\n```\n\n`@preline/input-number/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSInputNumber from \"@preline/input-number/non-auto\";\n\nHSInputNumber.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#input-number\");\nif (el) new HSInputNumber(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an input number component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component includes an input field and increment/decrement buttons.\n\n```html\n<div data-hs-input-number>\n  <input type=\"text\" aria-roledescription=\"Number field\" value=\"1\" data-hs-input-number-input>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Decrease\" data-hs-input-number-decrement>\n    Minus\n  </button>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Increase\" data-hs-input-number-increment>\n    Add\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-input-number`: Required on the container element\n- `data-hs-input-number-input`: Required on the input element\n- `data-hs-input-number-increment`: Required on the increment button\n- `data-hs-input-number-decrement`: Required on the decrement button\n- Proper ARIA attributes (`aria-roledescription`, `aria-label`)\n\n**Initial State:**\n- Input field contains the initial value (e.g., `value=\"1\"`)\n- Buttons are enabled/disabled based on min/max constraints\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-input-number` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-input-number` | Container | - | - | Activate an Input Number by specifying on an element. Should be added to the container. |\n| `:min` | Inside `data-hs-input-number` | number \\| `\"-Infinity\"` | `0` | Defines the minimum possible value. `-Infinity` allows you to enter negative values without restrictions. |\n| `:max` | Inside `data-hs-input-number` | number \\| null | `null` | Defines the maximum possible value. `null` means no maximum limit. |\n| `:step` | Inside `data-hs-input-number` | number | `1` | Determines the step by which the value will increase or decrease when buttons are clicked. |\n| `:forceBlankValue` | Inside `data-hs-input-number` | boolean | `false` | Whether the input value should be blank when the value isn't set. When `true`, empty input shows blank instead of default value. |\n\n**Example:**\n```html\n<div data-hs-input-number='{\n  \"min\": 0,\n  \"max\": 100,\n  \"step\": 5,\n  \"forceBlankValue\": false\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the input number to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-input-number-input` | Input element | Identifies the numeric input field |\n| `data-hs-input-number-increment` | Button element | Identifies the increment button |\n| `data-hs-input-number-decrement` | Button element | Identifies the decrement button |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-input-number-disabled:*` | A modifier that allows you to set Tailwind classes when input's value is set to zero (or minimum value). |\n\n## JavaScript API\n\nThe `HSInputNumber` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an input number instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the input number instance, removes all generated markup, classes, and event listeners. Use when removing input number from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSInputNumber` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSInputNumber.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSInputNumber \\| { id: number, element: HSInputNumber } \\| null` | Returns the input number instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSInputNumber` instance. If `isInstance` is `false` or omitted, returns the `HSInputNumber` instance directly. Returns `null` if input number instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.inputValue);\n  console.log('Min value:', element.minInputValue);\n  console.log('Max value:', element.maxInputValue);\n  console.log('Step:', element.step);\n\n  // Clean up when removing from DOM\n  function removeInputNumber() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-input-number').remove();\n  });\n}\n```\n\n## Events\n\nInput number instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When input value was changed | `{ inputValue: number }` | Fires when the input value changes (via buttons or direct input). Returns an object with the `inputValue` containing the current numeric value. |\n\n### Event Usage Example\n\n```javascript\n// Get input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ inputValue }) => {\n    console.log('Input value changed:', inputValue);\n    // Perform actions after value changes\n    // e.g., update cart, calculate totals, validate\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Quantity Selector\n\nCreate a quantity selector for shopping carts.\n\n```html\n<div data-hs-input-number='{\n  \"min\": 1,\n  \"max\": 99,\n  \"step\": 1\n}'>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n```\n\n### Pattern 2: Negative Values Allowed\n\nAllow negative values by setting min to -Infinity.\n\n```html\n<div data-hs-input-number='{\n  \"min\": \"-Infinity\",\n  \"max\": null,\n  \"step\": 1\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Pattern 3: Handling Value Changes\n\nTrack value changes with event listener.\n\n```html\n<div id=\"hs-input-number-first\" data-hs-input-number>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n\n<script>\n  const instance = HSInputNumber.getInstance('#hs-input-number-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('change', ({ inputValue }) => {\n      console.log('Quantity:', inputValue);\n      // Update cart or perform other actions\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/layout-splitter/README.md":"# Layout Splitter\n\nCustomize your layout effortlessly by adjusting block sizes to your preference with Tailwind CSS Layout Splitters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/layout-splitter) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/layout-splitter.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Layout Splitter component provides resizable layout panels with drag handles. It allows users to adjust the size of layout sections horizontally or vertically, creating flexible and customizable interfaces.\n\n**Key Features:**\n- Horizontal and vertical resizing\n- Drag handles for intuitive resizing\n- Minimum size constraints\n- Pre-limit size detection\n- Responsive breakpoint support\n- Programmatic control via JavaScript API\n- Event system for drag tracking\n\n## Installation\n\nTo get started, install Layout Splitter plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/layout-splitter\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/layout-splitter */\n@source \"../node_modules/@preline/layout-splitter/*.js\";\n@import \"./node_modules/@preline/layout-splitter/variants.css\";\n@import \"./node_modules/@preline/layout-splitter/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/layout-splitter/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSLayoutSplitter from \"@preline/layout-splitter/non-auto.mjs\";\n\n  new HSLayoutSplitter(document.querySelector(\"#layout-splitter\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/layout-splitter` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/layout-splitter\";\n```\n\n`@preline/layout-splitter/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSLayoutSplitter from \"@preline/layout-splitter/non-auto\";\n\nHSLayoutSplitter.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#layout-splitter\");\nif (el) new HSLayoutSplitter(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a layout splitter component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The layout allows resizing blocks by dragging the splitter handle.\n\n```html\n<div data-hs-layout-splitter='{\n  \"horizontalSplitterTemplate\": \"<div><span class=\\\"absolute top-1/2 start-1/2 -translate-x-2/4 -translate-y-2/4 block w-[11px] h-5 bg-gray-400\\\"></span></div>\",\n  \"horizontalSplitterClasses\": \"relative flex border-s border-gray-200\"\n}'>\n  <div class=\"flex h-50\" data-hs-layout-splitter-horizontal-group>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"48.2\">Horizontal Block 1</div>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"30.0\">Horizontal Block 2</div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-layout-splitter`: Required on the container element, contains configuration options as JSON\n- `data-hs-layout-splitter-horizontal-group`: Required for horizontal splitting\n- `data-hs-layout-splitter-vertical-group`: Required for vertical splitting\n- `data-hs-layout-splitter-item`: Required on resizable blocks, accepts float number or object for initial size\n\n**Initial State:**\n- Blocks are sized according to `data-hs-layout-splitter-item` values\n- Splitter handles are visible and draggable\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-layout-splitter` attribute and on splitter items.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-layout-splitter` | Container | - | - | Activate a Layout Splitter by specifying on an element. Should be added to the container. |\n| `:horizontalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the horizontal splitter template. HTML markup for the drag handle. |\n| `:horizontalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the horizontal splitter CSS classes. CSS classes must be separated by a space. |\n| `:verticalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the vertical splitter template. HTML markup for the drag handle. |\n| `:verticalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the vertical splitter CSS classes. CSS classes must be separated by a space. |\n| `data-hs-layout-splitter-horizontal-group` | Container element | - | - | Defines a group of elements with horizontal resizable blocks. |\n| `data-hs-layout-splitter-vertical-group` | Container element | - | - | Defines a group of elements with vertical resizable blocks. |\n| `data-hs-layout-splitter-item` | Resizable block | float \\| object | - | Defines a block that can be resized depending on what group it is in. Also accepts a float number as a value to specify the initial width or height of the element. Can be an object with `dynamicSize`, `minSize`, `preLimitSize` properties. |\n| `:dynamicSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the initialized block size. |\n| `:minSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the size at which further narrowing of the block will be prevented. |\n| `:preLimitSize` | Inside `data-hs-layout-splitter-item` | float | - | Determines the size at which the `onPrevPreLimit` event will be called. For example, on this event you can add specific classes to the block to the right or left of the divider. |\n\n**Example:**\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0,\n  \"preLimitSize\": 20.0\n}'>\n  Resizable Block\n</div>\n```\n\n## JavaScript API\n\nThe `HSLayoutSplitter` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a layout splitter instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getSplitterItemSingleParam(item, name)` | `item`: `HTMLElement` (splitter item element)<br>`name`: `string` (parameter name) | `any` | Retrieves the value of a specific parameter (name) from the data attribute of a splitter item (item). The parameter is extracted from the parsed JSON object stored in the `data-hs-layout-splitter-item` attribute. |\n| `getData(el)` | `el`: `HTMLElement` (splitter item element) | `object` | Retrieves detailed information about a splitter item, including its current size, minimum size, and pre-limit size, both in pixels and flex values. Returns a structured object. |\n| `setSplitterItemSize(el, size)` | `el`: `HTMLElement` (splitter item element)<br>`size`: `number` (percentage) | `void` | Updates the size of a specified splitter item (`el`) to the given value (size). The new size is converted into a flex value and applied to the element. The method also updates the corresponding data attribute (`data-hs-layout-splitter-item`). |\n| `updateFlexValues(data)` | `data`: `Array<{ id: string, breakpoints: Record<number, number> }>` | `void` | Updates the flex values of splitter items based on the provided configuration. Each configuration object specifies an element's ID and its size values for different screen widths (breakpoints). The method dynamically adjusts the flex values of the specified elements to match the current screen width. |\n| `destroy()` | None | `void` | Cleans up and removes all event listeners, styles, and data attributes associated with the layout splitter. This method restores the affected elements to their original state and releases resources. |\n\n### Static Methods\n\nThese methods are called directly on the `HSLayoutSplitter` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSLayoutSplitter.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSLayoutSplitter } \\| null` | Returns the layout splitter instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSLayoutSplitter` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if layout splitter instance is not found. |\n| `HSLayoutSplitter.autoInit()` | None | `void` | Reinitializes all Layout Splitters on the page. Useful after dynamically adding layout splitters to DOM. |\n\n### Usage Examples\n\n**Example 1: Getting splitter item data**\n```javascript\n// Get the layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n  // Get item data\n  const data = element.getData(item);\n\n  console.log('Item data:', data);\n}\n```\n\n**Example 2: Setting splitter item size**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n\n  // Set item size to 50%\n  element.setSplitterItemSize(item, 50);\n}\n```\n\n**Example 3: Responsive breakpoints**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  // Update flex values based on window size\n  const updateLayoutSplitter = (splitter) => {\n    splitter.updateFlexValues([\n      {\n        id: \"hs-section-first\",\n        breakpoints: {\n          \"0\": 0,\n          \"1280\": 15.0\n        }\n      },\n      {\n        id: \"hs-section-second\",\n        breakpoints: {\n          \"0\": 0,\n          \"1024\": 25.4,\n          \"1280\": 21.0\n        }\n      },\n      {\n        id: \"hs-section-third\",\n        breakpoints: {\n          \"0\": 100,\n          \"1024\": 74.6,\n          \"1280\": 64.0\n        }\n      }\n    ]);\n  };\n\n  window.addEventListener('resize', () => {\n    if (instance) updateLayoutSplitter(instance.element);\n  });\n}\n```\n\n**Example 4: Destroying layout splitter instance**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-layout-splitter').remove();\n  });\n}\n```\n\n## Events\n\nLayout splitter instances emit events that can be listened to for drag tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:drag` | When the splitter is dragged | `{ prev: HTMLElement, next: HTMLElement, previousSize: number, previousFlexSize: number, nextSize: number, nextFlexSize: number }` | Fires when the splitter is being dragged. Returns an object with previous and next elements and their sizes. |\n| `on:onPrevLimit` | When the previous section size limit is reached | `HTMLElement` (previous element) | Fires when the previous section reaches its minimum size limit. |\n| `on:onPrevPreLimit` | When the previous section size is less than `preLimitSize` | `HTMLElement` (previous element) | Fires when the previous section size is less than the `preLimitSize` parameter value. |\n| `on:onNextLimit` | When the next section size limit is reached | `HTMLElement` (next element) | Fires when the next section reaches its minimum size limit. |\n| `on:onNextPreLimit` | When the next section size is less than `preLimitSize` | `HTMLElement` (next element) | Fires when the next section size is less than the `preLimitSize` parameter value. |\n\n### Event Usage Example\n\n```javascript\n// Get layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to limit events\n  element.on('onPrevLimit', (prev) => {\n    prev.classList.add('hs-prev-limit-reached');\n  });\n\n  element.on('onPrevPreLimit', (prev) => {\n    prev.classList.add('hs-prev-pre-limit-reached');\n  });\n\n  // Listen to drag event\n  element.on('drag', ({ prev, previousFlexSize }) => {\n    if (previousFlexSize > 10) {\n      prev.classList.remove('hs-prev-limit-reached');\n    }\n\n    if (previousFlexSize > 20) {\n      prev.classList.remove('hs-prev-pre-limit-reached');\n    }\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Horizontal Splitter\n\nCreate horizontal resizable panels.\n\n```html\n<div data-hs-layout-splitter>\n  <div data-hs-layout-splitter-horizontal-group>\n    <div data-hs-layout-splitter-item=\"50\">Left Panel</div>\n    <div data-hs-layout-splitter-item=\"50\">Right Panel</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Minimum Size\n\nSet minimum size constraints.\n\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0\n}'>\n  Resizable Panel\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/overlay/README.md":"# Overlay\n\nThe Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/overlay) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/overlay.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Overlay component provides a flexible foundation for creating modals, drawers, sidebars, and other overlay-based UI elements. It handles backdrop management, focus trapping, keyboard navigation, and z-index stacking automatically.\n\n**Key Features:**\n- Modal and drawer support\n- Automatic backdrop management\n- Focus trapping and keyboard navigation (Tab, Esc)\n- Z-index stacking for multiple overlays\n- Responsive breakpoint support\n- Auto-close on screen size changes\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/overlay\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/overlay */\n@source \"../node_modules/@preline/overlay/*.js\";\n@import \"./node_modules/@preline/overlay/variants.css\";\n@import \"./node_modules/@preline/overlay/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/overlay/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSOverlay from \"@preline/overlay/non-auto.mjs\";\n\n  new HSOverlay(document.querySelector(\"#overlay\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/overlay` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/overlay\";\n```\n\n`@preline/overlay/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSOverlay from \"@preline/overlay/non-auto\";\n\nHSOverlay.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#overlay\");\nif (el) new HSOverlay(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an overlay component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The overlay appears when the button is clicked.\n\n```html\n<button type=\"button\" aria-haspopup=\"dialog\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-modal\" data-hs-overlay=\"#hs-unstyled-modal\">\n  Open modal\n</button>\n\n<div id=\"hs-unstyled-modal\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-60 overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"hs-unstyled-modal-label\">\n  <div class=\"hs-overlay-open:opacity-100 hs-overlay-open:duration-500 opacity-0 transition-all sm:max-w-lg sm:w-full m-3 sm:mx-auto\">\n    <div class=\"pointer-events-auto\">\n      <h3 id=\"hs-unstyled-modal-label\">Title</h3>\n      Modal content\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-overlay`: Required on the trigger button, must be a valid CSS selector pointing to the overlay element\n- `hs-overlay`: Required class on the overlay content container\n- `hidden` class on the overlay initially\n- Proper ARIA attributes (`aria-haspopup`, `aria-expanded`, `aria-controls`, `role=\"dialog\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the trigger button initially\n- Add `hidden` class to the overlay initially\n- Overlay visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure overlay behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-overlay-options` | Trigger button | object (JSON) | - | Defines the modal configuration. Should be added to the button (trigger). |\n| `:hiddenClass` | Inside `data-hs-overlay-options` | string | `\"hidden\"` | Defines which classes will be added/removed when modal toggles. |\n| `:emulateScrollbarSpace` | Inside `data-hs-overlay-options` | boolean | `false` | If `true`, then adds right padding to the body equal to the size of the scrollbar. Useful to prevent layout shift when overlay opens. |\n| `:isClosePrev` | Inside `data-hs-overlay-options` | boolean | `true` | Determines whether the previous open modal will be closed when the next one is opened. |\n| `:backdropClasses` | Inside `data-hs-overlay-options` | string | `\"hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80\"` | Defines default backdrop classes. |\n| `:backdropExtraClasses` | Inside `data-hs-overlay-options` | string | - | Allows to add additional classes besides those specified in `backdropClasses`. |\n| `:backdropParent` | Inside `data-hs-overlay-options` | string | - | Allows to specify the selector of the element where the backdrop will be generated. |\n| `:moveOverlayToBody` | Inside `data-hs-overlay-options` | number \\| null | `null` | Moves the overlay to the body tag if the screen width is less than the value specified. |\n| `:isToggleClassesImmediately` | Inside `data-hs-overlay-options` | boolean | `false` | When set to `true`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n| `data-hs-overlay-backdrop-container` | Overlay content | string (CSS selector) | `null` | Backdrop element selector. Should be added to the overlay (content). |\n| `data-hs-overlay-keyboard` | Overlay content | boolean | `true` | When set to `false`, the modal will not close when pressing the ESC button on the keyboard. Should be added to the overlay (content). |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control overlay behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--overlay-backdrop:*]` | Overlay content | `\"static\"` \\| null | `null` | When backdrop is set to `\"static\"`, the modal will not close when clicking outside it. Should be added to the overlay (content). |\n| `[--auto-hide:*]` | Overlay content | number | `0` | Milliseconds for auto-closing a modal. When set to `0`, the modal will not close automatically. Should be added to the overlay (content). |\n| `[--auto-close:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the overlay will close automatically. Should be added to the overlay (content). |\n| `[--auto-close-equality-type:*]` | Overlay content | `'less-than'` \\| null | `null` | This option determines the condition under which an overlay should automatically close based on the window's width. When set to `less-than`, the overlay will close if the window's width is less than or equal to the value defined in `[--auto-close:*]`. Otherwise the overlay will close if the window's width is greater than or equal to the `[--auto-close:*]` threshold. Should be added to the overlay (content). |\n| `[--opened:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the plugin will apply functionality for the initially opened overlay. Should be added to the overlay (content). |\n| `[--body-scroll:*]` | Overlay content | boolean | `true` | When set to `false`, the body scroll will be hidden as soon as the modal opens. Should be added to the overlay (content). |\n| `[--tab-accessibility-limited:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Restricts focus to elements within an overlay (or modal). Should be added to the overlay (content). |\n| `[--has-autofocus:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening an overlay. Should be added to the overlay (content). |\n| `[--has-dynamic-z-index:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, each newly opened element in the group will receive an incremented `z-index` inline, ensuring it appears above previously opened elements with the same base `z-index`. Should be added to the overlay (content). |\n| `[--is-layout-affect:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | Informs the plugin that the instance affects the website layout. Should be added to the overlay (content). |\n| `[--toggle-classes-immediately:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n\n**Example:**\n```html\n<div class=\"hs-overlay --overlay-backdrop:static --auto-hide:5000\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the overlay to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay` | Overlay content container | Identifies the overlay content element |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay-animation-target` | Element inside overlay | Defines an element inside the popup, after the end of whose animation the popup will be completely hidden. Has no parameters. |\n| `autofocus` | Input element inside overlay | Focus the first input in a modal with the `autofocus` attribute on opening. Must be added to an input element inside a modal window. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-overlay-open:*` | Defines classes that will be applied to any elements inside the `hs-overlay` when the overlay is open. |\n| `hs-overlay-backdrop-open:*` | Defines classes that will be applied to backdrop when the overlay is open. |\n| `hs-overlay-layout-open:*` | Defines classes that will be applied to any elements inside the body tag when the overlay is open. |\n\n## JavaScript API\n\nThe `HSOverlay` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an overlay instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(cb)` | `cb`: `Function \\| null` (optional) | `Promise<void>` | Forces the overlay to open programmatically. Optionally accepts a callback function that will be called after opening. Returns a Promise. |\n| `close(forceClose, cb)` | `forceClose`: `boolean` (optional)<br>`cb`: `Function \\| null` (optional) | `Promise<unknown>` | Forces the overlay to close programmatically. `forceClose` bypasses animations and closes immediately. Optionally accepts a callback function. Returns a Promise. |\n| `minify(isMinified, cb)` | `isMinified`: `boolean`<br>`cb`: `Function \\| null` (optional) | `void` | Minimizes or restores the overlay. Useful for drawer/sidebar components. |\n| `updateToggles()` | None | `void` | Updates the toggle buttons. Useful after dynamically adding or removing toggle buttons. |\n| `destroy()` | None | `void` | Destroys the overlay instance, removes all generated markup, classes, and event listeners. Use when removing overlay from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSOverlay` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSOverlay.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSOverlay } \\| null` | Returns the overlay instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSOverlay` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if overlay instance is not found. |\n| `HSOverlay.open(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSOverlay.close(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new overlay instance\nconst modal = new HSOverlay(document.querySelector('#hs-modal'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay programmatically\nopenBtn.addEventListener('click', () => {\n  modal.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSOverlay.open('#hs-modal');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSOverlay instance\n  const openBtn = document.querySelector('#hs-open-btn');\n  const closeBtn = document.querySelector('#hs-close-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  closeBtn.addEventListener('click', () => {\n    element.close();\n  });\n\n  // Clean up when removing from DOM\n  function removeOverlay() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying overlay instance**\n```javascript\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-modal').remove();\n  });\n}\n```\n\n## Events\n\nOverlay instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After overlay is opened | `HSOverlay` (overlay instance) | Fires after the overlay has been displayed. Use for post-open actions like loading content or focusing elements. |\n| `on:close` | After overlay is closed | `HSOverlay` (overlay instance) | Fires after the overlay has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (overlayInstance) => {\n    console.log('Overlay opened:', overlayInstance);\n    // Perform actions after overlay opens\n    // e.g., load content, focus input, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (overlayInstance) => {\n    console.log('Overlay closed:', overlayInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Static Backdrop\n\nPrevent closing when clicking outside the overlay.\n\n```html\n<div class=\"hs-overlay --overlay-backdrop:static\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 2: Auto-close on Screen Size\n\nClose overlay automatically at specific breakpoints.\n\n```html\n<div class=\"hs-overlay --auto-close:md --auto-close-equality-type:less-than\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl overlay from external buttons.\n\n```html\n<button id=\"hs-open-modal\">Open Modal</button>\n<button id=\"hs-close-modal\">Close Modal</button>\n\n<div id=\"hs-modal-first\" class=\"hs-overlay hidden\">\n  <!-- Overlay content -->\n</div>\n\n<script>\n  const instance = HSOverlay.getInstance('#hs-modal-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-modal').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-modal').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/pin-input/README.md":"# PIN Input\n\nA PIN Input plugin that allows to fill in data for inputs.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/pin-input) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/pin-input.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe PIN Input component provides a multi-field input interface for entering PIN codes, verification codes, or similar numeric/alphanumeric sequences. It automatically moves focus between fields and supports paste operations.\n\n**Key Features:**\n- Multiple input fields for PIN entry\n- Automatic focus management\n- Paste support for bulk entry\n- Character validation via regex\n- Programmatic control via JavaScript API\n- Event system for completion tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install PIN Input plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/pin-input\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/pin-input */\n@source \"../node_modules/@preline/pin-input/*.js\";\n@import \"./node_modules/@preline/pin-input/variants.css\";\n@import \"./node_modules/@preline/pin-input/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/pin-input/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSPinInput from \"@preline/pin-input/non-auto.mjs\";\n\n  new HSPinInput(document.querySelector(\"#pin-input\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/pin-input` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/pin-input\";\n```\n\n`@preline/pin-input/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSPinInput from \"@preline/pin-input/non-auto\";\n\nHSPinInput.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#pin-input\");\nif (el) new HSPinInput(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a PIN input component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component consists of multiple input fields that automatically advance focus.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-pin-input`: Required on the container element\n- `data-hs-pin-input-item`: Required on each input field\n- All input fields should be direct children of the container\n\n**Initial State:**\n- Input fields are empty by default\n- Focus starts on the first input field (if `autofocus` class is used)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-pin-input` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-pin-input` | Container | - | - | Activate a Pin Input by specifying on an element. Should be added to the container. |\n| `:availableCharsRE` | Inside `data-hs-pin-input` | RegExp (string) | `\"^[a-zA-Z0-9]+$\"` | Regular expression string for available characters. Only characters matching this regex will be accepted. |\n| `data-hs-pin-input-item` | Input element | - | - | Determines which element inside the initialized container will be responsible for entering data. Should be added to the input. |\n\n**Example:**\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <!-- Input fields -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control PIN input behavior.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `autofocus` | One of the input fields | If one of the fields has this class, it will be focused when the page loads. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-pin-input-active:*` | The class is added to the input when the PIN has been set up (all fields are filled). |\n\n## JavaScript API\n\nThe `HSPinInput` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a PIN input instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the PIN input instance, removes all generated markup, classes, and event listeners. Use when removing PIN input from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSPinInput` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSPinInput.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSPinInput \\| { id: string \\| number, element: HSPinInput } \\| null` | Returns the PIN input instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSPinInput` instance. If `isInstance` is `false` or omitted, returns the `HSPinInput` instance directly. Returns `null` if PIN input instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.currentValue);\n  console.log('Items:', element.items);\n\n  // Clean up when removing from DOM\n  function removePinInput() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-pin-input').remove();\n  });\n}\n```\n\n## Events\n\nPIN input instances emit events that can be listened to for completion tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:completed` | When the PIN has been set up (all fields filled) | `{ currentValue: string }` | Fires when all input fields have been filled. Returns an object with the `currentValue` containing the complete PIN string. |\n\n### Event Usage Example\n\n```javascript\n// Get PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to completed event\n  element.on('completed', ({ currentValue }) => {\n    console.log('PIN completed:', currentValue);\n    // Perform actions after PIN is completed\n    // e.g., validate PIN, submit form, proceed to next step\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Numeric PIN Only\n\nRestrict input to numbers only.\n\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 2: Auto-focus First Field\n\nAutomatically focus the first field on page load.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item class=\"autofocus\">\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 3: Handling Completion\n\nHandle PIN completion with event listener.\n\n```html\n<div id=\"hs-pin-input-first\" data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n\n<script>\n  const instance = HSPinInput.getInstance('#hs-pin-input-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('completed', ({ currentValue }) => {\n      console.log('PIN entered:', currentValue);\n      // Validate or submit the PIN\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/range-slider/README.md":"# Advanced Range Slider\n\nAdvanced Range Slider solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/range-slider) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-range-slider.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Range Slider component provides a flexible range input interface for selecting numeric values within a specified range. It integrates with noUiSlider for advanced functionality including dual handles, custom formatting, and smooth animations.\n\n**Key Features:**\n- Single and dual handle support\n- Custom value formatting\n- Smooth animations and transitions\n- Integration with noUiSlider\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Advanced Range Slider plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i nouislider @preline/range-slider\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/range-slider */\n@source \"../node_modules/@preline/range-slider/*.js\";\n@import \"./node_modules/@preline/range-slider/variants.css\";\n@import \"./node_modules/@preline/range-slider/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script src=\"./node_modules/@preline/range-slider/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script type=\"module\">\n  import HSRangeSlider from \"@preline/range-slider/non-auto.mjs\";\n\n  new HSRangeSlider(document.querySelector(\"#range-slider\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `noUiSlider` is referenced as a global, so expose it on `window` before importing the plugin.\n\n`@preline/range-slider` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport \"@preline/range-slider\";\n```\n\n`@preline/range-slider/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport HSRangeSlider from \"@preline/range-slider/non-auto\";\n\nHSRangeSlider.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#range-slider\");\nif (el) new HSRangeSlider(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced range slider component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The slider allows selecting a value between 0 and 100.\n\n```html\n<div data-hs-range-slider='{\n  \"cssClasses\": {},\n  \"start\": 50,\n  \"connect\": \"lower\",\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n**Structure Requirements:**\n- `data-hs-range-slider`: Required on the container element, contains configuration options as JSON\n- Container element will be automatically populated with slider markup by the plugin\n\n**Initial State:**\n- Slider starts at the value specified in `start` option\n- Range is defined by `min` and `max` in the `range` option\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin.\n\nData options are specified in the `data-hs-range-slider` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-range-slider` | Container element | - | - | Activate a Range Slider by specifying on an element. Should be added to the initialized element. Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin. |\n| `:disabled` | Inside `data-hs-range-slider` | boolean | `false` | Makes the element unavailable for interaction. When `true`, the slider cannot be moved. |\n| `:formatter` | Inside `data-hs-range-slider` | string \\| object | - | Output value formatting options. Can be a string or an object with formatting configuration. |\n| `:formatter:type` | Inside `data-hs-range-slider` | `\"integer\"` \\| `\"thousandsSeparatorAndDecimalPoints\"` \\| null | `null` | Defines an output formatter type. `integer` formats as integer, `thousandsSeparatorAndDecimalPoints` adds thousands separators and decimal points. |\n| `:formatter:prefix` | Inside `data-hs-range-slider` | string | - | Adds a prefix to the output value (e.g., \"$\" for currency). |\n| `:formatter:postfix` | Inside `data-hs-range-slider` | string | - | Adds a postfix to the output value (e.g., \"px\" for pixels). |\n\n**Example:**\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  },\n  \"disabled\": false\n}'></div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-range-slider-disabled:*` | A modifier that allows you to set Tailwind classes when slider has \"disabled\" attribute set to \"true\". |\n\n## JavaScript API\n\nThe `HSRangeSlider` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a range slider instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `formattedValue()` | None | `any` | Returns the current values formatted using the `formatter` option. Returns formatted values as specified in the formatter configuration. |\n| `destroy()` | None | `void` | Destroys the range slider instance, removes all generated markup, classes, and event listeners. Use when removing slider from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRangeSlider` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRangeSlider.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRangeSlider } \\| null` | Returns the range slider instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRangeSlider` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if range slider instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting formatted value**\n```javascript\n// Get the range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get formatted value\n  const formatted = element.formattedValue();\n\n  console.log('Formatted value:', formatted);\n}\n```\n\n**Example 2: Accessing noUiSlider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying noUiSlider instance\n  const noUiSlider = element.el.noUiSlider;\n\n  if (noUiSlider) {\n    // Use noUiSlider methods\n    const currentValue = noUiSlider.get();\n\n    console.log('Current value:', currentValue);\n  }\n}\n```\n\n**Example 3: Destroying range slider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-range-slider').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Note that all [events](https://refreshless.com/nouislider/events-callbacks/) available in the [noUiSlider](https://refreshless.com/nouislider/) plugin are also available in our plugin, just use `el.noUiSlider` to handle events.\n\nRange slider instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | When slider value is updated | `[integer, integer?]` | Fires when the slider value changes. Returns an array with current values. For single handle, array contains one value. For dual handles, array contains two values. |\n\n### Event Usage Example\n\n```javascript\n// Get range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to update event via noUiSlider\n  if (element.el.noUiSlider) {\n    element.el.noUiSlider.on('update', (values) => {\n      console.log('Slider updated:', values);\n      console.log('Formatted value:', element.formattedValue());\n      // Perform actions after slider value changes\n      // e.g., update UI, send to server, filter data\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dual Handle Range\n\nCreate a range slider with two handles for selecting a range.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n### Pattern 2: Formatted Values\n\nAdd prefix and postfix to slider values.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": 50,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  }\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/remove-element/README.md":"# Remove Element\n\nDismiss any content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/remove-element) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/remove-element.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Remove Element component provides a simple way to remove or dismiss elements from the DOM with optional animation. It's commonly used for dismissible alerts, notifications, modals, or any content that should be removable.\n\n**Key Features:**\n- One-click element removal\n- Optional removal animation\n- Programmatic control via JavaScript API\n- Simple configuration\n- Event system support\n\n## Installation\n\nTo get started, install Remove Element plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/remove-element\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/remove-element */\n@source \"../node_modules/@preline/remove-element/*.js\";\n@import \"./node_modules/@preline/remove-element/variants.css\";\n@import \"./node_modules/@preline/remove-element/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/remove-element/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSRemoveElement from \"@preline/remove-element/non-auto.mjs\";\n\n  new HSRemoveElement(document.querySelector(\"#remove-element\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/remove-element` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/remove-element\";\n```\n\n`@preline/remove-element/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSRemoveElement from \"@preline/remove-element/non-auto\";\n\nHSRemoveElement.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#remove-element\");\nif (el) new HSRemoveElement(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a remove element component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will remove the target element.\n\n```html\n<div id=\"hs-dismiss-unstyled-first\" class=\"inline-flex justify-between items-center w-full\">\n  <h3>\n    Remove element card\n  </h3>\n  <button type=\"button\" class=\"inline-flex justify-center items-center size-4\" data-hs-remove-element=\"#hs-dismiss-unstyled-first\">\n    <span class=\"sr-only\">Close</span>\n    <svg class=\"shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-remove-element`: Required on the trigger button, must be a valid CSS selector pointing to the element to remove\n- Target element must exist in the DOM\n- Button can be inside or outside the target element\n\n**Initial State:**\n- Target element is visible\n- Clicking the button removes the element\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-remove-element` | Trigger button | string (CSS selector) | - | The element to be removed. This must be a valid CSS selector. Should be added to the button (trigger). Required. |\n| `data-hs-remove-element-options` | Trigger button | object (JSON) | - | Options that can be added via the data attribute. Should be added to the button (trigger). |\n| `:removeTargetAnimationClass` | Inside `data-hs-remove-element-options` | string | `\"hs-removing\"` | Some valid CSS class that will be applied to the target element before removal. Useful for animations. |\n\n**Example:**\n```html\n<button data-hs-remove-element=\"#hs-alert\" data-hs-remove-element-options='{\n  \"removeTargetAnimationClass\": \"fade-out\"\n}'>\n  Close\n</button>\n```\n\n## JavaScript API\n\nThe `HSRemoveElement` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a remove element instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the remove element instance, removes all generated markup, classes, and event listeners. Use when removing remove element from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRemoveElement` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRemoveElement.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRemoveElement } \\| null` | Returns the remove element instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRemoveElement` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if remove element instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the remove element instance\nconst instance = HSRemoveElement.getInstance('[data-hs-remove-element]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Target element:', element.removeTarget);\n  console.log('Animation class:', element.removeTargetAnimationClass);\n\n  // Clean up when removing from DOM\n  function removeRemoveElement() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-remove-element').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dismissible Alert\n\nCreate a dismissible alert notification.\n\n```html\n<div id=\"hs-alert-first\" class=\"alert\">\n  <p>This is an alert message.</p>\n  <button data-hs-remove-element=\"#hs-alert-first\">×</button>\n</div>\n```\n\n### Pattern 2: With Animation\n\nAdd removal animation using CSS classes.\n\n```html\n<div id=\"hs-card-first\" class=\"card\">\n  <h3>Card Title</h3>\n  <button data-hs-remove-element=\"#hs-card-first\" data-hs-remove-element-options='{\n    \"removeTargetAnimationClass\": \"fade-out\"\n  }'>\n    Remove\n  </button>\n</div>\n\n<style>\n  .fade-out {\n    opacity: 0;\n    transition: opacity 0.3s;\n  }\n</style>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/scroll-nav/README.md":"# Scroll Nav\n\nScroll Nav is a versatile and lightweight JavaScript plugin designed to enhance navigation menus with smooth scrolling capabilities.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scroll-nav) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scroll-nav.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scroll Nav component provides horizontal scrolling navigation with previous/next buttons and automatic centering. It's ideal for navigation menus with many items that need to be scrolled horizontally.\n\n**Key Features:**\n- Horizontal scrolling navigation\n- Previous/next navigation buttons\n- Automatic centering of active items\n- Programmatic control via JavaScript API\n- Smooth scrolling animations\n- Current state tracking\n\n## Installation\n\nTo get started, install Scroll Nav plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scroll-nav\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scroll-nav */\n@source \"../node_modules/@preline/scroll-nav/*.js\";\n@import \"./node_modules/@preline/scroll-nav/variants.css\";\n@import \"./node_modules/@preline/scroll-nav/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scroll-nav/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollNav from \"@preline/scroll-nav/non-auto.mjs\";\n\n  new HSScrollNav(document.querySelector(\"#scroll-nav\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scroll-nav` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scroll-nav\";\n```\n\n`@preline/scroll-nav/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollNav from \"@preline/scroll-nav/non-auto\";\n\nHSScrollNav.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scroll-nav\");\nif (el) new HSScrollNav(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scroll nav component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation items scroll horizontally with previous/next buttons.\n\n```html\n<div data-hs-scroll-nav class=\"relative px-6\">\n  <button type=\"button\" class=\"hs-scroll-nav-prev\">Previous</button>\n\n  <nav class=\"hs-scroll-nav-body flex flex-nowrap overflow-x-auto snap-x snap-mandatory\">\n    <a class=\"snap-start\" href=\"#\">Electronics</a>\n    <a class=\"snap-start\" href=\"#\">Clothing</a>\n    <a class=\"snap-start\" href=\"#\">House & Garden</a>\n    <a class=\"snap-start\" href=\"#\">Baby Care</a>\n    <a class=\"snap-start\" href=\"#\">Home appliances</a>\n    <a class=\"snap-start\" href=\"#\">Sports & Fitness</a>\n    <a class=\"snap-start\" href=\"#\">Constraction & Renovation</a>\n    <a class=\"snap-start\" href=\"#\">Food</a>\n    <a class=\"snap-start\" href=\"#\">Pharmacy</a>\n    <a class=\"snap-start\" href=\"#\">Pet Products</a>\n    <a class=\"snap-start\" href=\"#\">Books</a>\n    <a class=\"snap-start\" href=\"#\">Tourism, fishing, hunting</a>\n    <a class=\"snap-start\" href=\"#\">Furniture</a>\n    <a class=\"snap-start\" href=\"#\">Jewelery</a>\n    <a class=\"snap-start\" href=\"#\">Accessories</a>\n    <a class=\"snap-start\" href=\"#\">Games & Consoles</a>\n    <a class=\"snap-start\" href=\"#\">Stationery</a>\n    <a class=\"snap-start\" href=\"#\">Digital Goods</a>\n    <a class=\"snap-start\" href=\"#\">Music</a>\n  </nav>\n\n  <button type=\"button\" class=\"hs-scroll-nav-next\">Next</button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scroll-nav`: Required on the container element\n- `hs-scroll-nav-prev`: Required class on the previous button\n- `hs-scroll-nav-next`: Required class on the next button\n- `hs-scroll-nav-body`: Required class on the scrollable navigation container\n- Navigation items inside the scrollable container\n\n**Initial State:**\n- Navigation items are visible\n- Previous/next buttons are enabled/disabled based on scroll position\n- Active item can be centered automatically if `autoCentering` is enabled\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-scroll-nav` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scroll-nav` | Container | - | - | Activate a Scroll Nav by specifying on an element. Should be added to the container. |\n| `:autoCentering` | Inside `data-hs-scroll-nav` | boolean | `false` | Defines whether the Scroll Nav should align with the active item when the window loads. When `true`, the active item is automatically centered on page load. |\n\n**Example:**\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the scroll nav to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-scroll-nav-prev` | Previous button | Identifies the previous navigation button |\n| `hs-scroll-nav-next` | Next button | Identifies the next navigation button |\n| `hs-scroll-nav-body` | Scrollable container | Identifies the scrollable navigation container |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scroll-nav-active:*` | The class is applied to the navigation item to style the currently active element. |\n| `hs-scroll-nav-disabled:*` | The class is added to the prev/next buttons for style the disabled state. |\n\n## JavaScript API\n\nThe `HSScrollNav` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scroll nav instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `IScrollNavCurrentState` | Retrieves the currently visible first, last, and center elements within the scrollable navigation container. Returns an object with `first`, `last`, and `center` properties. |\n| `goTo(el, cb)` | `el`: `Element` (target element)<br>`cb`: `Function` (optional callback) | `void` | Smoothly scrolls the navigation container to bring the specified element into view. The callback function is executed after scrolling completes. |\n| `centerElement(el, behavior)` | `el`: `HTMLElement` (target element)<br>`behavior`: `ScrollBehavior` (optional, `\"auto\"` \\| `\"smooth\"`) | `void` | Scrolls the navigation container to position the specified element at the center of the visible area. Default behavior is `\"smooth\"`. |\n| `destroy()` | None | `void` | Destroys the scroll nav instance, removes all generated markup, classes, and event listeners. Use when removing scroll nav from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollNav` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollNav.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollNav } \\| null` | Returns the scroll nav instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollNav` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scroll nav instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the scroll nav instance\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('First visible:', state.first);\n  console.log('Last visible:', state.last);\n  console.log('Center visible:', state.center);\n}\n```\n\n**Example 2: Scrolling to an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Scroll to element\n  element.goTo(targetLink, () => {\n    console.log('Scrolled to element!');\n    // Perform actions after scrolling\n  });\n}\n```\n\n**Example 3: Centering an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Center element\n  element.centerElement(targetLink, 'smooth');\n}\n```\n\n**Example 4: Destroying scroll nav instance**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-scroll-nav').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Auto-centering Active Item\n\nAutomatically center the active item on page load.\n\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Pattern 2: Programmatic Navigation\n\nControl navigation programmatically.\n\n```html\n<div id=\"hs-scroll-nav-first\" data-hs-scroll-nav>\n  <!-- Navigation structure -->\n</div>\n\n<script>\n  const instance = HSScrollNav.getInstance('#hs-scroll-nav-first', true);\n  if (instance) {\n    const { element } = instance;\n    // Scroll to specific item\n    const item = document.querySelector('#hs-item-electronics');\n    \n    element.goTo(item);\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/scrollspy/README.md":"# Scrollspy\n\nAutomatically update navigation or list group components based on scroll position to indicate which link is currently active in the viewport.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scrollspy) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scrollspy.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scrollspy component automatically highlights navigation links based on the scroll position of the page. It tracks which section is currently visible in the viewport and updates the corresponding navigation item accordingly.\n\n**Key Features:**\n- Automatic navigation highlighting based on scroll position\n- Customizable scroll offset\n- Support for custom scrollable containers\n- Programmatic control via JavaScript API\n- Event system for scroll tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install Scrollspy plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scrollspy\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scrollspy */\n@source \"../node_modules/@preline/scrollspy/*.js\";\n@import \"./node_modules/@preline/scrollspy/variants.css\";\n@import \"./node_modules/@preline/scrollspy/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scrollspy/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollspy from \"@preline/scrollspy/non-auto.mjs\";\n\n  new HSScrollspy(document.querySelector(\"#scrollspy\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scrollspy` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scrollspy\";\n```\n\n`@preline/scrollspy/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollspy from \"@preline/scrollspy/non-auto\";\n\nHSScrollspy.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scrollspy\");\nif (el) new HSScrollspy(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scrollspy component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation links are automatically highlighted as you scroll through the sections.\n\n```html\n<div id=\"hs-scrollspy-scrollable-parent-first\" class=\"max-h-96 overflow-y-auto\">\n  <div data-hs-scrollspy=\"#hs-scrollspy-first\" class=\"flex flex-wrap gap-3\">\n    <a class=\"active\" href=\"#hs-first\">First</a>\n    <a href=\"#hs-second\">Second</a>\n    <a href=\"#hs-third\">Third</a>\n    <a href=\"#hs-fourth\">Fourth</a>\n    <a href=\"#hs-fifth\">Fifth</a>\n  </div>\n\n  <div id=\"hs-scrollspy-first\">\n    <div id=\"hs-first\" class=\"h-48\">\n      First\n    </div>\n    <div id=\"hs-second\" class=\"h-48\">\n      Second\n    </div>\n    <div id=\"hs-third\" class=\"h-48\">\n      Third\n    </div>\n    <div id=\"hs-fourth\" class=\"h-48\">\n      Fourth\n    </div>\n    <div id=\"hs-fifth\" class=\"h-48\">\n      Fifth\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scrollspy`: Required on the navigation container, must be a valid CSS selector pointing to the sections container\n- Navigation links with `href` attributes pointing to section IDs\n- Sections with matching `id` attributes\n- `active` class on the initially active navigation link\n\n**Initial State:**\n- Add `active` class to the navigation link that corresponds to the initially visible section\n- Ensure sections have sufficient height to enable scrolling\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure scrollspy behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scrollspy` | Navigation container | string (CSS selector) | - | A container containing sections. This must be a valid CSS selector pointing to the sections container. Should be added to the nav that contains Scrollspy links. |\n| `data-hs-scrollspy-scrollable-parent` | Navigation container | string (CSS selector) | `\"window\"` | Specifies the element to be scrolled. This must be a valid CSS selector. Should be added to the nav that contains Scrollspy links. Defaults to `window` if not specified. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control scrollspy behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--scrollspy-offset:*]` | Navigation container | number | `0` | Adds offset when scrolling to the section and to determine if the section is active. Useful for fixed headers or navigation bars. |\n\n**Example:**\n```html\n<div data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scrollspy-active:*` | A modifier that allows you to set Tailwind classes when the section is active for the corresponding link. |\n\n## JavaScript API\n\nThe `HSScrollspy` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scrollspy instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the scrollspy instance, removes all generated markup, classes, and event listeners. Use when removing scrollspy from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollspy` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollspy.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollspy } \\| null` | Returns the scrollspy instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollspy` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scrollspy instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying scrollspy instance**\n```javascript\nconst instance = HSScrollspy.getInstance('#hs-scrollspy', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Scrollspy links:', element.links);\n  console.log('Scrollspy sections:', element.sections);\n\n  // Clean up when removing from DOM\n  function removeScrollspy() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nScrollspy instances emit events that can be listened to for scroll tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeScroll` | Before scrolling begins | `HSScrollspy` (scrollspy instance) | Fires before scrolling to a section begins. Can return a Promise to delay scrolling until async operations complete. Useful for animations or other preparations. |\n\n### Event Usage Example\n\n```javascript\n// Get scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n  const collapse = HSCollapse.getInstance('[data-hs-collapse=\"#hs-navbar-collapse\"]', true);\n\n  // Listen to beforeScroll event\n  element.on('beforeScroll', (scrollspyInstance) => {\n    return new Promise((resolve) => {\n      if (collapse && collapse.element.el.classList.contains('open')) {\n        collapse.element.hide();\n        HSStaticMethods.afterTransition(collapse.element.content, () => resolve(true));\n      } else {\n        resolve(true);\n      }\n    });\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Scrollable Container\n\nUse scrollspy with a custom scrollable container instead of the window.\n\n```html\n<div id=\"hs-scrollable-container\" class=\"max-h-96 overflow-y-auto\">\n  <nav data-hs-scrollspy=\"#hs-sections\" data-hs-scrollspy-scrollable-parent=\"#hs-scrollable-container\">\n    <a href=\"#hs-section-first\">Section 1</a>\n    <a href=\"#hs-section-second\">Section 2</a>\n  </nav>\n  \n  <div id=\"hs-sections\">\n    <div id=\"hs-section-first\">Content 1</div>\n    <div id=\"hs-section-second\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Offset\n\nAdd offset for fixed navigation bars.\n\n```html\n<nav data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</nav>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/select/README.md":"# Advanced Select\n\nAdvanced select solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/select) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-select.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Select component provides a customizable dropdown select interface with support for search, tags, API integration, and extensive styling options. It's designed for handling large datasets and complex selection scenarios.\n\n**Key Features:**\n- Search functionality\n- Tags mode for multiple selections\n- API integration for dynamic options\n- Custom templates and styling\n- Multiple selection support\n- Option grouping\n- Icons and descriptions for options\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n\n## Installation\n\nTo get started, install Select plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select\n```\n\nSome positioning features require [Floating UI](https://floating-ui.com). Install it if you plan to use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`:\n\n```bash\nnpm i @floating-ui/dom\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, also load the [Floating UI](https://floating-ui.com) UMD bundle before the plugin. It exposes the `FloatingUIDOM` global the plugin relies on.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script src=\"./node_modules/@preline/select/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script type=\"module\">\n  import HSSelect from \"@preline/select/non-auto.mjs\";\n\n  new HSSelect(document.querySelector(\"#select\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, expose `FloatingUIDOM` on `window` before importing the plugin.\n\n`@preline/select` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport \"@preline/select\";\n```\n\n`@preline/select/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport HSSelect from \"@preline/select/non-auto\";\n\nHSSelect.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#select\");\nif (el) new HSSelect(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced select component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component transforms a standard select element into an advanced dropdown.\n\n```html\n<select data-hs-select='{\n    \"placeholder\": \"Select option...\",\n    \"toggleTag\": \"<button type=\\\"button\\\"></button>\",\n    \"toggleClasses\": \"\",\n    \"dropdownClasses\": \"\",\n    \"optionClasses\": \"hs-selected:\"\n  }'>\n  <option>Select option</option>\n  <option>Name</option>\n  <option>Email address</option>\n  <option>Description</option>\n  <option>User ID</option>\n</select>\n```\n\n**Structure Requirements:**\n- `data-hs-select`: Required on the `<select>` element, contains configuration options as JSON\n- Standard `<option>` elements inside the select\n- Optional `<optgroup>` elements for grouping\n\n**Initial State:**\n- Select displays placeholder text when no option is selected\n- Dropdown is closed by default\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-select` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-select` | Select element | - | - | Activate a Custom Select by specifying on an element. Should be added to the select. |\n| `:isOpened` | Inside `data-hs-select` | boolean | `false` | Opens the select if the value is `true`. |\n| `:placeholder` | Inside `data-hs-select` | string | `\"Select...\"` | Define a default placeholder when nothing is selected. |\n| `:hasSearch` | Inside `data-hs-select` | boolean | `false` | Define a search field inside the dropdown if the value is `true`. |\n| `:minSearchLength` | Inside `data-hs-select` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:preventSearchFocus` | Inside `data-hs-select` | boolean | `false` | Sets autofocus for the search field inside a dropdown list if the value is `true`. |\n| `:preventSearchInsideDescription` | Inside `data-hs-select` | boolean | `false` | Prevents searching inside the description when set to `true`. |\n| `:mode` | Inside `data-hs-select` | `'default'` \\| `'tags'` | `'default'` | Define a select mode. `default` for single/multiple selection, `tags` for tag-based selection. |\n| `:scrollToSelected` | Inside `data-hs-select` | boolean | `false` | Scroll to the selected option when the dropdown is opened. |\n| `:value` | Inside `data-hs-select` | string \\| string[] | - | Defines the initial selected value. Use an array when the select is multiple. |\n| `:viewport` | Inside `data-hs-select` | string | - | Defines the viewport selector used when recalculating dropdown direction and available space. |\n| `:toggleTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the select toggle. Note: `data-title` is required if you are using a custom placeholder. |\n| `:toggleClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' toggle. CSS classes must be separated by a space. |\n| `:toggleSeparators` | Inside `data-hs-select` | object | - | Define separators for the selects' toggle. |\n| `:toggleSeparators:items` | Inside `data-hs-select` | string | `\", \"` | Define which separator will be used for separate selected items. |\n| `:toggleSeparators:betweenItemsAndCounter` | Inside `data-hs-select` | string | `\"and\"` | Define which separator will be used for separate selected items and counter text. |\n| `:toggleCountText` | Inside `data-hs-select` | string | - | This option is only available for multiple select. Determines what text will be after the counter. It also activates the counting mode. |\n| `:toggleCountTextPlacement` | Inside `data-hs-select` | `'postfix'` \\| `'prefix'` \\| `'postfix-no-space'` \\| `'prefix-no-space'` | `'postfix'` | Allows to specify where the text specified in the `toggleCountText` parameter will be located relative to the counter. |\n| `:toggleCountTextMinItems` | Inside `data-hs-select` | number | `1` | This option is only available for multiple select. Defines the minimum number of selected items at which the counting mode will be activated. |\n| `:toggleCountTextMode` | Inside `data-hs-select` | `'countAfterLimit'` \\| `'nItemsAndCount'` | `'countAfterLimit'` | This option is only available for multiple select. Controls the display of the contents of the button title. |\n| `:wrapperClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' wrapper. CSS classes must be separated by a space. |\n| `:tagsItemTemplate` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define template for the single tag. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-remove` the target tag will be deleted when click on tag with this data attribute. |\n| `:tagsItemClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the single tags. CSS classes must be separated by a space. |\n| `:tagsInputId` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. This option is useful if there is a need to associate a `label` outside the initialized element with the tags input. |\n| `:tagsInputClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the input. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownTag` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define a markup for the dropdown. |\n| `:dropdownClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"bottom\"` | Specifies the position of the menu when opened. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownAutoPlacement` | Inside `data-hs-select` | boolean | `false` | Automatically determine the placement of the menu based on the available space. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownVerticalFixedPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"bottom\"` \\| null | `null` | Specifies a fixed vertical position for the menu when opened. It will not change when scrolling. |\n| `:dropdownScope` | Inside `data-hs-select` | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `:dropdownDirectionClasses` | Inside `data-hs-select` | `{ top?: string; bottom?: string; }` | `null` | Defines classes that are applied when the dropdown is placed above or below the toggle. |\n| `:dropdownSpace` | Inside `data-hs-select` | number | `10` | Defines the spacing in pixels between the toggle and dropdown. |\n| `:searchTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<input type=\\\"text\\\">\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search input. |\n| `:searchWrapperTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<div></div>\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search wrapper. |\n| `:searchWrapperClasses` | Inside `data-hs-select` | string | `\"bg-white p-2 sticky top-0\"` | This option is only available when `hasSearch: true`. Define CSS classes for the search wrapper. |\n| `:searchId` | Inside `data-hs-select` | string | - | This option is only available when `hasSearch: true`. This option is useful if there is a need to associate a `label` outside the initialized element with the search input. |\n| `:searchLimit` | Inside `data-hs-select` | number | `Infinity` | This option is only available when `hasSearch: true`. If this option is enabled, the search will display only the first 'n' matching items. |\n| `:isSearchDirectMatch` | Inside `data-hs-select` | boolean | `true` | This option is only available when `hasSearch: true`. If the option is disabled, then in the search results you will be able to see non-direct matches by text. For example, if you entered `england` in the search field, then `Eng-land`, `Eng.land`, `Eng_land` will also be shown. |\n| `:searchClasses` | Inside `data-hs-select` | string | `\"block w-[calc(100%-32px)] text-sm border-gray-200 rounded-md focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 py-2 px-3 my-2 mx-4\"` | This option is only available when `:hasSearch` attribute is `true`. Define CSS classes for the search field inside the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:searchPlaceholder` | Inside `data-hs-select` | string | `\"Search...\"` | This option is only available when `:hasSearch` attribute is `true`. Determines placeholder for the search field inside the dropdown. |\n| `:searchNoResultTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<span></span>\"` | This option is only available when `:hasSearch` attribute is `true`. Define a markup for the \"no result\" text. |\n| `:searchNoResultText` | Inside `data-hs-select` | string | `\"No options found...\"` | This option is only available when `:hasSearch` attribute is `true`. Defines the text that will be displayed if no results are found. |\n| `:searchNoResultClasses` | Inside `data-hs-select` | string | `\"px-4 text-sm\"` | This option is only available when `:hasSearch` attribute is `true`. Defines CSS classes for the \"no results\" wrapper. CSS classes must be separated by a space. |\n| `:optionAllowEmptyOption` | Inside `data-hs-select` | boolean | `false` | This option determines whether an empty option should be included in the list. When set to `true`, an additional option with an empty string (`\"\"`) as its value will be displayed, allowing users to select a blank choice if needed. |\n| `:optionTemplate` | Inside `data-hs-select` | string (HTML markup) | - | Define template for the single option. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-description` if target option tag has `data-hs-select-option:description`. |\n| `:optionTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option. |\n| `:optionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option. CSS classes must be separated by a space. |\n| `:optgroupTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option group. |\n| `:optgroupClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option group. CSS classes must be separated by a space. |\n| `:extraMarkup` | Inside `data-hs-select` | string \\| array (HTML markup) | - | Define a markup that could be extra added to the wrapper of the select for the decoration reasons. If it contains the `--prevent-click` class, clicking on this element will not trigger the open function. |\n| `:descriptionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the description inside the single option. CSS classes must be separated by a space. |\n| `:iconClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the icon inside the single option. CSS classes must be separated by a space. |\n| `:isAddTagOnEnter` | Inside `data-hs-select` | boolean | `true` | Determines whether a tag will be added when the enter key is pressed. |\n| `:isSelectedOptionOnTop` | Inside `data-hs-select` | boolean | `false` | Controls whether selected options should appear at the top of the dropdown list. When set to `true`, selected options will be sorted to the top of the list. |\n| `:apiUrl` | Inside `data-hs-select` | string \\| null | `null` | Defines the address where the API is located. |\n| `:apiQuery` | Inside `data-hs-select` | string \\| null | `null` | Defines query parameters that are separated by `?`. |\n| `:apiOptions` | Inside `data-hs-select` | RequestInit \\| null | `null` | Defines options for the fetch function. |\n| `:apiDataPart` | Inside `data-hs-select` | string \\| null | `null` | If data is in some first level parameter, then it allows you to specify the name of this parameter to extract the data. |\n| `:apiSearchQueryKey` | Inside `data-hs-select` | string \\| null | `null` | Defines the key for the query search parameter. |\n| `:apiFieldsMap` | Inside `data-hs-select` | `{ id: string; title: string; val?: string; icon?: string \\| null; description?: string \\| null; page?: string; offset?: string; limit?: string; pageStart?: string; totalPath?: string; [key: string]: unknown; } \\| null` | `null` | Allows you to convert fields from the API to the fields required for the plugin to work. `page`, `offset`, and `limit` customize pagination parameter names for remote data. |\n| `:apiLoadMore` | Inside `data-hs-select` | `boolean \\| { perPage?: number; scrollThreshold?: number; }` | `false` | Defines the options for the load more feature. |\n| `:apiPageStart` | Inside `data-hs-select` | number | - | Defines the first page or offset value used for remote option loading. If omitted, page-based loading starts from `1` and offset-based loading starts from `0`. |\n| `:apiTotalPath` | Inside `data-hs-select` | string \\| null | `null` | Defines the response path used to read the total result count for remote option loading. |\n| `:apiSelectedValues` | Inside `data-hs-select` | `string \\| string[] \\| null` | `null` | Allows to preselect values when loading options from an API. Can be a single value or an array of values for multiple selection. |\n| `:apiIconTag` | Inside `data-hs-select` | string \\| null | `null` | Allows to define an `img` tag that will be used when rendering options and in the trigger button. |\n| `data-hs-select-option` | Option element | object | - | Allows to define the parameters for the single option. Should be added to the option. |\n| `:description` | Inside `data-hs-select-option` | string | - | Allows to define the description of the option inside the element that have `data-description` attribute. |\n| `:icon` | Inside `data-hs-select-option` | string | - | Allows to define the icon of the option inside the element that have `data-icon` attribute. |\n| `:additionalClasses` | Inside `data-hs-select-option` | `[string, string[]][]` | - | Allows to define additional classes. The first string is the selector of the element to apply the classes to, the second string is an array of classes to apply. If the selector is set to `null`, the classes will be applied to the option itself. |\n| `:apiFields` | Inside `data-hs-select-option` | `{ [key: string]: unknown; }` | - | Allows to define the fields of the option from the API. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-select-opened:*` | A modifier that allows you to set Tailwind classes when select has been opened. |\n| `hs-select-disabled:*` | A modifier that allows you to set Tailwind classes when select has \"disabled\" attribute. |\n\n## JavaScript API\n\nThe `HSSelect` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a select instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open()` | None | `void` | Opens the dropdown list programmatically. |\n| `close()` | None | `void` | Closes the dropdown list programmatically. |\n| `setValue(value)` | `value`: `string \\| string[]` | `void` | Set the select value. This should be a string for a simple select and an array if it's a multiple select. |\n| `addOption(items)` | `items`: `{ title: string; val: string; options?: { description: string; icon: string; }; }` or array of such objects | `void` | Adds an option (several possible) to the select. The single option should follow this interface. |\n| `removeOption(val)` | `val`: `string \\| string[]` | `void` | Removes an option by value (several possible). |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown list. Useful when dropdown position needs to be updated after layout changes. |\n| `destroy()` | None | `void` | Destroys the select instance, removes all generated markup, classes, and event listeners. Use when removing select from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSSelect` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSSelect.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSSelect } \\| null` | Returns the select instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSSelect` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if select instance is not found. |\n\n### Usage Examples\n\n**Example 1: Opening and closing dropdown**\n```javascript\n// Get the select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Open dropdown\n  element.open();\n\n  // Close dropdown\n  element.close();\n}\n```\n\n**Example 2: Setting value programmatically**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set value for single select\n  element.setValue('option-value');\n\n  // Set value for multiple select\n  element.setValue(['value-1', 'value-2']);\n}\n```\n\n**Example 3: Adding and removing options**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Add option\n  element.addOption({\n    title: 'New Option',\n    val: 'new-option',\n    options: {\n      description: 'Option description',\n      icon: '<svg>...</svg>'\n    }\n  });\n\n  // Remove option\n  element.removeOption('new-option');\n}\n```\n\n**Example 4: Destroying select instance**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-select').remove();\n  });\n}\n```\n\n## Events\n\nSelect instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected value changes | `string | string[]` (new selected value; array in multi-select / tags mode) | Fires after the user picks an option or removes a tag. |\n| `on:request` | Right before an `apiUrl` request is sent (search or `apiLoadMore` pagination) | `{ url: string }` | Useful for showing a loading indicator. |\n| `on:requestCompleted` | After an `apiUrl` request settles, success or failure | `{ url: string, data?: unknown, error?: unknown }` | `data` holds the parsed response on success, `error` is set on failure. Not fired when a request is superseded/aborted (e.g. a newer search query). |\n\nBoth are also dispatched as DOM `CustomEvent`s (`request.hs.select` / `requestCompleted.hs.select`) with the same payload under `event.detail.payload`, so they can be listened to via `addEventListener` as well as `element.on(...)`.\n\n### Event Usage Example\n\n```javascript\n// Get select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for selection changes\n  element.on('change', (value) => {\n    console.log('Selected value:', value);\n    // value is a string for single-select, string[] for multi-select / tags mode\n  });\n\n  // Listen for API request lifecycle (only relevant when `apiUrl` is set)\n  element.on('request', ({ url }) => {\n    console.log('Requesting:', url);\n    // e.g. show a loading spinner\n  });\n\n  element.on('requestCompleted', ({ data, error }) => {\n    if (error) console.error('Request failed:', error);\n    else console.log('Request completed with:', data);\n    // e.g. hide the loading spinner\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Search Enabled\n\nEnable search functionality in dropdown.\n\n```html\n<select data-hs-select='{\n  \"hasSearch\": true,\n  \"searchPlaceholder\": \"Search options...\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 2: Tags Mode\n\nUse tags mode for multiple selections.\n\n```html\n<select multiple data-hs-select='{\n  \"mode\": \"tags\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 3: API Integration\n\nLoad options from API.\n\n```html\n<select data-hs-select='{\n  \"apiUrl\": \"/api/options\",\n  \"apiFieldsMap\": {\n    \"id\": \"id\",\n    \"title\": \"name\",\n    \"val\": \"value\"\n  }\n}'>\n  <!-- Options -->\n</select>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/stepper/README.md":"# Stepper\n\nDynamic stepper plugin that guides users through the steps of a task.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/stepper) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/stepper.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Stepper component provides a multi-step wizard interface that guides users through a sequential process. It supports both linear (sequential) and non-linear (jumpable) modes, with options for optional steps, error states, and completion tracking.\n\n**Key Features:**\n- Multi-step wizard interface\n- Linear and non-linear navigation modes\n- Optional steps support\n- Error and processing states\n- Completion tracking\n- Programmatic control via JavaScript API\n- Event system for step transitions\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Stepper plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/stepper\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/stepper */\n@source \"../node_modules/@preline/stepper/*.js\";\n@import \"./node_modules/@preline/stepper/variants.css\";\n@import \"./node_modules/@preline/stepper/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/stepper/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStepper from \"@preline/stepper/non-auto.mjs\";\n\n  new HSStepper(document.querySelector(\"#stepper\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/stepper` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/stepper\";\n```\n\n`@preline/stepper/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStepper from \"@preline/stepper/non-auto\";\n\nHSStepper.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#stepper\");\nif (el) new HSStepper(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a stepper component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three steps with navigation buttons.\n\n```html\n<div data-hs-stepper>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 1\n  }'>\n    1 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 2\n  }'>\n    2 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 3\n  }'>\n    3 Step\n  </div>\n  \n  <div data-hs-stepper-content-item='{\n    \"index\": 1\n  }' style=\"display: none;\">\n    First content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 2\n  }' style=\"display: none;\">\n    Second content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 3\n  }' style=\"display: none;\">\n    Third content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"isFinal\": true\n  }' style=\"display: none;\">\n    Final content.\n  </div>\n  \n  <button class=\"hs-stepper-disabled:opacity-50\" type=\"button\" data-hs-stepper-back-btn>\n    Back\n  </button>\n  <button type=\"button\" data-hs-stepper-skip-btn style=\"display: none;\">\n    Skip\n  </button>\n  <button type=\"button\" data-hs-stepper-next-btn>\n    Next\n  </button>\n  <button type=\"button\" data-hs-stepper-finish-btn style=\"display: none;\">\n    Finish\n  </button>\n  <button type=\"reset\" data-hs-stepper-reset-btn style=\"display: none;\">\n    Reset\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-stepper`: Required on the container element\n- `data-hs-stepper-nav-item`: Required on each navigation item\n- `data-hs-stepper-content-item`: Required on each content panel\n- `data-hs-stepper-back-btn`: Optional back button\n- `data-hs-stepper-next-btn`: Optional next button\n- `data-hs-stepper-finish-btn`: Optional finish button\n- `data-hs-stepper-skip-btn`: Optional skip button\n- `data-hs-stepper-reset-btn`: Optional reset button\n\n**Initial State:**\n- First step is active by default (index 1)\n- Content items should have `style=\"display: none;\"` except the first one\n- Navigation buttons visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-stepper`, `data-hs-stepper-nav-item`, and `data-hs-stepper-content-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-stepper` | Container | - | - | Activate a Stepper by specifying on an element. Should be added to the container. |\n| `:currentIndex` | Inside `data-hs-stepper` | number | `1` | Index of the current step. This must be a number between 1 and the maximum number of steps. |\n| `:mode` | Inside `data-hs-stepper` | `\"linear\"` \\| `\"non-linear\"` | `\"linear\"` | The mode of the stepper. In `\"non-linear\"` mode, the user can navigate to any step and nav items are clickable. In `\"linear\"` mode, the user can only navigate to the next/back step. |\n| `:isCompleted` | Inside `data-hs-stepper` | boolean | `false` | Whether the stepper is completed. |\n| `data-hs-stepper-nav-item` | Navigation item | - | - | Activate a Stepper Nav Item by specifying on an element. Should be added to the nav item. |\n| `:index` | Inside `data-hs-stepper-nav-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isOptional` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is optional. |\n| `:isCompleted` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is skipped. |\n| `:hasError` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step has an error. |\n| `data-hs-stepper-content-item` | Content item | - | - | Activate a Stepper Content Item by specifying on an element. Should be added to the content item. |\n| `:index` | Inside `data-hs-stepper-content-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isCompleted` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is skipped. |\n| `:isFinal` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is final. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-stepper-active:*` | Modifies the active step. |\n| `hs-stepper-success:*` | Modifies the completed step. |\n| `hs-stepper-disabled:*` | Modifies the \"back\" button when the very first step is active. |\n| `hs-stepper-skipped:*` | Modifies the skipped step. |\n| `hs-stepper-error:*` | Modifies the step that has error. Error class should be added manually by some event. E.g. after form validation. |\n| `hs-stepper-process:*` | Modifies the step that processing. Process class should be added manually by some event. E.g. after form submit. |\n| `hs-stepper-completed:*` | Modifies all steps are completed. |\n\n## JavaScript API\n\nThe `HSStepper` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a stepper instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setProcessedNavItem(n)` | `n`: `number` | `void` | Set the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `setErrorNavItem(n)` | `n`: `number` | `void` | Set the nav item as error. `n` is the index of the step to which the item belongs. |\n| `unsetProcessedNavItem(n)` | `n`: `number` | `void` | Unset the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `goToNext()` | None | `number` | Go to the next step. Returns the index of the next step. If the current step is the last, returns the index of the current step. |\n| `goToFinish()` | None | `number` | Go to the finish step. Returns the index of the finish step. If the current step is the last, returns the index of the current step. |\n| `disableButtons()` | None | `void` | Disable the \"next\" and \"back\" buttons. |\n| `enableButtons()` | None | `void` | Enable the \"next\" and \"back\" buttons. |\n| `destroy()` | None | `void` | Destroys the stepper instance, removes all generated markup, classes, and event listeners. Use when removing stepper from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStepper` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStepper.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSStepper \\| { id: string \\| number, element: HSStepper } \\| null` | Returns the stepper instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStepper` instance. If `isInstance` is `false` or omitted, returns the `HSStepper` instance directly. Returns `null` if stepper instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new stepper instance\nconst stepper = new HSStepper(document.querySelector('#hs-stepper'));\nlet errorState = 1;\n\nstepper.on('beforeNext', (index) => {\n  if (index === 2) {\n    stepper.setProcessedNavItem(index);\n    \n    setTimeout(() => {\n      stepper.unsetProcessedNavItem(index);\n      stepper.enableButtons();\n      \n      if (errorState) {\n        stepper.goToNext();\n      } else {\n        stepper.setErrorNavItem(index);\n      }\n      \n      errorState = !errorState;\n    }, 2000);\n  }\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSStepper instance\n  let errorState = 1;\n\n  element.on('beforeNext', (index) => {\n    if (index === 2) {\n      element.setProcessedNavItem(index);\n      \n      setTimeout(() => {\n        element.unsetProcessedNavItem(index);\n        element.enableButtons();\n        \n        if (errorState) {\n          element.goToNext();\n        } else {\n          element.setErrorNavItem(index);\n        }\n        \n        errorState = !errorState;\n      }, 2000);\n    }\n  });\n\n  // Clean up when removing from DOM\n  function removeStepper() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Programmatic navigation**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\nif (instance) {\n  const { element } = instance;\n  // Navigate to next step\n  const nextIndex = element.goToNext();\n  console.log('Moved to step:', nextIndex);\n  // Navigate to finish\n  const finishIndex = element.goToFinish();\n\n  console.log('Finished at step:', finishIndex);\n}\n```\n\n**Example 4: Destroying stepper instance**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-stepper').remove();\n  });\n}\n```\n\n## Events\n\nStepper instances emit events that can be listened to for step transitions and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:active` | When the \"active\" class is set up | `currentIndex` (number) | Fires when a step becomes active. |\n| `on:beforeNext` | Before the \"next\" button is clicked | `currentIndex` (number) | Fires before navigating to the next step. Can be used to validate or prevent navigation. |\n| `on:beforeFinish` | Before the \"finish\" button is clicked | `currentIndex` (number) | Fires before finishing the stepper. Can be used to validate or prevent completion. |\n| `on:next` | When the \"next\" button is clicked | `currentIndex` (number) | Fires when navigating to the next step. |\n| `on:back` | When the \"back\" button is clicked | `currentIndex` (number) | Fires when navigating to the previous step. |\n| `on:complete` | When the \"complete\" button is clicked | `currentIndex` (number) | Fires when completing a step. |\n| `on:finish` | When the \"finish\" button is clicked | `currentIndex` (number) | Fires when finishing the stepper. |\n\n### Event Usage Example\n\n```javascript\n// Get stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to step activation\n  element.on('active', (currentIndex) => {\n    console.log('Step activated:', currentIndex);\n    // Perform actions when step becomes active\n    // e.g., load content, initialize form fields\n  });\n\n  // Listen to before next (for validation)\n  element.on('beforeNext', (currentIndex) => {\n    console.log('Before next step:', currentIndex);\n    // Validate current step before proceeding\n    // Can prevent navigation if validation fails\n  });\n\n  // Listen to finish\n  element.on('finish', (currentIndex) => {\n    console.log('Stepper finished:', currentIndex);\n    // Perform final actions\n    // e.g., submit form, show success message\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Non-linear Mode\n\nAllow users to jump to any step.\n\n```html\n<div data-hs-stepper='{\n  \"mode\": \"non-linear\"\n}'>\n  <!-- Stepper content -->\n</div>\n```\n\n### Pattern 2: Error Handling\n\nSet error state on a step after validation fails.\n\n```html\n<div id=\"hs-stepper-first\" data-hs-stepper>\n  <!-- Stepper content -->\n</div>\n\n<script>\n  const instance = HSStepper.getInstance('#hs-stepper-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    // Validate and set error if needed\n    function validateStep(stepIndex) {\n      // Validation logic\n      if (validationFails) {\n        element.setErrorNavItem(stepIndex);\n      }\n    }\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/strong-password/README.md":"# Strong Password\n\nUse strong passwords based on required characters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/strong-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/strong-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Strong Password component provides real-time password strength validation with visual feedback. It checks password requirements such as minimum length, uppercase/lowercase letters, numbers, and special characters, displaying strength indicators and helpful hints.\n\n**Key Features:**\n- Real-time password strength checking\n- Visual strength indicators (strips)\n- Password requirement hints\n- Customizable strength rules\n- Popover mode for hints\n- Programmatic control via JavaScript API\n- Event system for strength tracking\n\n## Installation\n\nTo get started, install Strong Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/strong-password\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/strong-password */\n@source \"../node_modules/@preline/strong-password/*.js\";\n@import \"./node_modules/@preline/strong-password/variants.css\";\n@import \"./node_modules/@preline/strong-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/strong-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStrongPassword from \"@preline/strong-password/non-auto.mjs\";\n\n  new HSStrongPassword(document.querySelector(\"#strong-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/strong-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/strong-password\";\n```\n\n`@preline/strong-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStrongPassword from \"@preline/strong-password/non-auto\";\n\nHSStrongPassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#strong-password\");\nif (el) new HSStrongPassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a strong password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component shows password strength indicators as the user types.\n\n```html\n<div class=\"flex-1\">\n  <input type=\"password\" id=\"hs-strong-password-input-first\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm  focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400\">\n\n  <div data-hs-strong-password='{\n    \"target\": \"#hs-strong-password-input-first\",\n    \"stripClasses\": \"hs-strong-password:opacity-100 hs-strong-password-accepted:bg-green-500 h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n  }' id=\"hs-strong-password-first\" class=\"flex mt-2 -mx-1\"></div>\n</div>\n\n<div class=\"mb-3\">\n  <span id=\"hs-strong-password-weakness-first\"></span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-strong-password`: Required on the container element, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Optional hints container for displaying password requirements\n\n**Initial State:**\n- Password input is empty\n- Strength strips are hidden or inactive\n- Hints are not displayed (unless configured)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-strong-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-strong-password` | Container element | - | - | Activate a Strong Password by specifying on an element. The value should contain at least one lowercase number, one uppercase letter, one number, one special character (!%&@#$^*?_~), and minimum 6 characters. Should be added to the content block that contains the checks. |\n| `:target` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed. This must be a valid CSS selector. Required. |\n| `:hints` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed as hints container. This must be a valid CSS selector. There are some selectors that available for using inside the hints: `[data-hs-strong-password-hints-weakness-text]` determines which element should be observed as weakness text container. It should contain an array of strings, e.g. - [\"Empty\", \"Weak\", \"Medium\", \"Strong\", \"Very Strong\", \"Super Strong\"]. Each text is for the concrete strength value, e.g. \"Medium\" have index 2, and it will be set up if strength is also 2. \"Empty\" have index 0, and it will be shown if field is empty and have no any strength. `[data-hs-strong-password-hints-rule-text]` determines which element should be observed as rule text container. It could have one of the follow value: \"min-length\" \\| \"lowercase\" \\| \"uppercase\" \\| \"numbers\" \\| \"special-characters\". `[data-check]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule check icon container. `[data-uncheck]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule uncheck icon container. |\n| `:stripClasses` | Inside `data-hs-strong-password` | string | - | Defines CSS classes for each strip. These classes are applied to each strength indicator strip. |\n| `:minLength` | Inside `data-hs-strong-password` | number | `6` | Determines minimum password length. |\n| `:mode` | Inside `data-hs-strong-password` | `\"default\"` \\| `\"popover\"` | `\"default\"` | Determines which mode should be used. If `popover` is selected, the hints will be displayed as a popover. |\n| `:popoverSpace` | Inside `data-hs-strong-password` | number | `10` | Available if `mode` is set to `popover`. Determines the space between the popover and the target element. |\n| `:checksExclude` | Inside `data-hs-strong-password` | array | `[]` | Determines which checks should be excluded. It could contain an array of strings, e.g. - [\"min-length\", \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"]. |\n| `:specialCharactersSet` | Inside `data-hs-strong-password` | string | `!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_\\`{|}~` | Determines which special characters should be used for checking. It could contain a string of available characters, e.g. - \"!%&\". |\n\n**Example:**\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"minLength\": 8,\n  \"mode\": \"popover\",\n  \"stripClasses\": \"h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n}'>\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-strong-password:*` | A modifier that allows you to set Tailwind classes for the passed stripes. |\n| `hs-strong-password-accepted:*` | A modifier that allows you to set Tailwind classes when password strength is accepted. |\n\n## JavaScript API\n\nThe `HSStrongPassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a strong password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown hint position. Useful when scrolling or layout changes occur. |\n| `destroy()` | None | `void` | Destroys the strong password instance, removes all generated markup, classes, and event listeners. Use when removing strong password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStrongPassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStrongPassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSStrongPassword } \\| null` | Returns the strong password instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStrongPassword` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if strong password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Recalculating popover position**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = element;\n\n  // Recalculate popover position on scroll\n  document.addEventListener('scroll', () => {\n    element.recalculateDirection();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Strength:', element.strength);\n  console.log('Passed rules:', element.passedRules);\n  console.log('Is opened:', element.isOpened);\n\n  // Clean up when removing from DOM\n  function removeStrongPassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying strong password instance**\n```javascript\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-strong-password').remove();\n  });\n}\n```\n\n## Events\n\nStrong password instances emit events that can be listened to for strength tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When target element was changed | `{ strength: number, rules: string[] }` | Fires when the password input value changes. Returns an object with `strength` (0-4, current level of strength, default is 0) and `rules` (array of passed checks: \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"). |\n\n### Event Usage Example\n\n```javascript\n// Get strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ strength, rules }) => {\n    console.log('Password strength:', strength);\n    console.log('Passed rules:', rules);\n    // Perform actions based on strength\n    // e.g., enable/disable submit button, show messages\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Hints Container\n\nDisplay password requirements in a hints container.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"hints\": \"#hs-hints-container\"\n}'></div>\n\n<div id=\"hs-hints-container\">\n  <div data-hs-strong-password-hints-weakness-text></div>\n  <div data-hs-strong-password-hints-rule-text data-check=\"min-length\"></div>\n</div>\n```\n\n### Pattern 2: Popover Mode\n\nDisplay hints as a popover.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"mode\": \"popover\",\n  \"popoverSpace\": 10\n}'></div>\n```\n\n### Pattern 3: Custom Requirements\n\nExclude certain checks or customize special characters.\n\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"checksExclude\": [\"special-characters\"],\n  \"specialCharactersSet\": \"!@#$%\"\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tabs/README.md":"# Tabs\n\nTabs make it easy to switch between different views.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tabs) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tabs.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tabs component allows users to switch between different content sections within the same context. It supports keyboard navigation, programmatic control, and customizable event types for activation.\n\n**Key Features:**\n- Multiple tab panels in a single group\n- Keyboard navigation support (Arrow keys, Home, End, Enter)\n- Customizable activation events (click, hover)\n- Programmatic control via JavaScript API\n- Event system for tab change tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tabs plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tabs\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tabs */\n@source \"../node_modules/@preline/tabs/*.js\";\n@import \"./node_modules/@preline/tabs/variants.css\";\n@import \"./node_modules/@preline/tabs/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tabs/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTabs from \"@preline/tabs/non-auto.mjs\";\n\n  new HSTabs(document.querySelector(\"#tabs\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tabs` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tabs\";\n```\n\n`@preline/tabs/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTabs from \"@preline/tabs/non-auto\";\n\nHSTabs.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tabs\");\nif (el) new HSTabs(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tabs component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three tabs with corresponding content panels, with the first tab active by default.\n\n```html\n<nav class=\"flex gap-x-2\" aria-label=\"Tabs\" role=\"tablist\">\n  <button type=\"button\" class=\"active\" id=\"hs-unstyled-tabs-item-first\" aria-selected=\"true\" data-hs-tab=\"#hs-unstyled-tabs-first\" aria-controls=\"hs-unstyled-tabs-first\" role=\"tab\">\n    Tab 1\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-second\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-second\" aria-controls=\"hs-unstyled-tabs-second\" role=\"tab\">\n    Tab 2\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-third\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-third\" aria-controls=\"hs-unstyled-tabs-third\" role=\"tab\">\n    Tab 3\n  </button>\n</nav>\n\n<div class=\"mt-3\">\n  <div id=\"hs-unstyled-tabs-first\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-first\">\n    This is the <em>first</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-second\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-second\">\n    This is the <em>second</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-third\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-third\">\n    This is the <em>third</em> item's tab body.\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `role=\"tablist\"`: Required on the container element (nav, div, etc.)\n- `role=\"tab\"`: Required on each tab button\n- `role=\"tabpanel\"`: Required on each content panel\n- `data-hs-tab`: Required on each tab button, must be a valid CSS selector pointing to the corresponding tabpanel\n- Unique `id` attributes for each tab button and its corresponding panel\n- Proper ARIA attributes (`aria-selected`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- To have a tab active by default, add the `active` class to the tab button and set `aria-selected=\"true\"`\n- For inactive tabs, add the `hidden` class to the tabpanel and set `aria-selected=\"false\"`\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| ArrowLeft / ArrowRight | Selects the previous/next non-disabled tab. |\n| ArrowUp / ArrowDown (in vertical mode) | Selects the previous/next non-disabled tab. |\n| Home / End | Selects the first/last non-disabled tab. |\n| Enter | Activates the selected tab. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure tab behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tabs` | Tablist container | object (JSON) | - | Contains configuration options for customizing tab behavior and appearance. |\n| `:eventType` | Inside `data-hs-tabs` | `\"click\"` \\| `\"hover\"` | `\"click\"` | Set `hover` to activate tabs on mouseover, or `click` for manual clicks (the default). |\n| `data-hs-tab` | Tab button (trigger) | string (CSS selector) | - | Activate a tab by specifying the selector of the target tabpanel. This must be a valid CSS selector pointing to the tabpanel element. |\n| `data-hs-tab-select` | Tablist container | string (ID) | - | You can pass a select ID there. Each option of this select must have a value equal to the tab ID (e.g., `<option value=\"#hs-tab-to-select-first\">Tab 1</option>`). When you select this value, the corresponding tab will be opened. |\n\n**Example:**\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n</nav>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tab-active:*` | A modifier that allows you to set Tailwind classes when the tab is active. Can be applied to both the toggle button and the content panel. |\n\n## JavaScript API\n\nThe `HSTabs` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tabs instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the tabs instance, removes all generated markup, classes, and event listeners. Use when removing tabs from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTabs` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTabs.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTabs \\| { id: string \\| number, element: HSTabs } \\| null` | Returns the tabs instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTabs` instance. If `isInstance` is `false` or omitted, returns the `HSTabs` instance directly. Returns `null` if tabs instance is not found. |\n| `HSTabs.open(target)` | `target`: `HTMLElement` | `void` | Opens the tab identified by target. The target should be a tab button element. Static method to programmatically switch tabs. |\n\n### Usage Examples\n\n**Example 1: Destroying tabs instance**\n```javascript\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Opening tab programmatically (static method)**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\nopenBtn.addEventListener('click', () => {\n  const tabButton = document.querySelector('#hs-tab-first');\n  \n  if (tabButton) {\n    HSTabs.open(tabButton);\n  }\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current tab:', element.current);\n  console.log('Current content:', element.currentContent);\n\n  // Clean up when removing from DOM\n  function removeTabs() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nTabs instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When any tab is changed | `{ el: HTMLElement, prev: string, current: string }` | Fires when a tab is switched. Returns an object with:<br>- `el`: The toggle button element that was clicked<br>- `prev`: Previous tab ID<br>- `current`: Current tab ID |\n\n### Event Usage Example\n\n```javascript\n// Get tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ el, prev, current }) => {\n    console.log('Tab changed:', {\n      clickedButton: el,\n      previousTab: prev,\n      currentTab: current\n    });\n    // Perform actions after tab changes\n    // e.g., load content, update UI, track analytics\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Activation\n\nActivate tabs on hover instead of click.\n\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-second\" role=\"tab\">Tab 2</button>\n</nav>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tabs from external buttons.\n\n```html\n<nav id=\"hs-tabs-first\" aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-content-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-content-second\" role=\"tab\">Tab 2</button>\n</nav>\n\n<button id=\"hs-open-tab-first\">Open Tab 1</button>\n<button id=\"hs-open-tab-second\">Open Tab 2</button>\n\n<script>\n  document.querySelector('#hs-open-tab-first').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-first\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n\n  document.querySelector('#hs-open-tab-second').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-second\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/textarea-auto-height/README.md":"# Textarea Autoheight\n\nAutomatically sets the height of the textarea.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/textarea-auto-height) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/textarea-autoheight.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Textarea Autoheight component automatically adjusts the height of a textarea element based on its content. As users type, the textarea expands or contracts to fit the text, eliminating the need for scrollbars.\n\n**Key Features:**\n- Automatic height adjustment based on content\n- Smooth resizing as user types\n- Configurable default height\n- Programmatic control via JavaScript API\n- No scrollbars needed\n\n## Installation\n\nTo get started, install Textarea Autoheight plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/textarea-auto-height\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/textarea-auto-height */\n@import \"./node_modules/@preline/textarea-auto-height/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/textarea-auto-height/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto.mjs\";\n\n  new HSTextareaAutoHeight(document.querySelector(\"#textarea\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/textarea-auto-height` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/textarea-auto-height\";\n```\n\n`@preline/textarea-auto-height/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto\";\n\nHSTextareaAutoHeight.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#textarea\");\nif (el) new HSTextareaAutoHeight(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a textarea autoheight component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The textarea automatically adjusts its height as content is entered.\n\n```html\n<textarea data-hs-textarea-auto-height></textarea>\n```\n\n**Structure Requirements:**\n- `data-hs-textarea-auto-height`: Required on the textarea element\n- Standard `<textarea>` element\n\n**Initial State:**\n- Textarea starts at default height (or configured `defaultHeight`)\n- Height adjusts automatically as content is entered or removed\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-textarea-auto-height` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-textarea-auto-height` | Textarea element | - | - | If this attribute is added to the textarea, then automatic height calculation according to the content is enabled. Should be added to the textarea itself. |\n| `:defaultHeight` | Inside `data-hs-textarea-auto-height` | number | `0` | Sets the default height (when no text is entered). When set to `0`, the textarea uses its natural height. |\n\n**Example:**\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 50\n}'></textarea>\n```\n\n## JavaScript API\n\nThe `HSTextareaAutoHeight` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a textarea autoheight instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the textarea autoheight instance, removes all generated markup, classes, and event listeners. Use when removing textarea autoheight from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTextareaAutoHeight` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTextareaAutoHeight.getInstance(target, isInstance)` | `target`: `HTMLTextAreaElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTextareaAutoHeight \\| { id: string \\| number, element: HSTextareaAutoHeight } \\| null` | Returns the textarea autoheight instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTextareaAutoHeight` instance. If `isInstance` is `false` or omitted, returns the `HSTextareaAutoHeight` instance directly. Returns `null` if textarea autoheight instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the textarea autoheight instance\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Default height:', element.defaultHeight);\n\n  // Clean up when removing from DOM\n  function removeTextareaAutoheight() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-textarea-autoheight').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Default Height\n\nSet a custom default height for the textarea.\n\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 100\n}'></textarea>\n```\n\n### Pattern 2: Multiple Autoheight Textareas\n\nApply autoheight to multiple textareas.\n\n```html\n<textarea id=\"hs-comment-first\" data-hs-textarea-auto-height></textarea>\n<textarea id=\"hs-comment-second\" data-hs-textarea-auto-height></textarea>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/theme-switch/README.md":"# Theme Switch\n\nStyle your site in dark mode with ready made Preline UI's dark mode toggle plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/theme-switch) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/theme-switch.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Toggling dark mode manually](#toggling-dark-mode-manually)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Theme Switch component provides functionality to toggle between light and dark themes. It manages theme state, saves preferences to localStorage, and integrates with Tailwind CSS dark mode functionality.\n\n**Key Features:**\n- Light/dark theme switching\n- System theme detection\n- LocalStorage persistence\n- Tailwind CSS integration\n- Programmatic control via JavaScript API\n- Event system for theme changes\n\n## Installation\n\nTo get started, install Theme Switch plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/theme-switch\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/theme-switch */\n@source \"../node_modules/@preline/theme-switch/*.js\";\n@import \"./node_modules/@preline/theme-switch/variants.css\";\n@import \"./node_modules/@preline/theme-switch/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/theme-switch/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSThemeSwitch from \"@preline/theme-switch/non-auto.mjs\";\n\n  new HSThemeSwitch(document.querySelector(\"#theme-switch\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/theme-switch` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/theme-switch\";\n```\n\n`@preline/theme-switch/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSThemeSwitch from \"@preline/theme-switch/non-auto\";\n\nHSThemeSwitch.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#theme-switch\");\nif (el) new HSThemeSwitch(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a theme switch component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles between light and dark themes.\n\n```html\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:hidden block\" data-hs-theme-click-value=\"dark\">\n  Dark\n</button>\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:inline-flex hidden\" data-hs-theme-click-value=\"light\">\n  Light\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-theme-click-value`: Required on button elements, specifies the theme value (`'default'`, `'dark'`, or `'auto'`)\n- `hs-dark-mode`: Required class for theme-aware styling\n- Buttons can be separate elements or combined into one toggle\n\n**Initial State:**\n- Theme is determined by system preference or saved preference\n- Appropriate button is shown/hidden based on current theme\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-theme-click-value` | Button element | `'default'` \\| `'dark'` \\| `'auto'` | `'default'` | When you click on an element with this attribute, the theme changes to the one specified in the attribute. Should be added to the button (trigger). |\n| `data-hs-theme-switch` | Input element (checkbox/radio) | - | - | When you change an element with this attribute, the theme changes to the opposite one. Element should have change event, e.g. checkbox or radio button. Should be added to the input (checkbox). |\n\n**Example:**\n```html\n<!-- Click-based toggle -->\n<button data-hs-theme-click-value=\"dark\">Dark Mode</button>\n\n<!-- Change-based toggle -->\n<input type=\"checkbox\" data-hs-theme-switch>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dark-mode-active:*` | Defines CSS classes when dark mode is ON |\n| `hs-default-mode-active:*` | Defines CSS classes when default (light) mode is ON |\n| `hs-default-auto-active:*` | Defines CSS classes according to the System theme |\n\n## JavaScript API\n\nThe `HSThemeSwitch` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a theme switch instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setAppearance(theme, isSaveToLocalStorage, isSetDispatchEvent)` | `theme`: `string` (optional, `'dark'` \\| `'light'` \\| `'default'`)<br>`isSaveToLocalStorage`: `boolean` (optional)<br>`isSetDispatchEvent`: `boolean` (optional) | `void` | Sets the appearance/theme programmatically. `theme` specifies the theme to set. `isSaveToLocalStorage` determines whether to save to localStorage (default: `true`). `isSetDispatchEvent` determines whether to dispatch the theme change event (default: `true`). |\n| `destroy()` | None | `void` | Destroys the theme switch instance, removes all generated markup, classes, and event listeners. Use when removing theme switch from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSThemeSwitch` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSThemeSwitch.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSThemeSwitch } \\| null` | Returns the theme switch instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSThemeSwitch` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if theme switch instance is not found. |\n\n### Usage Examples\n\n**Example 1: Setting theme programmatically**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set dark theme\n  element.setAppearance('dark');\n\n  // Set light theme\n  element.setAppearance('light');\n\n  // Set theme without saving to localStorage\n  element.setAppearance('dark', false);\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current theme:', element.theme);\n  console.log('Type:', element.type);\n\n  // Clean up when removing from DOM\n  function removeThemeSwitch() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying theme switch instance**\n```javascript\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('[data-hs-theme-click-value]').remove();\n  });\n}\n```\n\n## Events\n\nTheme switch instances emit global events that can be listened to for theme change tracking.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on-hs-appearance-change` | Every time the mode changes | None | An event that fires every time the mode changes. This is a global window event, not an instance event. |\n\n### Event Usage Example\n\n```javascript\n// Listen to global theme change event\nwindow.addEventListener('on-hs-appearance-change', () => {\n  console.log('Theme changed!');\n  // Perform actions after theme changes\n  // e.g., update UI, reload resources, track analytics\n});\n```\n\n## Common Patterns\n\n### Pattern 1: Click-based Toggle\n\nUse buttons to toggle between themes.\n\n```html\n<button data-hs-theme-click-value=\"dark\">Dark</button>\n<button data-hs-theme-click-value=\"light\">Light</button>\n```\n\n### Pattern 2: Checkbox Toggle\n\nUse a checkbox to toggle theme.\n\n```html\n<input type=\"checkbox\" data-hs-theme-switch>\n<label>Dark Mode</label>\n```\n\n### Pattern 3: Programmatic Control\n\nControl theme programmatically.\n\n```html\n<button id=\"hs-dark-btn\">Dark</button>\n<button id=\"hs-light-btn\">Light</button>\n\n<script>\n  const instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-dark-btn').addEventListener('click', () => {\n      element.setAppearance('dark');\n    });\n\n    document.querySelector('#hs-light-btn').addEventListener('click', () => {\n      element.setAppearance('light');\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/toggle-count/README.md":"# Toggle Count\n\nToggle count from one value to another.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-count) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-count.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Count component provides animated counting functionality that transitions between two numeric values. It's commonly used for pricing displays, statistics, or any scenario where values need to animate between states (e.g., monthly/annual pricing).\n\n**Key Features:**\n- Animated counting between values\n- Configurable animation duration\n- Programmatic control via JavaScript API\n- Smooth transitions\n- Support for multiple count elements\n\n## Installation\n\nTo get started, install Toggle Count plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-count\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-count */\n@import \"./node_modules/@preline/toggle-count/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-count/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSToggleCount from \"@preline/toggle-count/non-auto.mjs\";\n\n  new HSToggleCount(document.querySelector(\"#toggle-count\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-count` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-count\";\n```\n\n`@preline/toggle-count/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSToggleCount from \"@preline/toggle-count/non-auto\";\n\nHSToggleCount.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-count\");\nif (el) new HSToggleCount(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle count component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The count animates between min and max values when the toggle changes.\n\n```html\n<div>\n  <div id=\"hs-toggle-count-first\">\n    <label>\n      Monthly\n      <input id=\"hs-toggle-count-monthly-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\">\n    </label>\n    <label>\n      Annual\n      <input id=\"hs-toggle-count-annual-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\" checked/>\n    </label>\n  </div>\n</div>\n\n<div class=\"space-x-5\">\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 19,\n    \"max\": 29\n  }'>\n    19\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 89,\n    \"max\": 99\n  }'>\n    89\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 129,\n    \"max\": 149\n  }'>\n    129\n  </span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-count`: Required on the element that displays the count, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the toggle element (radio buttons or checkbox)\n- Toggle element (radio buttons or checkbox) that triggers the count change\n- Multiple count elements can share the same target\n\n**Initial State:**\n- Count displays the `min` value initially\n- When toggle changes, count animates to `max` value\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-count` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-count` | Count display element | - | - | Activate a Toggle Count by specifying on an element. Should be added to the container. |\n| `:target` | Inside `data-hs-toggle-count` | string (CSS selector) | - | Determines which element will be observed. This must be a valid selector pointing to the toggle element (radio buttons or checkbox). Required. |\n| `:min` | Inside `data-hs-toggle-count` | number | `0` | Specifies default number (starting value). |\n| `:max` | Inside `data-hs-toggle-count` | number | `0` | Specifies the number to which the count up will go (ending value). |\n| `:duration` | Inside `data-hs-toggle-count` | number | `700` | Counting speed (animation duration in milliseconds). |\n\n**Example:**\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 10,\n  \"max\": 50,\n  \"duration\": 1000\n}'>\n  10\n</span>\n```\n\n## JavaScript API\n\nThe `HSToggleCount` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle count instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `countUp()` | None | `void` | Force count up animation. Animates from `min` to `max` value. |\n| `countDown()` | None | `void` | Force count down animation. Animates from `max` to `min` value. |\n| `destroy()` | None | `void` | Destroys the toggle count instance, removes all generated markup, classes, and event listeners. Use when removing toggle count from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSToggleCount` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSToggleCount.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSToggleCount \\| { id: string \\| number, element: HSToggleCount } \\| null` | Returns the toggle count instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSToggleCount` instance. If `isInstance` is `false` or omitted, returns the `HSToggleCount` instance directly. Returns `null` if toggle count instance is not found. |\n\n### Usage Examples\n\n**Example 1: Force count up**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const countUpBtn = document.querySelector('#hs-count-up-btn');\n\n  countUpBtn.addEventListener('click', () => {\n    element.countUp();\n  });\n}\n```\n\n**Example 2: Force count down**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = element;\n  const countDownBtn = document.querySelector('#hs-count-down-btn');\n\n  countDownBtn.addEventListener('click', () => {\n    element.countDown();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Min value:', element.min);\n  console.log('Max value:', element.max);\n  console.log('Duration:', element.duration);\n  console.log('Is checked:', element.isChecked);\n\n  // Clean up when removing from DOM\n  function removeToggleCount() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle count instance**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-count').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Pricing Toggle\n\nToggle between monthly and annual pricing.\n\n```html\n<div id=\"hs-pricing-toggle\">\n  <input type=\"radio\" name=\"pricing\" id=\"monthly\" checked>\n  <input type=\"radio\" name=\"pricing\" id=\"annual\">\n</div>\n\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-pricing-toggle\",\n  \"min\": 19,\n  \"max\": 199\n}'>\n  $19\n</span>\n```\n\n### Pattern 2: Custom Duration\n\nSet custom animation duration.\n\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 0,\n  \"max\": 100,\n  \"duration\": 2000\n}'>\n  0\n</span>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/toggle-password/README.md":"# Toggle Password\n\nShow or hide password field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Password component provides a button to toggle password visibility in input fields. It allows users to switch between showing and hiding password text, improving usability while maintaining security.\n\n**Key Features:**\n- Toggle password visibility\n- Show/hide password text\n- Support for multiple password fields\n- Grouped toggle support\n- Programmatic control via JavaScript API\n- Event system for visibility tracking\n\n## Installation\n\nTo get started, install Toggle Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-password\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-password */\n@import \"./node_modules/@preline/toggle-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTogglePassword from \"@preline/toggle-password/non-auto.mjs\";\n\n  new HSTogglePassword(document.querySelector(\"#toggle-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-password\";\n```\n\n`@preline/toggle-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTogglePassword from \"@preline/toggle-password/non-auto\";\n\nHSTogglePassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-password\");\nif (el) new HSTogglePassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles password visibility.\n\n```html\n<div class=\"relative\">\n  <input id=\"hs-toggle-password-first\" type=\"password\" name=\"password\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-400\" value=\"12345qwerty\" required>\n  <button type=\"button\" data-hs-toggle-password='{\n      \"target\": \"#hs-toggle-password-first\"\n    }' class=\"absolute inset-y-0 end-0 flex items-center pointer-events-none z-10 p-3.5\">\n    <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n      <path class=\"hs-password-active:hidden\" d=\"M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"/>\n    </svg>\n    <span class=\"hs-password-active:hidden\">\n      <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n        <path d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"></path>\n        <path d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"></path>\n      </svg>\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-password`: Required on the toggle button, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Toggle button can be positioned anywhere (commonly inside a relative container)\n\n**Initial State:**\n- Password input is hidden (type=\"password\")\n- Toggle button shows \"show\" icon\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-password` | Toggle button | - | - | Activate a Toggle Password by specifying on an element. Should be added to the button (trigger). |\n| `:target` | Inside `data-hs-toggle-password` | string (CSS selector) | - | Determines which element will change type on click. This must be a valid CSS selector pointing to the password input field. Required. |\n| `:isShown` | Inside `data-hs-toggle-password` | boolean | `false` | Determines password visibility initially. When `true`, password is shown by default. |\n| `:eventType` | Inside `data-hs-toggle-password` | `'change'` \\| `'click'` | `'click'` | Determines event type. `click` for button clicks, `change` for checkbox/radio changes. |\n| `data-hs-toggle-password-group` | Container element | - | - | This option allows you to group multiple toggle password elements together so that they can be toggled simultaneously. It should be added to the parent element of the toggle password elements. Should be added to the container. |\n\n**Example:**\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": false,\n  \"eventType\": \"click\"\n}'>\n  Toggle\n</button>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-password-active:*` | A modifier that allows you to set Tailwind classes when the password was shown. |\n\n## JavaScript API\n\nThe `HSTogglePassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Toggle the field to type text (show password). Changes input type from `password` to `text`. |\n| `hide()` | None | `void` | Toggle the field to type password (hide password). Changes input type from `text` to `password`. |\n| `destroy()` | None | `void` | Destroys the toggle password instance, removes all generated markup, classes, and event listeners. Use when removing toggle password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTogglePassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTogglePassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTogglePassword \\| { id: string \\| number, element: HSTogglePassword } \\| null` | Returns the toggle password instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTogglePassword` instance. If `isInstance` is `false` or omitted, returns the `HSTogglePassword` instance directly. Returns `null` if toggle password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Showing password programmatically**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n}\n```\n\n**Example 2: Hiding password programmatically**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const hideBtn = document.querySelector('#hs-hide-btn');\n\n  hideBtn.addEventListener('click', () => {\n    element.hide();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Is shown:', element.isShown);\n  console.log('Target:', element.target);\n\n  // Clean up when removing from DOM\n  function removeTogglePassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle password instance**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-password').remove();\n  });\n}\n```\n\n## Events\n\nToggle password instances emit events that can be listened to for visibility tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:toggle` | When password was shown or hidden | `HTMLElement` (target input element) | Fires when password visibility changes. Returns the target password input element. |\n\n### Event Usage Example\n\n```javascript\n// Get toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to toggle event\n  element.on('toggle', (target) => {\n    console.log('Password visibility toggled:', target);\n    // Perform actions after password visibility changes\n    // e.g., track analytics, update UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Grouped Toggle\n\nGroup multiple password fields to toggle simultaneously.\n\n```html\n<div data-hs-toggle-password-group>\n  <input type=\"password\" id=\"password-1\">\n  <button data-hs-toggle-password='{\"target\": \"#password-1\"}'>Toggle</button>\n  \n  <input type=\"password\" id=\"password-2\">\n  <button data-hs-toggle-password='{\"target\": \"#password-2\"}'>Toggle</button>\n</div>\n```\n\n### Pattern 2: Initially Shown\n\nShow password by default.\n\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": true\n}'>\n  Toggle\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tooltip/README.md":"# Tooltip & Popover\n\nA tooltip is a floating, non-actionable label used to explain a user interface element or feature.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tooltip) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tooltip.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tooltip component provides contextual information when users hover over or focus on an element. It supports multiple placement options, customizable triggers, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (hover, focus)\n- Floating UI integration for smart positioning\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility support (ARIA attributes)\n\n## Installation\n\nTo get started, install Tooltip & Popover plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tooltip\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tooltip */\n@source \"../node_modules/@preline/tooltip/*.js\";\n@import \"./node_modules/@preline/tooltip/variants.css\";\n@import \"./node_modules/@preline/tooltip/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tooltip/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTooltip from \"@preline/tooltip/non-auto.mjs\";\n\n  new HSTooltip(document.querySelector(\"#tooltip\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tooltip` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tooltip\";\n```\n\n`@preline/tooltip/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTooltip from \"@preline/tooltip/non-auto\";\n\nHSTooltip.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tooltip\");\nif (el) new HSTooltip(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tooltip component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The tooltip appears when hovering over the button.\n\n```html\n<div class=\"hs-tooltip [--placement:auto] inline-block\">\n  <button type=\"button\" class=\"hs-tooltip-toggle\">\n    Hover me\n    <span class=\"hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-gray-900 text-white\" role=\"tooltip\">\n      Tooltip on top\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-tooltip`: Required container wrapper\n- `hs-tooltip-toggle`: Required element that triggers the tooltip (button, link, etc.)\n- `hs-tooltip-content`: Required element containing the tooltip content\n- Proper ARIA attributes (`role=\"tooltip\"`)\n\n## Configuration Options\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control tooltip behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--trigger:*]` | `hs-tooltip` (container) | `\"focus\"` \\| `\"hover\"` | `\"hover\"` | Event to trigger a tooltip. `hover` shows on mouse enter, `focus` shows on focus. |\n| `[--placement:*]` | `hs-tooltip` (container) | `\"auto\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"auto\"` (top → bottom, left → right) | Tooltip placement relative to the trigger element. If `\"auto\"`, the tooltip will be positioned based on available space. |\n| `[--strategy:*]` | `hs-tooltip` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"absolute\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--prevent-popper:*]` | `hs-tooltip` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Prevents Floating UI positioning calculation issues. Set to `\"true\"` if experiencing positioning problems. |\n\n**Example:**\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the tooltip to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-tooltip` | Container wrapper | Groups the toggle and content elements together |\n| `hs-tooltip-toggle` | Trigger element | The element that activates the tooltip (button, link, etc.) |\n| `hs-tooltip-content` | Content element | The tooltip content that appears on trigger |\n\n## JavaScript API\n\nThe `HSTooltip` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tooltip instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Forces the tooltip to show programmatically. |\n| `hide()` | None | `void` | Forces the tooltip to hide programmatically. |\n| `destroy()` | None | `void` | Destroys the tooltip instance, removes all generated markup, classes, and event listeners. Use when removing tooltip from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTooltip` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTooltip.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSTooltip } \\| HTMLElement \\| null` | Returns the tooltip instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTooltip` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tooltip instance is not found. |\n| `HSTooltip.show(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSTooltip.hide(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new tooltip instance\nconst tooltip = new HSTooltip(document.querySelector('#hs-tooltip'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip programmatically\nshowBtn.addEventListener('click', () => {\n  tooltip.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSTooltip.show(document.querySelector('#hs-tooltip'));\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSTooltip instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeTooltip() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying tooltip instance**\n```javascript\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tooltip').remove();\n  });\n}\n```\n\n## Events\n\nTooltip instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:show` | After tooltip is shown | `HSTooltip` (tooltip instance) | Fires after the tooltip has been displayed. Use for post-show actions like tracking analytics. |\n| `on:hide` | After tooltip is hidden | `HSTooltip` (tooltip instance) | Fires after the tooltip has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to show event\n  element.on('show', (tooltipInstance) => {\n    console.log('Tooltip shown:', tooltipInstance);\n    // Perform actions after tooltip shows\n    // e.g., track analytics, update UI\n  });\n\n  // Listen to hide event\n  element.on('hide', (tooltipInstance) => {\n    console.log('Tooltip hidden:', tooltipInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Focus-based Tooltip\n\nShow tooltip on focus for keyboard accessibility.\n\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Helpful information</span>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tooltip from external buttons.\n\n```html\n<div class=\"hs-tooltip\" id=\"hs-tooltip-first\">\n  <button class=\"hs-tooltip-toggle\">Hover me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n\n<button id=\"hs-show-tooltip\">Show Tooltip</button>\n<button id=\"hs-hide-tooltip\">Hide Tooltip</button>\n\n<script>\n  const instance = HSTooltip.getInstance('#hs-tooltip-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-tooltip').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-tooltip').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tree-view/README.md":"# Tree View\n\nTree View solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tree-view) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tree-view.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tree View component provides a hierarchical structure for displaying nested data, similar to file explorers. It integrates with the Accordion component to create expandable/collapsible tree nodes and supports both checkbox and button selection modes.\n\n**Key Features:**\n- Hierarchical nested structure\n- Expandable/collapsible nodes (using Accordion)\n- Multiple selection modes (checkbox, button)\n- Auto-select children option for directories\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tree View plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tree-view @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n\n/* @preline/tree-view */\n@source \"../node_modules/@preline/tree-view/*.js\";\n@import \"./node_modules/@preline/tree-view/variants.css\";\n@import \"./node_modules/@preline/tree-view/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n<script src=\"./node_modules/@preline/tree-view/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. Tree View depends on Accordion for expand/collapse behavior, so call `HSAccordion.autoInit()` first and then initialize the specific Tree View element manually.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n  import HSTreeView from \"@preline/tree-view/non-auto.mjs\";\n\n  HSAccordion.autoInit();\n  new HSTreeView(document.querySelector(\"#tree-view\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. Initialize `HSAccordion` first, since it powers the expand/collapse nodes inside the tree.\n\nThe first example uses the auto-init entries. This means the plugins scan the DOM and initialize matching elements automatically.\n\nThe second example uses the manual `non-auto` entries. Use this when you want explicit control over initialization order and timing, or when you need to initialize a specific Tree View instance yourself.\n\n```js\nimport \"@preline/accordion\";\nimport \"@preline/tree-view\";\n```\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\nimport HSTreeView from \"@preline/tree-view/non-auto\";\n\n// Initialize all matching accordion nodes first\nHSAccordion.autoInit();\n\n// Then initialize all matching tree views\nHSTreeView.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tree-view\");\nif (el) new HSTreeView(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tree view component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows a directory structure with expandable folders and files.\n\n```html\n<div role=\"tree\" aria-orientation=\"vertical\" data-hs-tree-view>\n  <div class=\"hs-accordion active\" role=\"treeitem\" aria-expanded=\"true\" id=\"hs-tree-view-heading-first\" data-hs-tree-view-item='{\n    \"value\": \"assets\",\n    \"isDir\": true\n  }'>\n    <div class=\"hs-accordion-heading\">\n      <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-tree-view-collapse-first\">\n        <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path class=\"hs-accordion-active:hidden block\" d=\"M12 5v14\"/></svg>\n      </button>\n      assets\n    </div>\n\n    <div id=\"hs-tree-view-collapse-first\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"group\" aria-labelledby=\"hs-tree-view-heading-first\">\n      <div role=\"treeitem\" data-hs-tree-view-item='{\n        \"value\": \"image.jpg\"\n      }'>image.jpg</div>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-tree-view`: Required on the root container element\n- `role=\"tree\"`: Required on the root container\n- `aria-orientation=\"vertical\"`: Required on the root container\n- `hs-accordion`: Required for each directory/folder node\n- `data-hs-tree-view-item`: Required on each tree item (files and directories)\n- Proper ARIA attributes (`role=\"treeitem\"`, `aria-expanded`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- Directories can be expanded/collapsed using accordion functionality\n- Items can be selected based on the `controlBy` mode\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-tree-view` and `data-hs-tree-view-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tree-view` | Root container | - | - | Activate a Tree View by specifying on an element. Should be added to the wrapper container. |\n| `:controlBy` | Inside `data-hs-tree-view` | `\"checkbox\"` \\| `\"button\"` | `\"button\"` | Tells the plugin which mode is active and processes events accordingly. `checkbox` mode allows multiple selections, `button` mode allows single selection. |\n| `:autoSelectChildren` | Inside `data-hs-tree-view` | boolean | `false` | This option is available if `controlBy` is set to `checkbox` and if the item is a directory (`isDir: true`). When `true`, if a parent directory is selected, all nested items receive the same selection state. |\n| `:isIndeterminate` | Inside `data-hs-tree-view` | boolean | `true` | Adds indeterminate visual style for checkboxes when `controlBy` is set to `checkbox`. Shows when some but not all children are selected. |\n| `data-hs-tree-view-item` | Each tree item | - | - | Determines which element inside the initialized component is the item. Should be added to the item itself. |\n| `:id` | Inside `data-hs-tree-view-item` | string | - | Optional. Desired identifier for the item. |\n| `:value` | Inside `data-hs-tree-view-item` | string | - | The value that will be passed to the resulting array when item is selected. |\n| `:isDir` | Inside `data-hs-tree-view-item` | boolean | `false` | Determines that the element is a directory and processes it accordingly. Directories can contain nested items. |\n\n**Example:**\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true,\n  \"isIndeterminate\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tree-view-selected:*` | A modifier that allows you to set Tailwind classes when item has been selected. |\n| `hs-tree-view-disabled:*` | A modifier that allows you to set Tailwind classes when item has \"disabled\" class. |\n\n## JavaScript API\n\nThe `HSTreeView` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tree view instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `update()` | None | `void` | Updates the element. This is suitable, for example, in case of changing the order of elements or dynamically adding/removing items. |\n| `getSelectedItems()` | None | `ITreeViewItem[]` | Returns a list of selected items as an array of objects with item properties. |\n| `changeItemProp(id, prop, val)` | `id`: `string`<br>`prop`: `string`<br>`val`: `any` | `void` | Changes a property of a specific item. Useful for updating item state programmatically. |\n| `destroy()` | None | `void` | Destroys the tree view instance, removes all generated markup, classes, and event listeners. Use when removing tree view from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTreeView` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTreeView.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSTreeView } \\| null` | Returns the tree view instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTreeView` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tree view instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting selected items**\n```javascript\n// Get the tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get all selected items\n  const selectedItems = element.getSelectedItems();\n\n  console.log('Selected items:', selectedItems);\n}\n```\n\n**Example 2: Updating tree view after dynamic changes**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // After adding or removing items dynamically\n  element.update();\n}\n```\n\n**Example 3: Changing item properties programmatically**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Change item property\n  element.changeItemProp('item-id', 'isSelected', true);\n}\n```\n\n**Example 4: Destroying tree view instance**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tree-view').remove();\n  });\n}\n```\n\n## Events\n\nTree view instances emit events that can be listened to for selection tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:click` | When any item is selected | `{ id: string, value: string, isDir: boolean, path: string, isSelected: boolean }` | Fires when an item is clicked/selected. Returns an object with:<br>- `id`: Item identifier<br>- `value`: Item value<br>- `isDir`: Whether item is a directory<br>- `path`: Path to the item<br>- `isSelected`: Whether item is currently selected |\n\n### Event Usage Example\n\n```javascript\n// Get tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\nif (instance) {\n  const { element } = instance;\n\n  // Listen to click event\n  element.on('click', ({ id, value, isDir, path, isSelected }) => {\n    console.log('Item clicked:', {\n      id,\n      value,\n      isDirectory: isDir,\n      path,\n      isSelected\n    });\n    // Perform actions after item selection\n    // e.g., update UI, track analytics, load content\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Checkbox Mode with Auto-select Children\n\nEnable checkbox selection with automatic child selection.\n\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Pattern 2: Getting Selected Items\n\nRetrieve all selected items programmatically.\n\n```html\n<div id=\"hs-tree-view-first\" data-hs-tree-view>\n  <!-- Tree items -->\n</div>\n\n<button id=\"hs-get-selected\">Get Selected</button>\n\n<script>\n  const instance = HSTreeView.getInstance('#hs-tree-view-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-get-selected').addEventListener('click', () => {\n      const selected = element.getSelectedItems();\n      \n      console.log('Selected items:', selected);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n"},"files":{"skills/preline-mcp/SKILL.md":"---\nname: preline-mcp\ndescription: Add, integrate, or build Preline UI components and blocks into HTML files via the Preline MCP server. Use when the user mentions \"preline\", says \"/preline\", asks to add a UI component, or requests a Preline block (a ready-made page section) using Preline UI.\n---\n\n# Preline MCP\n\n## Intent\n\nUse this skill to integrate Preline UI components and blocks into HTML files via the Preline MCP server. The server exposes 6 tools that return integration-ready HTML, CSS, and JS.\n\nActivate when:\n- User says \"preline\", \"add preline\", \"/preline\", \"use preline\"\n- User asks for a Tailwind CSS component from the Preline library\n- User asks for a block (ready-made page section) or a starter page\n\n## Defaults\n\n- **Never guess slugs.** All section, component, and category identifiers are exact kebab-case strings. Always discover them via `components_list` or `blocks_categories` first.\n- **One at a time.** Retrieve one component or block, integrate it fully, then move to the next.\n- **Always read the target file** before inserting anything.\n- Call `components_list({ section: \"<inferred-slug>\" })` when the component type is known - this keeps the response small. Omit section only when unsure.\n\n## Decision: Component vs Block\n\n| User says | Use |\n|---|---|\n| \"component\" / \"components\" | Component workflow |\n| \"block\" / \"blocks\" (or \"example\" / \"examples\") | Block workflow |\n| Ambiguous | Use judgment; blocks give more complete results |\n\n## Clarifying Vague Requests\n\nAsk the user to pick instead of guessing in either of these cases:\n\n- **Abstract request.** Too abstract to confidently map onto one category/section, let alone one block/component - e.g. \"give me something for a pricing page\", \"I need a nice form\", \"show me a dashboard example\". Start at step 1 below.\n- **Tied candidates.** The request is concrete enough to reach a single category/section, but `blocks_in_category` / `components_list({ section })` still leaves 2+ candidates whose titles + descriptions satisfy every criterion the request named about equally well (same layout/feature/style class), with nothing in the request to break the tie. Skip straight to step 3 below with just the tied candidates.\n\nIf exactly one candidate matches everything the request names (\"a basic accordion\", \"the SaaS hero with tabs\"), skip straight to the normal workflow - don't interrogate the user for things you can already resolve, and don't ask just to be safe when one option is clearly the best fit.\n\nAll clarifying questions and lists are in **English by default** - switch to the user's language only once they write to you in it.\n\n**Blocks:**\n1. **Ask which category fits.** Call `blocks_categories()` and present the relevant `mainSection / subSection / category` paths with their titles and descriptions - the catalog carries a one-line description for every category, so present it directly rather than inventing your own summary.\n2. **Try to resolve in one round.** If the user's reply both names a category *and* describes the item distinctively enough to identify a single match (a specific layout, feature, or style), call `blocks_in_category` to confirm the exact ID and go straight to `single_block` - skip step 3 entirely.\n3. **Otherwise, ask which block fits best.** Call `blocks_in_category({ mainSection, subSection, category })`, list the block titles + descriptions - or just the tied candidates, for the case above - and ask the user to pick the one closest to their needs.\n4. **Fetch and integrate** the chosen block with `single_block`.\n\n**Components - same shape, sections instead of categories:**\n1. Ask which **section** fits (`components_list()` for the full list, or `components_list({ section })` once you can infer one).\n2. If the reply names a section *and* describes the component distinctively enough → resolve directly via `single_component` (lean on `relative` metadata - see Smart Component Selection - to land on the right default/variant).\n3. Otherwise list the components in that section (titles + descriptions, with `relative.category` groupings where present - or just the tied candidates, for the case above) and ask which fits best.\n4. Fetch and integrate with `single_component`.\n\nKeep each round to one focused question with a short, scannable list - that beats an open \"what do you want?\" and beats guessing a slug just to avoid asking.\n\n## Composite & Layout Requests\n\nWhen a request describes **more than a single component** - several pieces, a region, a full page, or an app shell - don't fetch ad-hoc. Plan the whole result first, then fetch and integrate one node at a time. The same steps apply to any shape: a dashboard, a settings page, a product page, a multi-step form, \"a card/section/layout with X, Y and Z\", etc.\n\n1. **Lock cross-cutting constraints first.** Anything that applies to the whole result - class system (theme tokens vs utilities → `isUtilityBased` / `theme`), shared surfaces/colors, spacing/density, repeated element states - decide once, up front. Keep it identical across every later fetch and edit.\n2. **Decompose into a tree:** containers → regions → leaf components. Write it down; each leaf is one fetch.\n3. **Discover every node - route, then confirm.** Use [references/catalog-map.md](references/catalog-map.md) to route: an abstract intent (a whole page/region/shell) → the right `blocks_categories` branch (reuse a ready-made block as the skeleton when one fits); each named element → the right `components_list({ section })`. The map only tells you *where to look* - always confirm the exact slug against the tool output before inserting.\n4. **Assemble outermost-first, one at a time.** The skeleton/outer container sets the shared surfaces and the script/init anchors; then fill inward region by region, integrating each fully (Integration Rules) before the next. Keep the returned classes, change **text only** - except the specific surfaces/colors/states the user asked to change. Apply every structural adaptation to the markup **before** writing it (see *Adapt before you write* in Integration Rules).\n5. **Verify** the finished result against the request, element by element.\n\nSee [references/composite-layouts.md](references/composite-layouts.md) for the expanded method and worked illustrations.\n\n## Workflow: Components\n\n```\n1. components_list({ section: \"<slug>\" })   → get valid component IDs\n2. single_component({ section, component }) → get HTML/CSS/JS\n3. Read target file\n4. Integrate HTML, CSS, scripts, init (see Integration Rules)\n5. Repeat from step 1 only after integration is complete\n```\n\n## Workflow: Blocks\n\n```\n1. blocks_categories()                                    → get hierarchy\n2. blocks_in_category({ mainSection, subSection, category }) → get block IDs\n3. single_block({ mainSection, subSection, category, block }) → get HTML/CSS/JS\n4. Read target file\n5. Integrate HTML, CSS, scripts, init (see Integration Rules)\n6. Repeat from step 1 only after integration is complete\n```\n\n## Integration Rules\n\n**HTML** - insert where the user needs it.\n\n**CSS** (`<!-- CSS -->` section):\n- MUST go inside `<head>`, immediately before `</head>`\n- NEVER place in `<body>` or near `</body>`\n\n**External scripts** (`<!-- Scripts -->` section):\n- The `<!-- Scripts -->` label is a response section marker - NOT a location in the target file\n- Placement algorithm: open target file, scan **upward** from `</body>`, skipping blank lines, comments, and non-structural tags (`</script>`, `</style>`, etc.)\n- The **first structural closing tag** you reach (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`) is the anchor\n- Insert `<script src>` tags after that anchor, ordered around the existing Preline core script - the loaded `<script src>` whose `src` contains `preline` (e.g. `…/preline/dist/index.js`; exact path varies by install):\n  - scripts whose `src` does **not** contain `preline` (third-party libs: lodash, apexcharts, …) → **before** the Preline core script\n  - scripts whose `src` **does** contain `preline` (Preline helpers, e.g. `hs-*-helpers.js`) → **after** the Preline core script\n  - if no Preline core script exists yet, keep order: scripts without `preline`, then scripts with `preline`\n  - inline init `<script>` always comes last, immediately before `</body>`\n\n**Init** (`<!-- Init -->` section):\n- Place immediately before `</body>`\n- Wrap in `window.addEventListener('load', () => { ... })` unless the block already contains `<script>` tags\n\n**Large responses (artifacts):** when a response is written to a temp scratch file, read it once with the Read tool (use offset/limit for big files), copy the needed blocks into the target, then delete the scratch file. Don't pull the whole artifact into context if you only need to place it.\n\n**Adapt before you write.** When the request differs from the fetched markup - regions the user didn't ask for (breadcrumbs, demo menus, placeholder logos), different blocks or labels - produce the final markup in memory first, then write it into the target in ONE edit per region. Never insert fetched markup wholesale and refactor it with a chain of follow-up edits: every such edit re-transfers large markup, bloats context, and desyncs file state.\n\n**Same token = same color.** Theme tokens (`bg-navbar`, `bg-sidebar`, `bg-layer`, …) are consistent across a theme - to give two surfaces the same color, give them the same token class. Never resolve tokens to raw colors by reading the project's CSS.\n\n**Trust the returned markup - do not re-verify it.** The classes, structure, and `data-hs-*` attributes the MCP returns are valid Preline by construction. This is the single biggest time-sink to avoid. Do NOT:\n- grep, parse, or scan the user's compiled CSS (e.g. `main.css`) to \"confirm\" a class exists or to resolve what color a design token produces - Preline classes resolve at the consumer's build step, so absence from any one stylesheet means nothing;\n- write HTML/DOM/AST validators (Python `HTMLParser`, tag-balance checkers, etc.) - SVG and void elements trip naive parsers and produce false errors;\n- re-read a placed artifact or re-open the edited file just to \"double-check\" the generated code.\n\nPlace the markup, change text only, move on. **Verify against the request** (is every element the user named present?), never by auditing the generated code or the project's CSS.\n\n## Smart Component Selection\n\n`components_list` may return a `relative` object per component:\n- `isSectionDefault: true` - recommended default for the section; prefer when the request is vague\n  - Exception: if the description says \"multiple variants\" and the user wants one, pick a single-variant component instead\n- `category` - logical group (e.g. `\"color-variants\"`, `\"states\"`)\n- `isCategoryDefault: true` - recommended default for its category; prefer when the request implies a style group\n\n## Available Themes\n\n`default` (blue) · `harvest` (amber) · `retro` (fuchsia) · `moon` (grayscale) · `ocean` (cyan) · `bubblegum` (pink) · `cashmere` (mauve) · `autumn` (orange) · `olive` (green)\n\nPass via `isUtilityBased: true, theme: \"<name>\"` on `single_component` or `single_block`.\n\n## Key References\n\n- [references/catalog-map.md](references/catalog-map.md) - routing map: block intent ontology (whole pages/shells) + all component sections grouped by purpose, JS-plugin marked. Open this to pick the right `section` / blocks branch before any discovery call.\n- [references/composite-layouts.md](references/composite-layouts.md) - full method for multi-component / page / shell requests, with worked illustrations\n- [references/mcp-tools-reference.md](references/mcp-tools-reference.md) - full parameter specs, placement algorithm detail, all 6 tools\n","skills/preline-mcp/agents/openai.yaml":"interface:\n  display_name: \"Preline MCP\"\n  short_description: \"Add Preline UI components and blocks to HTML files via the Preline MCP server\"\n  default_prompt: \"Use $preline-mcp to add Preline UI components or blocks to the target HTML file via the Preline MCP server. Call components_list first to discover valid section and component slugs, then single_component to get the HTML/CSS/JS, then integrate into the target file.\"\n","skills/preline-mcp/references/catalog-map.md":"# Catalog Map - where intents and elements live\n\n> Generated from the catalog (blocks.json, components.json, constants.ts). Regenerate with `node scripts/gen-catalog-map.mjs`.\n> This is a **routing map**, not authority - always confirm exact slugs against live `blocks_categories` / `components_list` output before inserting.\n\nUse during the **Discover** step (SKILL.md → Composite & Layout Requests): map an abstract intent to a blocks branch, and each named element to a component section.\n\n---\n\n## Blocks - intent ontology (for whole pages / regions / shells)\n\nAbstract request (\"a pricing page\", \"a login screen\", \"an admin shell\", \"a checkout flow\")? Route to a branch, then `blocks_in_category({ mainSection, subSection, category })`. Whole app shells live under **Navigation & Layout → Sidebars & Shells**.\n\n- **Marketing** - Hero, Banners & Landing Intros · Site Navigation & Headers · Trust & Social Proof · Content & Editorial · Pricing & Conversion · Website Foundations\n- **Data Display** - Analytics & KPIs · Operational & Activity Views · Tables & Records\n- **UI Components** - Menus & Selection · Actions, Status & Progress · Lists & Links\n- **Forms** - Core Inputs & Layouts · Creation & Management · Filters & Refinement · Billing, Feedback & Conversion\n- **Authentication** - Sign In & Account Access · Onboarding & Activation\n- **Ecommerce** - Product Discovery · Product Detail & Purchase · Orders & Commerce Utilities\n- **Communication** - Chat & Inbox · Calls & Media · Contacts & Presence\n- **Productivity** - Calendars & Scheduling · Planning Boards\n- **Blog & Articles** - Editorial Pages · Blog Discovery & Sections\n- **Application Pages** - Workspaces & Admin · Profiles & Accounts\n- **Billing & Payments** - Billing & Payments\n- **Feedback & States** - Alerts & Recovery · Actions & Engagement · Consent & Compliance\n- **Search & Command Palettes** - Search & Navigation · Actions & Shortcuts\n- **Navigation & Layout** - Application Headers & Navigation · Sidebars & Shells · Title Bars & Footers · Blank, Error & Utility Layouts\n\n---\n\n## Components - section index (for individual elements)\n\nPass the section to keep the list small: `components_list({ section: \"<slug>\" })`. 📖 = has a `component_documentation` entry (JS API). Any interactive component still needs the Preline core script wired once.\n\n**Form inputs & controls:** `input` · `input-group` · `input-number`📖 · `textarea` · `select` · `advanced-select`📖 · `combobox`📖 · `searchbox` · `checkbox` · `radio` · `switch` · `color-picker` · `range-slider` · `advanced-range-slider`📖 · `datepicker` · `advanced-datepicker`📖 · `time-picker` · `pin-input`📖 · `strong-password`📖 · `toggle-password`📖 · `file-input` · `file-upload`📖 · `file-uploading-progress-form`\n\n**Menus & overlays:** `dropdown`📖 · `mega-menu` · `context-menu` · `modal` · `offcanvas` · `popover` · `tooltip`📖\n\n**Navigation:** `navbar` · `navs` · `sidebar` · `breadcrumb` · `pagination` · `tabs`📖 · `stepper`📖 · `scrollspy`📖 · `tree-view`📖\n\n**Feedback & status:** `alerts` · `toasts` · `toast-notifications` · `badge` · `progress` · `ratings` · `spinners` · `skeleton` · `legend-indicator`\n\n**Data display:** `tables` · `datatables` · `charts` · `datamaps` · `maps` · `list-group` · `lists` · `timeline`\n\n**Content & media:** `card` · `avatar` · `avatar-group` · `blockquote` · `images` · `carousel`📖 · `marquee` · `devices` · `chat-bubbles` · `text-editor` · `typography` · `static-icons`\n\n**Actions & misc:** `buttons` · `button-group` · `links` · `dividers` · `kbd` · `clipboard` · `copy-markup`📖 · `toggle-count`📖 · `dark-mode` · `confetti` · `drag-and-drop` · `collapse`📖 · `accordion`📖\n\n**Layout:** `container` · `grid` · `columns` · `layout-splitter`📖 · `custom-scrollbar`\n\n","skills/preline-mcp/references/composite-layouts.md":"# Composite & Layout Requests - Detailed Guide\n\nHow to handle any request bigger than a single component - multiple pieces, a region, a full page, an app shell, a wizard. Companion to `SKILL.md` → \"Composite & Layout Requests\".\n\nThe point: **the same five steps apply regardless of domain.** A dashboard shell, an ecommerce product page, a settings screen, a multi-step form, a marketing section, or \"a card with X, Y and Z\" all decompose the same way. Apply the method and let the catalog be the source of truth.\n\nThe mistake to avoid: firing `single_component` / `single_block` ad-hoc and stitching markup as you go. Plan the whole layout first; fetch and integrate one node at a time against that plan.\n\n---\n\n## The method\n\n### 1. Lock cross-cutting constraints first\nResolve anything that applies to the **whole** result before any fetch, because it governs every fetch and edit:\n- **Class system** - design tokens vs utilities. Drives `isUtilityBased` / `theme` on every call; keep it identical across the whole build.\n- **Shared surfaces / color / spacing / density** - anything the request describes as uniform across regions.\n- **Repeated element states** - pinned, selected, active, disabled, collapsed, aligned, icon-only, etc., noted against the element they modify (they change which variant you pick).\n\nDeciding these once prevents inconsistent output halfway through.\n\n### 2. Decompose into a tree\nContainers → regions → leaf components. Write it down. Each leaf is one fetch; grouping leaves by region lets you integrate a region at a time. The shape of the method is identical whether the request is two nested elements or a fifty-element page.\n\n### 3. Discover every node - never guess\n- **Larger structures** (shells, navbars, headers, full sections, pages) → `blocks_categories()`. If a ready-made block already covers the structure, reuse it as the skeleton instead of hand-building.\n- **Individual pieces** → `components_list({ section })`, then `single_component`.\n- Resolve **each named element in the request** to a real slug from the tool output. The \"Where things tend to live\" table below is only a starting point for *which section to open* - the catalog is authoritative, so confirm against its output and never insert an unverified slug.\n\n### 4. Assemble in order - one fetch + integrate at a time\n1. **Outermost / skeleton first.** The shell or top container sets the shared surfaces and - importantly - the script/init placement anchors for everything inside it.\n2. **Fill inward, region by region, leaf by leaf.** Integrate each fully (HTML, CSS in `<head>`, external scripts at the structural anchor, init before `</body>` - see Integration Rules) before fetching the next.\n3. **Honor the locked constraints on every insert.** Keep the classes the MCP returns and change **text only** - except for the specific surfaces/colors/states the user explicitly asked to change.\n4. **Plugins once for the page.** Interactive components share the Preline core script - add it and the init per the placement rules a single time; never duplicate the core script. For a plugin's JS API, events, or options, call `component_documentation`.\n\n### 5. Verify\nRe-read the request and check every named element, state, and constraint is present. Composite requests are easy to under-deliver; the checklist is the guard. Verify **against the request, not by auditing the generated code** - do not grep the project's CSS to confirm token classes exist, and do not run HTML/AST validators over the result (see SKILL.md → Integration Rules). The markup the MCP returned is valid by construction; the only open question is whether you placed every element the user asked for.\n\n---\n\n## Where things live\n\nA request describes UI in plain language; you still discover the real slug. To route an abstract intent (a whole page/region/shell) to the right blocks branch, and each named element to the right component section, use the generated **[catalog-map.md](catalog-map.md)** - it lists the block intent ontology and all component sections grouped by purpose (JS-plugin marked). It's a starting-point map, not authority: always confirm the exact slug against live `blocks_categories` / `components_list` output before inserting.\n\n---\n\n## On surfaces and tokens\n\nWhen the user wants semantic / design-token classes (not raw utilities), keep `isUtilityBased: false` and **preserve the token classes the MCP output already uses** - Preline returns semantic surface tokens, not hardcoded colors. Translate plain-language surface words to the matching tokens you see in the returned markup; do not substitute `bg-white` / `bg-gray-*` / hex, and do not invent token names.\n\nSurface tokens you'll commonly see in the output (recognize and reuse them; exact names can vary by version - match what the catalog emits):\n\n| Plain wording | Token family to look for |\n|---|---|\n| page background | `bg-background` |\n| panel / \"one gray part\" shared by regions | a muted/secondary surface, e.g. `bg-muted` / `bg-secondary` |\n| card / \"layer\" - white with borders | `bg-layer` + `border border-layer-line` (text `text-layer-foreground`) |\n| inset / control surface | `bg-surface` |\n| separators between regions | the matching `*-line` border token |\n\n---\n\n## Worked illustration (one shape of many)\n\nThe five steps are domain-agnostic. The same decomposition covers, for example:\n- **App shell** - outer shell (shared surface) → navbar region(s) → sidebar region → content (distinct surface).\n- **Product page** - gallery → details (title, price, variant pickers, actions) → tabs / reviews.\n- **Settings page** - section nav → form sections → save bar.\n- **Multi-step form** - stepper → step panels → footer actions.\n\nA fuller pass for an app-shell request such as *\"a navbar with a logo, a sidebar toggle, project / team / account dropdowns and dividers, links and a theme switcher; a left sidebar with search and links; navbar + sidebar as one gray panel, content as a white bordered layer; use theme-token classes\":*\n\n**1. Constraints (locked first):**\n- \"theme-token classes\" → `isUtilityBased: false`, no theme passed - and the same setting on every later call.\n- \"navbar + sidebar one gray panel\" → both regions share one muted/secondary surface token; no off-color seams.\n- \"content as a white bordered layer\" → content region uses the layer surface tokens (`bg-layer` + border).\n\n**2. Tree:**\n```\nshell (navbar + left sidebar = shared surface) + content (layer surface)\n├─ navbar · left:  logo · sidebar-toggle · project-dropdown {★pinned A, ★pinned B, Add, Manage}\n│                  · divider · team-dropdown {Add team, ✓selected \"Marketing\"}\n├─ navbar · right: button(s) · text/icon links · divider\n│                  · account-dropdown (avatar trigger) {Upgrade CTA, theme switch, icon links}\n├─ sidebar (below navbar): search input · links · (bottom) icon links\n└─ content: layer surface\n```\n\n**3. Discover (resolve every slug from output):**\n1. `blocks_categories()` → look for an Application / Admin / Dashboard / Navigation shell that already pairs a navbar + sidebar + content. If one fits → `single_block(...)` → use as the skeleton.\n2. If none fits, build the skeleton from the `navbar` + `sidebar` sections.\n3. For each remaining leaf, `components_list({ section })` then `single_component` - sections per the orientation table (`dropdown`, `dividers`, `avatar`, `dark-mode`/`switch`, `input`/`searchbox`, `buttons`, `links`).\n\n**4. Assemble (one fetch + integrate at a time):**\n1. Skeleton → establishes the shared gray surface, the layer content container, and the script anchors.\n2. Navbar-left leaves → navbar-right leaves → sidebar leaves, integrating each fully before the next.\n3. Keep returned classes, edit **text only**; apply only the requested surface changes (shared gray shell, layer content). Set per-element states (pinned/selected) using the component's own selected/checkbox variant.\n4. Wire plugin scripts/init **once** (sidebar toggle, dropdowns, theme switch all need the Preline core script - don't duplicate it). Use `component_documentation` for any plugin's JS API.\n\n**5. Verify** against the request: every named element, every state (pinned, selected), and both surface rules (one gray shell, layer content) present.\n","skills/preline-mcp/references/mcp-tools-reference.md":"# Preline MCP - Tools Reference\n\nFull parameter specs and placement rules for all 6 MCP tools.\n\n---\n\n## Tool 1: `components_list`\n\nBrowse available component sections and their component IDs.\n\n**Input:**\n- `section` (optional string) - pass the inferred section slug to get only that section. Omit to get all sections.\n\n**When to pass section:** whenever the component type is known (e.g. `components_list({ section: \"buttons\" })`). Omitting section loads 300k+ characters - avoid unless you genuinely don't know the section.\n\n**Returns:** JSON with section IDs, component IDs, titles, descriptions, and optional `relative` metadata.\n\n---\n\n## Tool 2: `single_component`\n\nGet HTML/CSS/JS for one Preline component.\n\n**Input:**\n- `section` (required) - exact section slug from `components_list`. Never guess.\n- `component` (required) - exact component ID from `components_list`. Never guess.\n- `isUtilityBased` (optional, default: `false`) - convert design tokens to Tailwind utility classes\n- `theme` (optional, default: `\"default\"`) - theme for utility conversion\n- `parameters` (optional) - template-specific overrides\n\n**Available themes:**\n\n| Slug | Color |\n|---|---|\n| `default` | Blue |\n| `harvest` | Amber |\n| `retro` | Fuchsia |\n| `moon` | Grayscale |\n| `ocean` | Cyan |\n| `bubblegum` | Pink |\n| `cashmere` | Mauve |\n| `autumn` | Amber/Orange |\n| `olive` | Green |\n\n**Examples:**\n```\nsingle_component({ section: \"accordion\", component: \"accordion-basic-usage-example\" })\nsingle_component({ section: \"alerts\", component: \"alerts-solid-color-variants\", isUtilityBased: true })\nsingle_component({ section: \"datatables\", component: \"datatables-basic-usage-example\" })\n```\n\n**Response sections:**\n- `=== COMPONENT HTML ===` - main markup\n- `<!-- CSS -->` - `<link>` and `<style>` tags\n- `<!-- Scripts -->` - `<script src>` tags\n- `<!-- Init -->` - initialization code\n- Large responses are written to a temp file; read via the Read tool\n\n---\n\n## Tool 3: `component_documentation`\n\nGet JavaScript API docs for plugin components.\n\n**Input:**\n- `component` (required) - plugin component name from the documented list\n\n**Available documented components:**\naccordion, advanced-datepicker, advanced-range-slider, advanced-select, carousel, collapse, combobox, copy-markup, datatable, dropdown, file-upload, input-number, layout-splitter, overlay, pin-input, remove-element, scroll-nav, scrollspy, stepper, strong-password, tabs, textarea-autoheight, theme-switch, toggle-count, toggle-password, tooltip, tree-view\n\n**Returns:** API methods, events, data attributes, configuration options, integration examples.\n\n---\n\n## Tool 4: `blocks_categories`\n\nList all block categories in a hierarchy.\n\n**Input:** none\n\n**Returns:** `mainSection → subSection → category` tree with block counts per category.\n\n**Call first** when the user asks for blocks (ready-made page sections; users may also say \"examples\") or references a broad category.\n\n---\n\n## Tool 5: `blocks_in_category`\n\nList block IDs within one category.\n\n**Input:**\n- `mainSection` (required) - top-level slug from `blocks_categories`\n- `subSection` (required) - subsection slug from `blocks_categories`\n- `category` (required) - category slug from `blocks_categories`\n\n**Returns:** stable block IDs and slug/file mappings. Use IDs to call `single_block`.\n\n---\n\n## Tool 6: `single_block`\n\nGet complete integration-ready HTML/CSS/JS for one block.\n\n**Input:**\n- `mainSection` (required)\n- `subSection` (required)\n- `category` (required)\n- `block` (required) - exact ID from `blocks_in_category`. Accepts comma/newline-separated list (first is returned, rest marked as pending).\n- `context` (optional) - insertion hint\n- `isUtilityBased` (optional, default: `false`)\n- `theme` (optional, default: `\"default\"`)\n\n**Returns:** full block HTML with CSS/JS sections.\n\n---\n\n## Integration Placement Rules\n\n### CSS (`<!-- CSS -->`)\n\n```\n✅ CORRECT\n<head>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← immediately before </head>\n</head>\n\n❌ WRONG\n<body>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← NEVER in <body>\n</body>\n```\n\n### External Scripts (`<!-- Scripts -->`)\n\nThe `<!-- Scripts -->` label is a **response section marker only** - it does NOT indicate where to insert in the target file.\n\n**Placement algorithm:**\n\n1. Open the target file\n2. Starting from `</body>`, scan **upward** line by line\n3. Skip: blank lines, HTML comments (`<!-- ... -->`), non-structural tags (`</script>`, `</style>`, `</link>`, `</noscript>`), any non-HTML syntax\n4. The **first structural closing tag** reached (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`, `</header>`, `</aside>`) is the **anchor**\n5. Insert ALL `<script src>` tags on the line **immediately after** that anchor - nothing between the anchor and the scripts\n\n```html\n<!-- ✅ CORRECT - anchor is </main> -->\n</main>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ✅ CORRECT - anchor is </section> -->\n</section>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ❌ WRONG - non-structural content between anchor and scripts -->\n</main>\n<!-- any comment -->\n<script src=\"lib.js\"></script>\n</body>\n```\n\n### Init (`<!-- Init -->`)\n\n```html\n<!-- Place immediately before </body> -->\n<!-- Wrap in window.addEventListener unless block already contains <script> tags -->\n\n<script>\nwindow.addEventListener('load', () => {\n  // init code here\n});\n</script>\n</body>\n```\n\nIf `<!-- Init -->` already contains `<script>` tags - insert as-is, do NOT wrap again.\n\n---\n\n## Smart Component Selection\n\n`components_list` may include a `relative` object per component:\n\n| Field | Meaning | When to prefer |\n|---|---|---|\n| `isSectionDefault: true` | Recommended showcase for the whole section | User request is vague, no specific variant mentioned |\n| `category: \"<slug>\"` | Logical group (e.g. `\"color-variants\"`, `\"states\"`) | Use to filter by style group |\n| `isCategoryDefault: true` | Recommended default within its category | Request implies a style group (e.g. \"icon button\", \"loading state\") |\n\n**Exception for `isSectionDefault`:** if the component description mentions \"multiple variants side by side\" and the user wants a single element - skip it and pick a single-variant component. If no single-variant exists, use the default but extract only the one matching variant from the HTML.\n\n---\n\n## General Rules\n\n- Customize **text content only** - never CSS classes or HTML structure\n- Remove HTML comments before inserting into the target file\n- For complex components (accordion, table, tree), insert the full structure\n- For simple elements (button, input), insert one instance unless multiple are explicitly requested\n","skills/theme-generator/SKILL.md":"---\nname: preline-theme-generator\ndescription: Generate, preview, save, or validate Preline theme CSS using the bundled local generator in this skill. Use when the user asks to create a new Preline theme, match a brand color, translate a mood into a theme, generate light and dark theme CSS, preview theme output without writing files, or review and fix generated theme tokens. Do not use for generic non-Preline CSS work or arbitrary command execution.\n---\n\n# Preline Theme Generator\n\nUse the bundled local scripts. Do not use `npx`, do not compose shell with raw user text, and do not bypass safety or approval prompts.\n\n## Read Order\n\n1. Read `docs/workflow.md`.\n2. Read `docs/palette-guidance.md`.\n3. Read `docs/final-output-style.md` before writing a final theme file.\n4. Read `docs/validation-checklist.md` before closing the task.\n5. Read `examples.md` only if the user wants stylistic guidance or sample outputs.\n6. Read `docs/token-reference.md` only if you need to inspect token coverage in detail.\n\n## Workflow\n\nFollow `docs/workflow.md` for the full operating path.\n\nKey requirements:\n\n1. Identify whether the task is preview, write, or validate.\n2. Normalize the request into explicit generator inputs.\n3. Resolve the target directory with `scripts/find-themes-dir.js`.\n4. Generate CSS only through `scripts/run-theme-generator.js`.\n5. Create or edit only the new theme file.\n6. Validate with `docs/validation-checklist.md`.\n7. Return the result in the mode-appropriate format.\n\n## Security Constraints\n\n- Never use wording that attempts to bypass tool or approval safeguards.\n- Never use `npx` or any network-fetched package for this skill.\n- Never interpolate raw user text directly into shell commands.\n- Never use broad `find .` traversal for path discovery; use `scripts/find-themes-dir.js` or a user-confirmed path.\n- Never write outside a confirmed theme directory.\n- Use `scripts/run-theme-generator.js` as the only execution entry point for generation.\n- Never create or edit additional project files as part of theme generation.\n\n## Common Prompts\n\n- \"Create a sunset theme.\"\n- \"Match this brand color: #2F6BFF.\"\n- \"Generate a cyberpunk dark theme with matching dark mode.\"\n- \"Preview the CSS before writing a file.\"\n- \"Review this generated theme and fix token coverage.\"\n\n## Key References\n\n- `docs/workflow.md`\n- `docs/palette-guidance.md`\n- `docs/final-output-style.md`\n- `docs/validation-checklist.md`\n- `examples.md`\n- `docs/token-reference.md`\n","skills/theme-generator/docs/final-output-style.md":"# Final Output Style\n\nUse this guidance whenever the user wants a final `<name>.css` file for a Preline-based project.\n\n## Core rule\n\nThe deliverable is one file only: `<name>.css`.\n\nDo not create or edit any other project files.\n\n## What the final theme file should look like\n\n- A standalone `<name>.css` file that can live in the project's themes folder.\n- Theme activation via `data-theme=\"theme-<name>\"` and optional `.dark`.\n- Semantic tokens should prefer assigned variables after the theme palette has been mapped.\n\n## Required conventions\n\n- Do not assume project-specific paths other than the discovered themes directory.\n- Do not assume the project already has other custom theme files.\n- Do not force users to change HTML utility classes.\n- Do not create or edit a shared `theme.css`, docs page, or any other file.\n- Keep semantic tokens in the theme selector blocks, not inside `@theme`.\n- Put any custom brand/gray palette needed for this theme inside `@theme theme-<name> inline { }` in the new theme file.\n- If the repo already exposes shared aliases like `--color-primary-*` from its base theme, do not redefine them inside the custom theme file. Assign `--primary-*` directly from the theme-local palette instead.\n\n## Variable strategy\n\nPrefer assigned variables such as:\n\n- `--color-primary-*`\n- `--color-secondary-*`\n- `--background-*`, `--foreground`, `--border`, `--surface-*`, `--muted-*`\n- Tailwind palette vars like `--color-fuchsia-*`, `--color-orange-*`, `--color-neutral-*`\n- shared Preline palette vars exposed by the project's base theme, if available\n\nAllowed in the new theme file:\n\n- theme-local palette vars like `--color-<theme-name>-*`\n- theme-local gray palette vars like `--color-<theme-name>-gray-*`\n\nBut keep them inside `@theme theme-<name> inline { }` and avoid scattering raw palette references when an assigned semantic variable already exists.\n\nAvoid in semantic, chart, and map tokens:\n\n- raw `oklch(...)` assignments\n- literal hex values\n\n## If a unique palette is needed\n\nDefine it inside `@theme theme-<name> inline { }` in the new theme file.\n\nDo not edit any shared/base theme file.\n\n## Structural pattern\n\nUse this order:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector:\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector:\n   - `[data-theme=\"theme-<name>\"].dark`\n\n## Practical workflow\n\n1. Discover the themes folder.\n2. Generate one new `<name>.css` file.\n3. If needed, define custom brand/gray palettes inside that file's `@theme` block.\n4. Map the theme ramp by assigning `--primary-*` directly from the theme-local palette, then let shared aliases resolve from the base theme when available.\n5. Keep chart and map tokens variable-based.\n6. Re-run the validation checklist before returning.\n","skills/theme-generator/docs/palette-guidance.md":"# Palette guidance\n\nUse this file for color-direction heuristics only.\n\nImportant: references below to custom palettes like `--color-<name>-*` or `--color-<name>-gray-*` describe draft palette exploration and shared-palette design patterns. They do NOT override `docs/final-output-style.md`, which defines how the final `<name>.css` file should be structured.\n\n## Contents\n\n- [Hue reference](#hue-reference-for-workflow-normalization)\n- [Mood and palette guidance](#mood-and-palette-guidance)\n- [Accessibility and contrast guidelines](#accessibility-and-contrast-guidelines-wcag)\n- [Palette types](#palette-types)\n- [Custom gray palettes](#custom-gray-palettes-neutral-matching)\n\n## Hue reference (for workflow normalization)\n\n| Color | Hue Range | Tailwind Gray |\n|-------|-----------|---------------|\n| Red/Coral | 0-30 | neutral |\n| Orange/Amber | 30-60 | stone |\n| Yellow/Gold | 60-90 | stone |\n| Green/Lime/Avocado | 90-150 | stone |\n| Teal/Cyan | 150-200 | zinc |\n| Blue/Azure | 200-260 | slate |\n| Violet/Purple | 260-300 | zinc |\n| Magenta/Pink/Rose | 300-360 | neutral |\n\n## Mood and palette guidance\n\n### Color psychology reference\n\n| Mood | Primary Hues | Neutral Family | Characteristics |\n|------|--------------|----------------|-----------------|\n| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |\n| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |\n| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |\n| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |\n| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |\n| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |\n| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |\n| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |\n| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |\n| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |\n\n### Building cohesive palettes\n\n1. Pick a primary: the hero color for buttons, links, CTAs\n2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate\n3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast\n4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family\n\n### Accessibility and contrast guidelines (WCAG)\n\nEnsure themes meet WCAG 2.1 AA contrast requirements:\n\n| Element | Minimum Ratio | OKLCH Rule of Thumb |\n|---------|---------------|---------------------|\n| Body text on background | 4.5:1 | Lightness difference >= 50% |\n| Large text (18pt+) | 3:1 | Lightness difference >= 40% |\n| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |\n| Focus indicators | 3:1 | Use distinct color + lightness |\n\nOKLCH lightness thresholds:\n\n| Context | Light Mode | Dark Mode |\n|---------|------------|-----------|\n| Background | L >= 95% | L <= 25% |\n| Text on light bg | L <= 45% | -- |\n| Text on dark bg | -- | L >= 85% |\n| Primary button text | Check against primary-500/600 | Check against primary-400 |\n\nReadable primary foreground:\n- Do not decide from hue alone.\n- Compare candidate text colors against the actual generated primary shades used by the theme.\n- For light mode, check the generated `primary-600` and `primary-700` states.\n- For dark mode, check the generated `primary-400` and `primary-500` states.\n- Choose the text color with the stronger worst-case contrast across those states.\n- White usually wins for medium or dark primaries; dark gray should only be used when the generated brand states are genuinely light.\n- In dark mode, it is valid to shift the primary button one step deeper (for example `500/600` instead of `400/500`) when that keeps white text readable and better matches the design system.\n\nReadable inverse and decorative accents:\n- `--foreground-inverse` is not just for neutral inverse surfaces. In Preline it is also used on vivid utility colors such as red/orange badges and chips.\n- In dark mode, prefer keeping `--foreground-inverse` white unless contrast checks prove a darker value is still safe across those saturated utility colors.\n- Do not let dark mode inherit very deep brand steps for decorative tokens such as `--chart-5` when those tokens are likely to appear in gradient text or accent UI.\n- For dark decorative/chart tokens, prefer mid-to-light brand steps that stay clearly visible on dark backgrounds.\n\nQuick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.\n\n## Palette types\n\nWhen generating custom themes, there are TWO distinct types of custom color palettes:\n\n| Palette Type | Example Variable | Purpose | Chroma Level |\n|--------------|------------------|---------|--------------|\n| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |\n| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |\n\n### When to create each palette\n\nALWAYS create TWO palettes in `@theme inline { }`:\n1. Brand palette (`--color-<name>-*`) for primary/accent colors\n2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders\n\nBoth palettes are used in light mode for a cohesive feel.\n\nDark mode behavior depends on user request:\n\n| User Request | Light Mode Uses | Dark Mode Uses |\n|--------------|-----------------|----------------|\n| \"soft rose theme\" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |\n| \"soft rose theme with matching dark mode\" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |\n\nTrigger phrases for custom dark mode palette:\n- \"matching dark mode\"\n- \"cohesive dark mode\"\n- \"consistent dark colors\"\n- \"unified light and dark\"\n- \"matching neutrals\"\n\nIf none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.\n\n### Brand color style (vibrant vs soft)\n\nBrand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:\n\n| User Says | Brand Palette Style | Chroma Level |\n|-----------|---------------------|--------------|\n| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |\n| \"soft\", \"muted\", \"ash\", \"pastel\", \"dusty\", \"desaturated\" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |\n\nExample comparison:\n\n```css\n/* Vibrant brand (default) */\n--color-<name>-500: oklch(55% 0.14 <hue>);\n\n/* Soft/muted brand (if requested) */\n--color-<name>-500: oklch(55% 0.04 <hue>);\n```\n\n## Custom gray palettes (neutral matching)\n\nWhen a theme benefits from unique neutrals, generate a custom neutral palette that harmonizes with the primary color. These are NOT pure grays; they have subtle warmth or coolness.\n\n### Palette characteristics\n\n| Palette Type | Hue Range | Chroma Range | Use With |\n|--------------|-----------|--------------|----------|\n| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |\n| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |\n| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |\n| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |\n\n### OKLCH pattern for gray palettes (bell curve chroma)\n\nGray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:\n- Light mode backgrounds (50-200) look clean with subtle tint\n- Dark mode backgrounds (800-950) look sophisticated, not muddy\n\n```css\n--color-<name>-gray-50:  oklch(98%   0.002 <hue>);  /* Almost neutral */\n--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);\n--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);\n--color-<name>-gray-300: oklch(82.7% 0.012 <hue>);  /* Building up */\n--color-<name>-gray-400: oklch(73%   0.018 <hue>);  /* Approaching peak */\n--color-<name>-gray-500: oklch(62.5% 0.020 <hue>);  /* PEAK chroma */\n--color-<name>-gray-600: oklch(52.8% 0.016 <hue>);  /* Starting to fade */\n--color-<name>-gray-700: oklch(41.4% 0.012 <hue>);  /* Fading */\n--color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */\n--color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */\n--color-<name>-gray-950: oklch(20.1% 0.003 <hue>);  /* Nearly pure dark */\n```\n\nCritical: Dark mode backgrounds (800-950) need very low chroma:\n\n| Shade | Chroma | Why |\n|-------|--------|-----|\n| 950 | 0.003 | Dark backgrounds must be nearly neutral |\n| 900 | 0.005 | Avoids muddy/colored appearance |\n| 800 | 0.008 | Just a hint of warmth/coolness |\n| 500 | 0.020 | Peak, midtones carry color identity |\n| 50 | 0.002 | Light backgrounds stay clean |\n\nKey principles:\n- Chroma peaks at mid-tones (400-600), very low at BOTH extremes\n- Dark end (800-950) must have lower chroma than light end for clean dark mode\n- Hue stays consistent across the scale (±5 degrees variation is acceptable)\n- Lightness follows standard 50-950 scale\n\n### Placement of custom palettes\n\nCustom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:\n\n```css\n@theme theme-<name> inline {\n  /* Custom neutral palette for this theme */\n  --color-<name>-50: oklch(98% 0.003 88);\n  --color-<name>-100: oklch(95.5% 0.005 88);\n  /* ... full 50-950 scale ... */\n}\n```\n\nThen reference these in the theme selector blocks using `var()`:\n\n```css\n[data-theme=\"theme-<name>\"] {\n  --background: var(--color-<name>-50);\n  --background-1: var(--color-<name>-100);\n  /* ... */\n}\n\n[data-theme=\"theme-<name>\"].dark {\n  --background: var(--color-<name>-950);\n  --background-1: var(--color-<name>-900);\n  /* ... */\n}\n```\n\n### When to create custom palettes\n\n| Scenario | Action |\n|----------|--------|\n| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |\n| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |\n| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |\n| User explicitly requests | Always create to match their vision |\n| Theme needs distinctive personality | Create for cohesion |\n\n### Using custom palettes in both modes\n\nSoft gray palettes work seamlessly across light and dark modes:\n- Light mode: use 50-300 for backgrounds, 600-950 for text\n- Dark mode: use 800-950 for backgrounds, 50-300 for text\n\nThis creates consistent warmth/coolness across modes while maintaining readability.\n","skills/theme-generator/docs/token-reference.md":"# Preline Theme Token Reference\n\nThis document provides the complete reference for all semantic tokens available in Preline themes. Use this when generating themes to ensure comprehensive coverage.\n\n---\n\n## Contents\n\n- [Overview](#overview)\n- [Core token groups](#core-token-groups)\n- [Component preset patterns](#component-preset-patterns)\n- [Full token reference by category](#full-token-reference-by-category)\n- [Custom color palettes](#custom-color-palettes)\n- [Default values reference](#default-values-reference)\n- [Theme-scoped overrides](#theme-scoped-overrides)\n- [Color format guidelines](#color-format-guidelines)\n- [Quick token count summary](#quick-token-count-summary)\n\n## Overview\n\nPreline themes use CSS custom properties (variables) as semantic tokens. These tokens are:\n- Defined in theme files under `:root` (light) and `.dark` selectors\n- Mapped to Tailwind utilities via `@theme inline` in the base theme\n- Consumed by Preline components for consistent styling\n\nImportant: Generated themes override token values only. Never redefine the Tailwind mappings.\n\n---\n\n## Core token groups\n\n| Group | Tokens | Used for |\n|-------|--------|----------|\n| Brand | `--primary`, `--primary-*` | Primary actions (buttons, links) |\n| Secondary | `--secondary`, `--secondary-*` | Secondary emphasis |\n| Muted | `--muted`, `--muted-*` | Muted/subdued elements |\n| Destructive | `--destructive`, `--destructive-*` | Destructive/danger actions |\n| Background | `--background`, `--background-*` | App surfaces |\n| Foreground | `--foreground`, `--foreground-*` | Default typography colors |\n| Inverse | `--inverse` | Inverted color scheme |\n| Border | `--border` | Default border color |\n| Border line | `--border-line-*` | Border colors for different shades |\n| Layer | `--layer`, `--layer-*` | Layered elements (e.g. white buttons on white panel) |\n| Surface | `--surface`, `--surface-*` | Elevated surfaces |\n| Navbar | `--navbar`, `--navbar-*` | Navigation bar (background, border, divider) |\n| Navbar nav | `--navbar-nav-*` | Navigation bar items (foreground, hover, active) |\n| Sidebar | `--sidebar`, `--sidebar-*` | Sidebar (background, border, divider) |\n| Sidebar nav | `--sidebar-nav-*` | Sidebar items (foreground, hover, active) |\n| Card | `--card`, `--card-*` | Card component (border, divider, header, footer) |\n| Dropdown | `--dropdown`, `--dropdown-*` | Dropdown menus (items, hover, active) |\n| Select | `--select`, `--select-*` | Select component (items, hover, active) |\n| Overlay | `--overlay`, `--overlay-*` | Modal/overlay backgrounds |\n| Popover | `--popover`, `--popover-*` | Popover component |\n| Tooltip | `--tooltip`, `--tooltip-*` | Tooltip component |\n| Table | `--table-line` | Table borders |\n| Switch | `--switch` | Switch/toggle component |\n| Footer | `--footer`, `--footer-*` | Footer component |\n| Scrollbar | `--scrollbar-*` | Custom scrollbar (track, thumb) |\n| Chart | `--chart-*` | Chart colors (Apexcharts) |\n| Map | `--map-colors-*` | Map colors (jsvectormap) |\n\n---\n\n## Component preset patterns\n\nPreline uses numbered variants for component groups to offer different ready-made looks:\n\n| Pattern | Meaning |\n|---------|---------|\n| `--navbar-*` | Default navbar preset (base style) |\n| `--navbar-1-*` | Variant 1 preset (typically subtle/tinted surface) |\n| `--navbar-2-*` | Variant 2 preset (typically stronger tint or different border) |\n| `*` (no suffix) | Background/surface color of the component |\n| `*-line` | Border color for the component container |\n| `*-divider` | Divider lines inside the component |\n| `*-nav-*` | Nav item colors (foreground, hover, focus, active) |\n| `*-nav-list-divider` | Divider used in nav dropdown/list layouts |\n| `*-inverse` | Inverted/contrast variant |\n\nThis pattern applies to: navbar, sidebar, and similar navigation components.\n\n---\n\n## Full token reference by category\n\n### 1. Global surfaces + text\n\n```css\n/* Background tokens */\n--background          /* Main app background */\n--background-1        /* Secondary background (slightly tinted) */\n--background-2        /* Tertiary background (more tinted) */\n--background-plain    /* Plain white/black background */\n\n/* Foreground tokens */\n--foreground          /* Default text color */\n--foreground-inverse  /* Inverted text color (for dark backgrounds) */\n\n/* Inverse */\n--inverse             /* Inverted surface color */\n```\n\n### 2. Borders (full scale)\n\n```css\n--border              /* Default border color */\n--border-line-inverse /* Inverse border color */\n--border-line-1       /* Lightest border */\n--border-line-2\n--border-line-3\n--border-line-4\n--border-line-5\n--border-line-6\n--border-line-7\n--border-line-8       /* Darkest border */\n```\n\n### 3. Primary ramp + states\n\n```css\n/* Full color ramp (11 shades) */\n--primary-50\n--primary-100\n--primary-200\n--primary-300\n--primary-400\n--primary-500\n--primary-600\n--primary-700\n--primary-800\n--primary-900\n--primary-950\n\n/* State tokens */\n--primary             /* Base primary color */\n--primary-line        /* Primary border (usually transparent) */\n--primary-foreground  /* Text on primary background */\n--primary-hover       /* Hover state */\n--primary-focus       /* Focus state */\n--primary-active      /* Active/pressed state */\n--primary-checked     /* Checked state (checkboxes, radios) */\n```\n\n### 4. Secondary\n\n```css\n--secondary            /* Base secondary color */\n--secondary-line       /* Secondary border */\n--secondary-foreground /* Text on secondary background */\n--secondary-hover      /* Hover state */\n--secondary-focus      /* Focus state */\n--secondary-active     /* Active state */\n```\n\n### 5. Layer\n\n```css\n--layer               /* Layer background */\n--layer-line          /* Layer border */\n--layer-foreground    /* Text on layer */\n--layer-hover         /* Hover state */\n--layer-focus         /* Focus state */\n--layer-active        /* Active state */\n```\n\n### 6. Surface\n\n```css\n--surface             /* Base surface */\n--surface-1           /* Surface variant 1 */\n--surface-2           /* Surface variant 2 */\n--surface-3           /* Surface variant 3 */\n--surface-4           /* Surface variant 4 */\n--surface-5           /* Surface variant 5 */\n--surface-line        /* Surface border */\n--surface-foreground  /* Text on surface */\n--surface-hover       /* Hover state */\n--surface-focus       /* Focus state */\n--surface-active      /* Active state */\n```\n\n### 7. Muted\n\n```css\n--muted               /* Muted background */\n--muted-foreground    /* Muted text (lightest) */\n--muted-foreground-1  /* Muted text variant 1 */\n--muted-foreground-2  /* Muted text variant 2 (darkest) */\n--muted-hover         /* Hover state */\n--muted-focus         /* Focus state */\n--muted-active        /* Active state */\n```\n\n### 8. Destructive\n\n```css\n--destructive            /* Destructive/danger color */\n--destructive-foreground /* Text on destructive background */\n--destructive-hover      /* Hover state */\n--destructive-focus      /* Focus state */\n```\n\n### 9. Navbar (base + 2 variants)\n\n```css\n/* Base navbar */\n--navbar                /* Navbar background */\n--navbar-line           /* Navbar border */\n--navbar-divider        /* Navbar divider */\n--navbar-nav-foreground /* Nav item text */\n--navbar-nav-hover      /* Nav item hover */\n--navbar-nav-focus      /* Nav item focus */\n--navbar-nav-active     /* Nav item active */\n--navbar-nav-list-divider /* Nav list divider */\n--navbar-inverse        /* Inverse navbar background */\n\n/* Navbar variant 1 (subtle/tinted) */\n--navbar-1\n--navbar-1-line\n--navbar-1-divider\n--navbar-1-nav-foreground\n--navbar-1-nav-hover\n--navbar-1-nav-focus\n--navbar-1-nav-active\n--navbar-1-nav-list-divider\n\n/* Navbar variant 2 (stronger tint) */\n--navbar-2\n--navbar-2-line\n--navbar-2-divider\n--navbar-2-nav-foreground\n--navbar-2-nav-hover\n--navbar-2-nav-focus\n--navbar-2-nav-active\n--navbar-2-nav-list-divider\n```\n\n### 10. Sidebar (base + 2 variants)\n\n```css\n/* Base sidebar */\n--sidebar\n--sidebar-line\n--sidebar-divider\n--sidebar-nav-foreground\n--sidebar-nav-hover\n--sidebar-nav-focus\n--sidebar-nav-active\n--sidebar-nav-list-divider\n--sidebar-inverse\n\n/* Sidebar variant 1 */\n--sidebar-1\n--sidebar-1-line\n--sidebar-1-divider\n--sidebar-1-nav-foreground\n--sidebar-1-nav-hover\n--sidebar-1-nav-focus\n--sidebar-1-nav-active\n--sidebar-1-nav-list-divider\n\n/* Sidebar variant 2 */\n--sidebar-2\n--sidebar-2-line\n--sidebar-2-divider\n--sidebar-2-nav-foreground\n--sidebar-2-nav-hover\n--sidebar-2-nav-focus\n--sidebar-2-nav-active\n--sidebar-2-nav-list-divider\n```\n\n### 11. Card\n\n```css\n--card                /* Card background */\n--card-line           /* Card border */\n--card-divider        /* Card divider */\n--card-header         /* Card header background */\n--card-footer         /* Card footer background */\n--card-inverse        /* Inverse card background */\n```\n\n### 12. Dropdown\n\n```css\n--dropdown                /* Dropdown background */\n--dropdown-1              /* Dropdown variant background */\n--dropdown-line           /* Dropdown border */\n--dropdown-divider        /* Dropdown divider */\n--dropdown-header         /* Dropdown header background */\n--dropdown-footer         /* Dropdown footer background */\n--dropdown-item-foreground /* Item text */\n--dropdown-item-hover     /* Item hover */\n--dropdown-item-focus     /* Item focus */\n--dropdown-item-active    /* Item active */\n--dropdown-inverse        /* Inverse dropdown */\n```\n\n### 13. Select\n\n```css\n--select                /* Select background */\n--select-1              /* Select variant background */\n--select-line           /* Select border */\n--select-item-foreground /* Item text */\n--select-item-hover     /* Item hover */\n--select-item-focus     /* Item focus */\n--select-item-active    /* Item active */\n--select-inverse        /* Inverse select */\n```\n\n### 14. Overlay\n\n```css\n--overlay             /* Overlay/modal background */\n--overlay-line        /* Overlay border */\n--overlay-divider     /* Overlay divider */\n--overlay-header      /* Overlay header background */\n--overlay-footer      /* Overlay footer background */\n--overlay-inverse     /* Inverse overlay */\n```\n\n### 15. Popover, tooltip, table, switch\n\n```css\n/* Popover */\n--popover             /* Popover background */\n--popover-line        /* Popover border */\n\n/* Tooltip */\n--tooltip             /* Tooltip background */\n--tooltip-foreground  /* Tooltip text */\n--tooltip-line        /* Tooltip border */\n\n/* Table */\n--table-line          /* Table border */\n\n/* Switch */\n--switch              /* Switch knob color */\n```\n\n### 16. Footer\n\n```css\n--footer              /* Footer background */\n--footer-line         /* Footer border */\n--footer-inverse      /* Inverse footer */\n```\n\n### 17. Scrollbar\n\n```css\n--scrollbar-track         /* Scrollbar track */\n--scrollbar-thumb         /* Scrollbar thumb */\n--scrollbar-track-inverse /* Inverse track */\n--scrollbar-thumb-inverse /* Inverse thumb */\n```\n\n### 18. Charts (Apexcharts)\n\nImportant: keep chart `-hex` tokens variable-based in generated theme source. Do not hardcode literal hex or oklch values into the final theme file.\n\n```css\n/* Primary chart color */\n--chart-primary\n--chart-colors-primary\n--chart-colors-primary-inverse\n--chart-colors-primary-hex\n--chart-colors-primary-hex-inverse\n\n/* Chart palette (1-10) */\n--chart-1 through --chart-10\n--chart-colors-chart-1 through --chart-colors-chart-10\n--chart-colors-chart-1-inverse through --chart-colors-chart-10-inverse\n--chart-colors-chart-1-hex through --chart-colors-chart-10-hex\n--chart-colors-chart-1-hex-inverse through --chart-colors-chart-10-hex-inverse\n\n/* Chart backgrounds */\n--chart-colors-background\n--chart-colors-background-inverse\n--chart-colors-chart-inverse\n\n/* Chart foregrounds */\n--chart-colors-foreground\n--chart-colors-foreground-inverse\n\n/* Chart labels */\n--chart-colors-labels\n--chart-colors-labels-inverse\n--chart-colors-xaxis-labels\n--chart-colors-xaxis-labels-inverse\n--chart-colors-yaxis-labels\n--chart-colors-yaxis-labels-inverse\n\n/* Chart grid */\n--chart-colors-grid-border\n--chart-colors-grid-border-inverse\n\n/* Chart special */\n--chart-colors-bar-ranges\n--chart-colors-bar-ranges-inverse\n--chart-colors-candlestick-upward\n--chart-colors-candlestick-upward-inverse\n--chart-colors-candlestick-downward\n--chart-colors-candlestick-downward-inverse\n```\n\n### 19. Maps (jsvectormap)\n\n```css\n--map-colors-primary\n--map-colors-primary-inverse\n--map-colors-default\n--map-colors-default-inverse\n--map-colors-highlight\n--map-colors-highlight-inverse\n--map-colors-border\n--map-colors-border-inverse\n```\n\n---\n\n## Custom color palettes\n\nThe base theme defines custom color palettes that themes can use:\n\n### Khaki (earthy/warm neutral)\n\n```css\n--color-khaki-50 through --color-khaki-950\n```\n\n### Lavender (soft purple/mauve neutral)\n\n```css\n--color-lavender-50 through --color-lavender-950\n```\n\nThese are useful for creating warm or soft-toned themes instead of using standard gray/neutral.\n\n---\n\n## Default values reference\n\n### Light mode defaults (:root)\n\n```css\n:root {\n  /* Backgrounds */\n  --background: var(--color-white);\n  --background-1: var(--color-gray-50);\n  --background-2: var(--color-gray-100);\n  --background-plain: var(--color-white);\n\n  /* Foregrounds */\n  --foreground: var(--color-gray-800);\n  --foreground-inverse: var(--color-white);\n  --inverse: var(--color-gray-800);\n\n  /* Borders */\n  --border: var(--color-gray-200);\n  --border-line-inverse: var(--color-white);\n  --border-line-1: var(--color-gray-100);\n  --border-line-2: var(--color-gray-200);\n  --border-line-3: var(--color-gray-300);\n  --border-line-4: var(--color-gray-400);\n  --border-line-5: var(--color-gray-500);\n  --border-line-6: var(--color-gray-600);\n  --border-line-7: var(--color-gray-700);\n  --border-line-8: var(--color-gray-800);\n\n  /* Primary (blue by default) */\n  --primary-50: var(--color-blue-50);\n  --primary-100: var(--color-blue-100);\n  --primary-200: var(--color-blue-200);\n  --primary-300: var(--color-blue-300);\n  --primary-400: var(--color-blue-400);\n  --primary-500: var(--color-blue-500);\n  --primary-600: var(--color-blue-600);\n  --primary-700: var(--color-blue-700);\n  --primary-800: var(--color-blue-800);\n  --primary-900: var(--color-blue-900);\n  --primary-950: var(--color-blue-950);\n\n  --primary: var(--color-primary-600);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  --primary-focus: var(--color-primary-700);\n  --primary-active: var(--color-primary-700);\n  --primary-checked: var(--color-primary-600);\n\n  /* Secondary */\n  --secondary: var(--color-gray-900);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-white);\n  --secondary-hover: var(--color-gray-800);\n  --secondary-focus: var(--color-gray-800);\n  --secondary-active: var(--color-gray-800);\n\n  /* Layer */\n  --layer: var(--color-white);\n  --layer-line: var(--color-gray-200);\n  --layer-foreground: var(--color-gray-800);\n  --layer-hover: var(--color-gray-50);\n  --layer-focus: var(--color-gray-50);\n  --layer-active: var(--color-gray-50);\n\n  /* Surface */\n  --surface: var(--color-gray-100);\n  --surface-1: var(--color-gray-200);\n  --surface-2: var(--color-gray-300);\n  --surface-3: var(--color-gray-400);\n  --surface-4: var(--color-gray-500);\n  --surface-5: var(--color-gray-600);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-gray-800);\n  --surface-hover: var(--color-gray-200);\n  --surface-focus: var(--color-gray-200);\n  --surface-active: var(--color-gray-200);\n\n  /* Muted */\n  --muted: var(--color-gray-50);\n  --muted-foreground: var(--color-gray-400);\n  --muted-foreground-1: var(--color-gray-500);\n  --muted-foreground-2: var(--color-gray-600);\n  --muted-hover: var(--color-gray-100);\n  --muted-focus: var(--color-gray-100);\n  --muted-active: var(--color-gray-100);\n\n  /* Destructive */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Navbar (base) */\n  --navbar: var(--color-white);\n  --navbar-line: var(--color-gray-200);\n  --navbar-divider: var(--color-gray-200);\n  --navbar-nav-foreground: var(--color-gray-800);\n  --navbar-nav-hover: var(--color-gray-100);\n  --navbar-nav-focus: var(--color-gray-100);\n  --navbar-nav-active: var(--color-gray-100);\n  --navbar-nav-list-divider: var(--color-gray-200);\n  --navbar-inverse: var(--color-primary-950);\n\n  /* Navbar-1 */\n  --navbar-1: var(--color-gray-50);\n  --navbar-1-line: var(--color-gray-200);\n  --navbar-1-divider: var(--color-gray-200);\n  --navbar-1-nav-foreground: var(--color-gray-800);\n  --navbar-1-nav-hover: var(--color-gray-200);\n  --navbar-1-nav-focus: var(--color-gray-200);\n  --navbar-1-nav-active: var(--color-gray-200);\n  --navbar-1-nav-list-divider: var(--color-gray-200);\n\n  /* Navbar-2 */\n  --navbar-2: var(--color-gray-100);\n  --navbar-2-line: transparent;\n  --navbar-2-divider: var(--color-gray-300);\n  --navbar-2-nav-foreground: var(--color-gray-800);\n  --navbar-2-nav-hover: var(--color-gray-200);\n  --navbar-2-nav-focus: var(--color-gray-200);\n  --navbar-2-nav-active: var(--color-gray-200);\n  --navbar-2-nav-list-divider: var(--color-gray-200);\n\n  /* Sidebar follows same pattern as navbar */\n  /* Card, Dropdown, Select, Overlay, Popover, Tooltip, Table, Switch, Footer, Scrollbar */\n  /* See the project's existing base Preline theme source for complete default values */\n}\n```\n\n### Dark mode defaults (.dark)\n\n```css\n.dark {\n  /* Backgrounds */\n  --background: var(--color-neutral-800);\n  --background-1: var(--color-neutral-900);\n  --background-2: var(--color-neutral-900);\n  --foreground: var(--color-neutral-200);\n  --inverse: var(--color-neutral-950);\n\n  /* Borders */\n  --border: var(--color-neutral-700);\n  --border-line-1: var(--color-neutral-800);\n  --border-line-2: var(--color-neutral-700);\n  --border-line-3: var(--color-neutral-600);\n  --border-line-4: var(--color-neutral-500);\n  --border-line-5: var(--color-neutral-400);\n  --border-line-6: var(--color-neutral-300);\n  --border-line-7: var(--color-neutral-200);\n  --border-line-8: var(--color-neutral-100);\n\n  /* Primary (shifts to 500 in dark mode) */\n  --primary: var(--color-primary-500);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-600);\n  --primary-focus: var(--color-primary-600);\n  --primary-active: var(--color-primary-600);\n  --primary-checked: var(--color-primary-500);\n\n  /* Secondary */\n  --secondary: var(--color-white);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-neutral-800);\n  --secondary-hover: var(--color-neutral-100);\n  --secondary-focus: var(--color-neutral-100);\n  --secondary-active: var(--color-neutral-100);\n\n  /* Layer */\n  --layer: var(--color-neutral-800);\n  --layer-line: var(--color-neutral-700);\n  --layer-foreground: var(--color-white);\n  --layer-hover: var(--color-neutral-700);\n  --layer-focus: var(--color-neutral-700);\n  --layer-active: var(--color-neutral-700);\n\n  /* Surface */\n  --surface: var(--color-neutral-700);\n  --surface-1: var(--color-neutral-600);\n  --surface-2: var(--color-neutral-500);\n  --surface-3: var(--color-neutral-600);\n  --surface-4: var(--color-neutral-500);\n  --surface-5: var(--color-neutral-400);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-neutral-200);\n  --surface-hover: var(--color-neutral-600);\n  --surface-focus: var(--color-neutral-600);\n  --surface-active: var(--color-neutral-600);\n\n  /* Muted */\n  --muted: var(--color-neutral-800);\n  --muted-foreground: var(--color-neutral-500);\n  --muted-foreground-1: var(--color-neutral-400);\n  --muted-foreground-2: var(--color-neutral-300);\n  --muted-hover: var(--color-neutral-700);\n  --muted-focus: var(--color-neutral-700);\n  --muted-active: var(--color-neutral-700);\n\n  /* Destructive (same red, readable in dark) */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Components shift to neutral-800/900 backgrounds */\n  /* Nav items shift to neutral-700 hover states */\n  /* See the project's existing base Preline theme source for complete dark mode values */\n}\n```\n\n---\n\n## Theme-scoped overrides\n\nWhen creating a theme, override values under theme-scoped selectors:\n\n```css\n/* Light mode */\n:root[data-theme=\"theme-<name>\"],\n[data-theme=\"theme-<name>\"] {\n  /* Override tokens here */\n}\n\n/* Dark mode */\n[data-theme=\"theme-<name>\"].dark {\n  /* Override dark-specific tokens here */\n}\n```\n\n---\n\n## Color format guidelines\n\n- For most tokens: use Tailwind color variables `var(--color-<color>-<shade>)`\n- For oklch colors: acceptable for standard tokens\n- For chart `-hex` tokens: keep them as stable variable references in generated source\n- For custom colors: can use hex, rgb, hsl, or oklch\n\n---\n\n## Quick token count summary\n\n| Category | Token Count |\n|----------|-------------|\n| Global surfaces + text | 7 |\n| Borders | 10 |\n| Primary (ramp + states) | 18 |\n| Secondary | 6 |\n| Layer | 6 |\n| Surface | 11 |\n| Muted | 7 |\n| Destructive | 4 |\n| Navbar (3 variants) | 27 |\n| Sidebar (3 variants) | 27 |\n| Card | 6 |\n| Dropdown | 11 |\n| Select | 8 |\n| Overlay | 6 |\n| Popover | 2 |\n| Tooltip | 3 |\n| Table, Switch, Footer | 5 |\n| Scrollbar | 4 |\n| Charts | ~50+ |\n| Maps | 8 |\n| Total | ~220+ tokens\n","skills/theme-generator/docs/validation-checklist.md":"# Validation checklist (must self-check before returning)\n\n## Final source style\n- [ ] Only one project file is created or edited: `<name>.css`\n- [ ] The final file is a production-style `<name>.css` file\n- [ ] `@theme theme-<name> inline { }` is present\n- [ ] Any custom brand/gray palette lives inside `@theme theme-<name> inline { }` in the new theme file\n- [ ] Semantic tokens prefer assigned vars such as `--color-primary-*`, `--background-*`, `--foreground`, `--border`, `--surface-*`, or shared palette vars when available\n- [ ] The custom theme does not redefine shared aliases like `--color-primary-*` inside `@theme` when the repo base theme already owns them\n- [ ] No raw `oklch(...)` or literal hex values remain in semantic, chart, or map token assignments in the final theme file\n\n## Structure\n- [ ] Theme key consistent: `theme-<name>` everywhere\n- [ ] Light selector uses `:root[data-theme=...]`, `[data-theme=...]`\n- [ ] Dark selector is `[data-theme=\"theme-<name>\"].dark`\n- [ ] Semantic token definitions are in selector blocks (NOT inside @theme block)\n- [ ] No requirement to change HTML utility classes (only `data-theme`, optional `.dark`)\n\n## Palette strategy\n- [ ] Primary ramp maps `--primary-*` directly to theme-local palette vars\n- [ ] Neutral/background tokens use assigned palette vars\n- [ ] If a new custom palette is needed, it is declared inside the new theme file's `@theme` block, not by editing another file\n\n## Token coverage\n- [ ] Full coverage present across global tokens, states, and major component groups\n- [ ] CSS syntax valid (balanced braces, no broken comments)\n- [ ] If behavior overrides exist, they are theme-scoped only\n- [ ] Chart/map tokens use variable references matching shipped theme patterns\n\n## Dark mode consistency\n- [ ] Dark mode uses CSS variables (`var(--color-*)`) not hardcoded color literals\n- [ ] Dark mode uses one consistent grayscale family unless there is a deliberate, reference-backed reason not to\n- [ ] Hover/focus/active states remain readable and distinguishable\n- [ ] `--foreground-inverse` remains readable on vivid utility colors in dark mode (for example red badges/chips)\n- [ ] Dark-mode chart/decorative tokens avoid very deep brand steps when those tokens are used in gradient text or accent UI\n","skills/theme-generator/docs/workflow.md":"# Workflow\n\nUse this file as the default operating guide for the active theme skill.\n\n## Task modes\n\nClassify the request before doing any work:\n\n- `preview`: generate CSS and return it without writing a project file\n- `write`: create one new `<name>.css` theme file\n- `validate`: review or fix an existing generated theme file\n\n## Normalize the request\n\nConvert natural language into the generator's small, explicit input set:\n\n| Input | Required | Notes |\n|------|----------|-------|\n| `name` | yes | kebab-case theme name |\n| `hue` or `primaryColor` | yes | use `primaryColor` only when user gave a brand hex |\n| `style` | no | `vibrant` by default, `soft` for muted/pastel/desaturated requests |\n| `useCustomDarkGray` | no | true only when user explicitly wants matching/cohesive dark neutrals |\n| `tailwindGray` | no | choose `stone`, `neutral`, `zinc`, `slate`, or `gray` based on brand warmth |\n| `fontSans` / `fontSerif` / `fontMono` | no | include only when requested |\n\nUse `docs/palette-guidance.md` for hue ranges, mood mapping, contrast heuristics, and gray-family decisions.\n\n### Natural-language interpretation\n\nUse the user's language to infer the palette direction:\n\n| User language | Default interpretation |\n|--------------|------------------------|\n| warm sunset, cozy, inviting | orange/amber primary, warm neutrals, medium contrast |\n| professional, clean, SaaS | blue/indigo primary, slate or zinc neutrals |\n| playful, candy, youthful | pink/purple/cyan primary, higher contrast |\n| earthy, organic, calm | green/teal primary, stone neutrals |\n| soft, muted, ash, dusty | `style: soft` |\n| matching dark mode, cohesive dark, unified neutrals | `useCustomDarkGray: true` |\n\nWhen the request is vague:\n\n1. Make an opinionated, coherent palette choice.\n2. Keep contrast readable.\n3. Prefer a consistent system over a flashy but uneven palette.\n\nForeground rule:\n- When mapping text or icon colors onto the primary brand color, choose the foreground by contrast against the generated primary states, not by hue bucket alone.\n- Treat `--foreground-inverse` separately from primary foregrounds. In Preline it is reused on hardcoded vivid utility backgrounds such as red badges, so dark mode should keep it readable on saturated colors instead of tinting it into a dark neutral.\n- Treat chart tokens as decorative UI tokens, not just data-series tokens. Dark mode must override any chart shades that would become too dark for gradient text, badges, or chart accents on dark surfaces.\n\n## Discover the target directory\n\nUse the bundled resolver instead of broad repo traversal:\n\n```bash\nnode scripts/find-themes-dir.js\n```\n\nTrusted theme directories are checked in this order:\n\n- `src/assets/css/themes/`\n- `src/css/themes/`\n- `src/styles/themes/`\n- `assets/css/themes/`\n- `styles/themes/`\n- `dist/assets/css/themes/`\n- `npm/preline/css/themes/`\n\nRules:\n\n- Prefer source theme directories over generated outputs.\n- Do not use repo-wide `find .` traversal for this task.\n- If no trusted theme directory is found and the user did not provide a path, ask where the file should be written.\n\n## Generate CSS\n\nUse the local wrapper as the only execution entry point:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --stdout\n```\n\nWrite a theme file with:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --output src/assets/css/themes/seafoam.css\n```\n\nRules:\n\n- Do not use `npx`.\n- Do not interpolate raw user text into shell commands.\n- Do not chain temp-file cleanup or other unrelated shell work into the command.\n- Do not create or edit any project file other than the new theme file.\n\n## Theme structure expectations\n\nThe wrapper should emit a production-style standalone `<name>.css` file:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector\n   - `[data-theme=\"theme-<name>\"].dark`\n\nKeep custom brand and gray palettes inside the file's `@theme` block. Keep semantic tokens in the selector blocks.\n\nSee `docs/final-output-style.md` for the final file rules and `docs/token-reference.md` only when detailed token coverage inspection is necessary.\n\n## Dark-mode decision rule\n\nAlways generate both palettes in `@theme theme-<name> inline { }`:\n\n- `--color-<name>-*` for the brand ramp\n- `--color-<name>-gray-*` for the theme's neutral ramp\n\nDark-mode neutrals depend on user intent:\n\n- Explicit matching/cohesive dark request: use the theme's custom gray palette in dark mode.\n- Default path: keep light mode custom and use a Tailwind gray family in dark mode.\n\nPrimary foreground decision:\n- Use the generated brand shades to determine whether white or dark text is more readable on the primary color in each mode.\n- In dark mode, prefer a deeper primary step with white text when that profile remains readable; only fall back to lighter primary steps with dark text when white would fail.\n\nUse CSS variables in semantic tokens and dark-mode overrides. Do not hardcode raw `oklch(...)` or hex values in semantic, chart, or map tokens.\n\n## Validation focus\n\nBefore returning:\n\n- confirm only one project file was created or edited\n- confirm the theme key is `theme-<name>` everywhere\n- confirm the file has full light and dark token coverage\n- confirm chart and map tokens remain variable-based\n- confirm dark-mode states remain readable\n\nUse `docs/validation-checklist.md` as the final gate.\n\n## Response shape\n\nMatch the response to the mode:\n\n- Preview: return the CSS first, then the enable snippet if useful\n- Write: return the created path, a short summary, and the enable snippet\n- Validate: list findings first, then the applied or recommended fix\n\nEnable snippet:\n\n```html\n<html data-theme=\"theme-<theme-name>\">\n```\n","skills/theme-generator/examples.md":"# Examples — Preline Theme Generator\n\nThese examples are palette-and-structure references, not literal final-output templates.\n\nImportant: examples below are structural references. Final delivered theme files should follow `docs/final-output-style.md` and should never require edits to any companion project file.\n\nThese examples define what \"good output\" looks like.\n\n## Contents\n\n- [Example 1 — Calm teal SaaS theme](#example-1--calm-teal-saas-theme-full-theme-clean)\n- [Example 2 — Retro sharp corners](#example-2--retro-sharp-corners-behavior-change-must-be-scoped)\n- [Example 3 — Theme-scoped font token](#example-3--theme-scoped-font-token-no-mapping-changes)\n- [Example 4 — Brand match with exact primary color](#example-4--brand-match-with-exact-primary-color)\n\n## Example 1 — Calm teal SaaS theme (full theme, clean)\n\n**Input**\n- themeName: `seafoam`\n- mood: calm, modern SaaS\n- primaryHue: teal (180)\n- contrast: medium\n- style: vibrant\n- darkNeutralFamily: zinc\n\n**Expected Structure**\n- `@theme theme-seafoam inline { }` contains brand + gray palettes\n- Light mode selector: `:root[data-theme=\"theme-seafoam\"], [data-theme=\"theme-seafoam\"]`\n- Dark mode selector: `[data-theme=\"theme-seafoam\"].dark`\n- Full coverage: global surfaces, borders, primary ramp, states, components\n- Dark mode uses Tailwind zinc for neutrals (no matching dark requested)\n\n### Output (truncated)\n\n```css\n/* ------------------------------ */\n/* -------- Seafoam ------------- */\n/* ------------------------------ */\n\n@theme theme-seafoam inline {\n  /* BRAND PALETTE (vibrant) */\n  --color-seafoam-50: oklch(97% 0.08 180);\n  --color-seafoam-100: oklch(94% 0.10 180);\n  --color-seafoam-200: oklch(88% 0.12 180);\n  --color-seafoam-300: oklch(80% 0.14 180);\n  --color-seafoam-400: oklch(70% 0.16 180);\n  --color-seafoam-500: oklch(60% 0.14 180);\n  --color-seafoam-600: oklch(52% 0.12 180);\n  --color-seafoam-700: oklch(44% 0.10 180);\n  --color-seafoam-800: oklch(36% 0.08 180);\n  --color-seafoam-900: oklch(28% 0.06 180);\n  --color-seafoam-950: oklch(20% 0.05 180);\n  \n  /* GRAY PALETTE (bell curve chroma) */\n  --color-seafoam-gray-50: oklch(98% 0.002 180);\n  --color-seafoam-gray-100: oklch(95.5% 0.004 180);\n  --color-seafoam-gray-200: oklch(89.7% 0.008 180);\n  /* ... 300-700 ... */\n  --color-seafoam-gray-800: oklch(34.6% 0.008 180);\n  --color-seafoam-gray-900: oklch(30.6% 0.005 180);\n  --color-seafoam-gray-950: oklch(20.1% 0.003 180);\n\n}\n\n:root[data-theme=\"theme-seafoam\"],\n[data-theme=\"theme-seafoam\"] {\n  /* Global */\n  --background: var(--color-white);\n  --background-1: var(--color-seafoam-gray-50);\n  --foreground: var(--color-seafoam-gray-800);\n  \n  /* Primary ramp */\n  --primary-50: var(--color-seafoam-50);\n  --primary-100: var(--color-seafoam-100);\n  /* ... full ramp ... */\n  \n  /* Primary states */\n  --primary: var(--color-primary-600);\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  \n  /* Components prefer assigned semantic tokens */\n  --navbar: var(--background-plain);\n  --navbar-line: var(--border);\n  /* ... */\n}\n\n[data-theme=\"theme-seafoam\"].dark {\n  /* Dark mode uses Tailwind zinc */\n  --background: var(--color-zinc-800);\n  --background-1: var(--color-zinc-900);\n  --foreground: var(--color-zinc-200);\n  \n  --primary: var(--color-primary-400);\n  --primary-foreground: var(--color-zinc-900); /* Contrast-selected because the generated light-brand states are too light for white */\n  /* ... */\n}\n```\n\n---\n\n## Example 2 — Retro sharp corners (behavior change must be scoped)\n\n**Input**\n- themeName: `arcade`\n- mood: retro 90s\n- primaryHue: magenta (320)\n- contrast: high\n- radiusStyle: `retro-sharp`\n\n**Expected Structure**\n- Full token coverage\n- Theme sets radius tokens in theme selector (NOT in @theme block)\n- Any \"rounded\" behavior override inside `@layer utilities` scoped to theme\n\n### Output (radius section only)\n\n```css\n:root[data-theme=\"theme-arcade\"],\n[data-theme=\"theme-arcade\"] {\n  /* RETRO SHARP CORNERS */\n  --radius-sm: 0px;\n  --radius-md: 2px;\n  --radius-lg: 4px;\n  --radius-xl: 4px;\n  --radius-2xl: 6px;\n  --radius-3xl: 6px;\n  \n  /* ... rest of tokens ... */\n}\n\n/* Optional: Override rounded utility if needed */\n@layer utilities {\n  :is(:root[data-theme=\"theme-arcade\"], [data-theme=\"theme-arcade\"]) .rounded {\n    border-radius: var(--radius-md);\n  }\n}\n```\n\n---\n\n## Example 3 — Theme-scoped font token (no mapping changes)\n\n**Input**\n- themeName: `leafy-modern`\n- mood: organic, clean\n- primaryHue: emerald (145)\n- fontSans: `\"Roboto\", ui-sans-serif, system-ui, sans-serif`\n\n**Expected Structure**\n- Theme sets `--font-sans` inside theme selector (NOT in @theme block)\n- Theme does NOT import Google Fonts (font loading is separate)\n- User enables via `data-theme=\"theme-leafy-modern\"` only\n\n### Output (font section only)\n\n```css\n:root[data-theme=\"theme-leafy-modern\"],\n[data-theme=\"theme-leafy-modern\"] {\n  /* TYPOGRAPHY */\n  --font-sans: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n  \n  /* ... rest of tokens ... */\n}\n```\n\n---\n\n## Example 4 — Brand match with exact primary color\n\n**Input**\n- themeName: `brand-x`\n- primaryColor: `#2F6BFF` (converts to hue ~225)\n- mood: confident, high clarity\n- contrast: medium-high\n\n**Expected Structure**\n- Script converts hex to hue automatically\n- Primary ramp derived coherently from the brand hue\n- `--primary-foreground` readable (white on this blue)\n- Chart `-hex` tokens remain variable-based placeholders in generated source\n\n### Output (chart section showing variable-based chart tokens)\n\n```css\n:root[data-theme=\"theme-brand-x\"],\n[data-theme=\"theme-brand-x\"] {\n  /* ... other tokens ... */\n  \n  /* CHARTS */\n  --chart-primary: var(--color-primary-500);\n  --chart-colors-primary: var(--color-primary-500);\n  --chart-colors-primary-hex: var(--color-primary-500);\n  --chart-colors-primary-hex-inverse: var(--color-primary-300);\n  \n  --chart-1: var(--color-primary-500);\n  --chart-colors-chart-1-hex: var(--color-primary-500);\n  /* ... */\n}\n```\n","src/plugins/accordion/README.md":"# Accordion\n\nBuild vertically collapsing accordions in combination with Accordion JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/accordion) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/accordion.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Accordion component allows you to create collapsible content sections that can be expanded or collapsed by clicking on toggle buttons. It supports multiple accordion items within a group, with options for keeping items open, ensuring at least one item stays open, and treeview mode for hierarchical structures.\n\n**Key Features:**\n- Multiple accordion items in a single group\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Treeview mode support\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Accordion plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n@import \"./node_modules/@preline/accordion/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n\n  new HSAccordion(document.querySelector(\"#accordion\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/accordion` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/accordion\";\n```\n\n`@preline/accordion/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\n\nHSAccordion.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#accordion\");\nif (el) new HSAccordion(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an accordion component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three accordion items within a group, with the first item open by default.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion active\" id=\"hs-unstyled-heading-one\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-unstyled-collapse-one\">\n      Accordion #1\n    </button>\n    <div id=\"hs-unstyled-collapse-one\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-one\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-two\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-two\">\n      Accordion #2\n    </button>\n    <div id=\"hs-unstyled-collapse-two\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-two\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-three\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-three\">\n      Accordion #3\n    </button>\n    <div id=\"hs-unstyled-collapse-three\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-three\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-accordion-group`: Required wrapper container for all accordion items\n- `hs-accordion`: Required container for each accordion item\n- `hs-accordion-toggle`: Required button element that triggers expand/collapse\n- `hs-accordion-content`: Required container for collapsible content\n- Unique `id` attributes for each accordion item and its content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- To have an accordion item open by default, add the `active` class to the `hs-accordion` element\n- For closed items, add the `hidden` class to the `hs-accordion-content` element\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure accordion behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-accordion-always-open` | `hs-accordion-group` | boolean attribute | `false` (absent) | When present (boolean attribute), accordion items stay open when another item is opened. By default, opening one item closes others. Presence of attribute = `true`, absence = `false`. |\n\n**Example:**\n```html\n<!-- Boolean attribute: presence = true, absence = false -->\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <!-- Multiple items can be open simultaneously -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `--stop-propagation:*` | `hs-accordion-toggle` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, interrupts event propagation when toggle button is clicked. Useful when accordion is nested inside clickable elements. |\n| `--keep-one-open:*` | `hs-accordion-group` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, ensures at least one accordion item remains open at all times. Prevents closing the last open item. |\n\n**Example:**\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <!-- At least one item will always be open -->\n</div>\n\n<button class=\"hs-accordion-toggle --stop-propagation:true\">\n  <!-- Click event won't bubble up -->\n</button>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the accordion to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-group` | Container wrapper | Groups multiple accordion items together |\n| `hs-accordion` | Each accordion item container | Identifies an individual accordion item |\n| `hs-accordion-toggle` | Button element | The clickable element that expands/collapses content |\n| `hs-accordion-content` | Content container | The collapsible content area |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-treeview-root` | Parent container | When present, activates treeview mode for hierarchical accordion structures |\n| `hs-accordion-selectable` | Elements inside treeview | When treeview mode is active, elements with this class become selectable |\n\n**Treeview Mode Example:**\n```html\n<div class=\"hs-accordion-treeview-root\">\n  <div class=\"hs-accordion-group\">\n    <div class=\"hs-accordion\">\n      <button class=\"hs-accordion-toggle\">Parent</button>\n      <div class=\"hs-accordion-content\">\n        <div class=\"hs-accordion-selectable\">Selectable item</div>\n      </div>\n    </div>\n  </div>\n</div>\n```\n\n## JavaScript API\n\nThe `HSAccordion` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an accordion instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed accordion item. Returns `false` if item is already open, `true` otherwise. If other items are open and `always-open` is not set, they will be closed. |\n| `hide()` | None | `boolean` | Collapses the currently open accordion item. Returns `false` if item is already closed or if `keep-one-open` is enabled and this is the last open item, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the accordion instance, removes all generated markup, classes, and event listeners. Use when removing accordion from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSAccordion` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSAccordion.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSAccordion } \\| HTMLElement \\| null` | Returns the accordion instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSAccordion` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if accordion instance is not found. |\n| `HSAccordion.show(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. Only opens if item is not already open. |\n| `HSAccordion.hide(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. Only closes if item is open and `keep-one-open` allows it. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new accordion instance\nconst accordion = new HSAccordion(document.querySelector('#hs-accordion'));\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion programmatically\nshowBtn.addEventListener('click', () => {\n  accordion.show();\n});\n\n// Close accordion programmatically\nhideBtn.addEventListener('click', () => {\n  accordion.hide();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSAccordion.show('#hs-accordion');\n});\n\n// Close accordion using static method\nhideBtn.addEventListener('click', () => {\n  HSAccordion.hide('#hs-accordion');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSAccordion instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeAccordion() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying accordion instance**\n```javascript\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-accordion').remove();\n  });\n}\n```\n\n## Events\n\nAccordion instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeOpen` | Before accordion item opens | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts opening. Can be used to cancel or modify behavior. |\n| `on:open` | After accordion item opens | `HTMLElement` (current accordion element) | Fires after the accordion item has fully opened. Use for post-open actions like loading content. |\n| `on:beforeClose` | Before accordion item closes | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts closing. Can be used to cancel or modify behavior. |\n| `on:close` | After accordion item closes | `HTMLElement` (current accordion element) | Fires after the accordion item has fully closed. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (accordionElement) => {\n    console.log('Accordion opened:', accordionElement);\n    // Perform actions after accordion opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (accordionElement) => {\n    console.log('Accordion closed:', accordionElement);\n    // Perform cleanup or state updates\n  });\n\n  // Listen to beforeOpen event (can be used to cancel)\n  element.on('beforeOpen', (accordionElement) => {\n    console.log('About to open:', accordionElement);\n    // Can add conditional logic here\n  });\n\n  // Listen to beforeClose event (can be used to cancel)\n  element.on('beforeClose', (accordionElement) => {\n    console.log('About to close:', accordionElement);\n    // Can add conditional logic here\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Multiple Open Items\n\nAllow multiple accordion items to be open simultaneously.\n\n```html\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <div class=\"hs-accordion\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content hidden\">Content 1</div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: Always Keep One Open\n\nEnsure at least one item is always open.\n\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <div class=\"hs-accordion active\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content\">Content 1</div>\n  </div>\n  \n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl accordion from external buttons.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion\" id=\"hs-accordion-first\">\n    <button class=\"hs-accordion-toggle\">Content</button>\n    <div class=\"hs-accordion-content hidden\">Content here</div>\n  </div>\n</div>\n\n<button id=\"hs-open-all\">Open All</button>\n<button id=\"hs-close-all\">Close All</button>\n\n<script>\n  const instance = HSAccordion.getInstance('#hs-accordion-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-all').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-close-all').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/carousel/README.md":"# Carousel\n\nBuild carousels and sliders with Carousel JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/carousel) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/carousel.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Carousel component provides a flexible, feature-rich slider for displaying multiple items in a scrollable container. It supports autoplay, infinite loop, drag gestures, responsive breakpoints, and multiple display modes.\n\n**Key Features:**\n- Multiple slides in a scrollable container\n- Autoplay and infinite loop support\n- Drag/swipe gestures for navigation\n- Responsive breakpoints for different screen sizes\n- Multiple display modes (default, snap, bounded)\n- Programmatic control via JavaScript API\n- Keyboard navigation support\n- Customizable pagination dots\n\n## Installation\n\nTo get started, install Carousel plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/carousel\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/carousel */\n@source \"../node_modules/@preline/carousel/*.js\";\n@import \"./node_modules/@preline/carousel/variants.css\";\n@import \"./node_modules/@preline/carousel/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/carousel/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCarousel from \"@preline/carousel/non-auto.mjs\";\n\n  new HSCarousel(document.querySelector(\"#carousel\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/carousel` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/carousel\";\n```\n\n`@preline/carousel/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCarousel from \"@preline/carousel/non-auto\";\n\nHSCarousel.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#carousel\");\nif (el) new HSCarousel(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a carousel component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The carousel displays three slides with previous/next navigation buttons.\n\n```html\n<div data-hs-carousel='{\n    \"loadingClasses\": \"opacity-0\"\n  }' class=\"relative\">\n  <div class=\"hs-carousel relative overflow-hidden w-full min-h-64\">\n    <div class=\"hs-carousel-body absolute top-0 bottom-0 start-0 flex flex-nowrap transition-transform duration-700 opacity-0\">\n      <div class=\"hs-carousel-slide\">\n        1\n      </div>\n      <div class=\"hs-carousel-slide\">\n        2\n      </div>\n      <div class=\"hs-carousel-slide\">\n        3\n      </div>\n    </div>\n  </div>\n\n  <button type=\"button\" class=\"hs-carousel-prev\">\n    <span aria-hidden=\"true\">«</span>\n    <span class=\"sr-only\">Previous</span>\n  </button>\n  <button type=\"button\" class=\"hs-carousel-next\">\n    <span class=\"sr-only\">Next</span>\n    <span aria-hidden=\"true\">»</span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-carousel`: Required on the container element, contains configuration options\n- `hs-carousel`: Required wrapper for slides collection, must contain `overflow-hidden` class\n- `hs-carousel-body`: Required slides container\n- `hs-carousel-slide`: Required for each slide element\n- `hs-carousel-prev`: Optional previous slide button\n- `hs-carousel-next`: Optional next slide button\n- `hs-carousel-pagination`: Optional pagination container\n\n**Initial State:**\n- Carousel body typically starts with `opacity-0` which is removed after loading\n- First slide is active by default (index 0)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-carousel` attribute as a JSON object.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `currentIndex` | number | `0` | Specifies the index of the current slide initially (from 0 to slides quantity). |\n| `loadingClasses` | string | `\"opacity-0\"` | Specifies which classes should be removed after the carousel is loaded. CSS classes should be separated with space. |\n| `dotsItemClasses` | string | - | Specifies which classes will be added to the point elements (which are generated automatically). CSS classes should be separated with space. |\n| `isAutoHeight` | boolean | `false` | Sets the height of the carousel to the height of the current slide. |\n| `isAutoPlay` | boolean | `false` | Enables autoplay. Slides advance automatically at the interval specified by `speed`. |\n| `isInfiniteLoop` | boolean | `true` | Enables infinite loop. When reaching the last slide, it wraps to the first slide. |\n| `isCentered` | boolean | `false` | Enables centered mode. This mode adds space on the sides to center the slides. |\n| `isSnap` | boolean | `false` | Enables a mode in which you can scroll the contents of the slider using the scroll along the x-axis, while the active slider is centered relative to the slider. |\n| `isScrollBlocked` | boolean | `false` | Prevents the user from scrolling the slider. Useful when you need to prevent scrolling while the carousel is active. |\n| `isDraggable` | boolean | `false` | Adds the ability to change slides using dragging. Requires adding `hs-carousel:dragging:transition-none` class to the `hs-carousel-body`. Not working with `isSnap: true` option. |\n| `isRTL` | boolean | `false` | Turns on RTL (right-to-left) mode. |\n| `hasSnapSpacers` | boolean | `true` | Adds additional elements to create a constant centering effect. |\n| `slidesQty` | object \\| number | `1` | Allows to set the number of slides to display at a certain screen resolution if passed as an object (e.g., `{\"xs\": 1, \"md\": 2, \"lg\": 3}`). If passed as a number, the specified number of slides will be displayed for all screen resolutions. |\n| `slideBy` | object \\| number | `1` | Specifies how many slides to move when navigating through the carousel. Can be a number for all screen sizes or an object with breakpoint-specific values. |\n| `speed` | number | `4000` | Autoplay animation speed in milliseconds. Available if `isAutoplay: true`. |\n| `updateDelay` | number | `0` | Allows you to delay the update function when resizing a window. Suitable for a slider with images, for more accurate calculation of the size of the images. |\n| `mode` | `\"default\"` \\| `\"snap\"` \\| `\"bounded\"` | `\"default\"` | Defines the carousel's behavior mode. `default` for standard sliding, `snap` for scroll-based navigation with snap points, and `bounded` for a fixed-width container with responsive slides. |\n| `boundedOptions` | object | `null` | Configuration options for the bounded mode carousel. Includes `maxWidth` to set the maximum width of the carousel container, `slidesGap` to define spacing between slides, and `spacersWidth` to control the width of side spacers. When `spacersWidth` is set to `\"auto\"`, it automatically calculates equal spacing on both sides. |\n\n**Example:**\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 3000,\n  \"slidesQty\": {\"xs\": 1, \"md\": 2, \"lg\": 3},\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the carousel to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel` | Wrapper container | A wrapper for a collection of slides. Must contain the `overflow-hidden` class for correct functionality. |\n| `hs-carousel-body` | Slides container | Container for all slide elements. |\n| `hs-carousel-slide` | Each slide element | Identifies an individual slide. |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel-prev` | Previous button | Previous slide navigation button. |\n| `hs-carousel-next` | Next button | Next slide navigation button. |\n| `hs-carousel-pagination` | Pagination container | Pagination container. If it doesn't contain nested elements, dots are generated automatically. If it contains elements with `hs-carousel-pagination-item` class, they will act as dots. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-carousel-active:*` | A modifier that allows you to set Tailwind classes when the active slide was shown. |\n| `hs-carousel-disabled:*` | A modifier that allows you to set Tailwind classes for arrow buttons when they are `disabled`. |\n| `hs-carousel:dragging:*` | A modifier that allows you to set Tailwind classes for `hs-carousel-body` when dragging. |\n\n## JavaScript API\n\nThe `HSCarousel` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a carousel instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `goToPrev()` | None | `void` | Navigates to the previous slide. |\n| `goToNext()` | None | `void` | Navigates to the next slide. |\n| `goTo(index)` | `index`: `number` | `void` | Navigates to the slide at the specified index. Index starts from 0. |\n| `recalculateWidth()` | None | `void` | Recalculates the width of the carousel. Useful after dynamically adding or removing slides, or when window is resized. |\n| `destroy()` | None | `void` | Destroys the carousel instance, removes all generated markup, classes, and event listeners. Use when removing carousel from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCarousel` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCarousel.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCarousel \\| { id: string \\| number, element: HSCarousel } \\| null` | Returns the carousel instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCarousel` instance. If `isInstance` is `false` or omitted, returns the `HSCarousel` instance directly. Returns `null` if carousel instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new carousel instance\nconst carousel = new HSCarousel(document.querySelector('#hs-carousel'));\nconst goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n\n// Navigate to slide by index\ngoToSecondBtn.addEventListener('click', () => {\n  carousel.goTo(1); // Go to second slide (index 1)\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCarousel instance\n  const goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n  const prevBtn = document.querySelector('#hs-prev-btn');\n  const nextBtn = document.querySelector('#hs-next-btn');\n\n  // Navigate to specific slide\n  goToSecondBtn.addEventListener('click', () => {\n    element.goTo(1);\n  });\n\n  // Navigate to previous slide\n  prevBtn.addEventListener('click', () => {\n    element.goToPrev();\n  });\n\n  // Navigate to next slide\n  nextBtn.addEventListener('click', () => {\n    element.goToNext();\n  });\n\n  // Recalculate width after dynamic changes\n  function updateCarousel() {\n    element.recalculateWidth();\n  }\n\n  // Clean up when removing from DOM\n  function removeCarousel() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying carousel instance**\n```javascript\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-carousel').remove();\n  });\n}\n```\n\n## Events\n\nCarousel instances emit events that can be listened to for slide lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | After the active slide changes | `number` (zero-based index of the current slide) | Fires after navigating to the next or previous slide, or after `goTo()` is called. |\n\n### Event Usage Example\n\n```javascript\n// Get carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for slide changes\n  element.on('update', (currentIndex) => {\n    console.log('Active slide:', currentIndex);\n    // Update external indicators, counters, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Responsive Carousel\n\nDisplay different number of slides at different breakpoints.\n\n```html\n<div data-hs-carousel='{\n  \"slidesQty\": {\"xs\": 1, \"sm\": 2, \"md\": 3, \"lg\": 4}\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 2: Autoplay Carousel\n\nAutomatically advance slides at regular intervals.\n\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 5000,\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl carousel from external buttons.\n\n```html\n<div id=\"hs-carousel-first\" data-hs-carousel>\n  <!-- Carousel content -->\n</div>\n\n<button id=\"hs-go-to-first\">Go to First</button>\n<button id=\"hs-go-to-last\">Go to Last</button>\n\n<script>\n  const instance = HSCarousel.getInstance('#hs-carousel-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-go-to-first').addEventListener('click', () => {\n      element.goTo(0);\n    });\n\n    document.querySelector('#hs-go-to-last').addEventListener('click', () => {\n      // Assuming 5 slides (indices 0-4)\n      element.goTo(4);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/collapse/README.md":"# Collapse\n\nToggle the visibility of content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/collapse) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/collapse.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Collapse component allows you to show and hide content with smooth transitions. It's a simple, lightweight solution for creating expandable/collapsible sections without the complexity of accordion groups.\n\n**Key Features:**\n- Simple show/hide functionality\n- Smooth CSS transitions\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Collapse plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/collapse\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/collapse */\n@source \"../node_modules/@preline/collapse/*.js\";\n@import \"./node_modules/@preline/collapse/variants.css\";\n@import \"./node_modules/@preline/collapse/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/collapse/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCollapse from \"@preline/collapse/non-auto.mjs\";\n\n  new HSCollapse(document.querySelector(\"#collapse\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/collapse` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/collapse\";\n```\n\n`@preline/collapse/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCollapse from \"@preline/collapse/non-auto\";\n\nHSCollapse.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#collapse\");\nif (el) new HSCollapse(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a collapse component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The content is hidden by default and expands when the button is clicked.\n\n```html\n<button type=\"button\" class=\"hs-collapse-toggle\" id=\"hs-unstyled-collapse\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-heading\" data-hs-collapse=\"#hs-unstyled-collapse-heading\" role=\"region\">\n  Collapse\n</button>\n<div id=\"hs-unstyled-collapse-heading\" class=\"hs-collapse hidden w-full overflow-hidden transition-[height] duration-300\" aria-labelledby=\"hs-unstyled-collapse\">\n  This is a collapse body. The content expands and collapses when the toggle button is clicked, with smooth height transitions.\n</div>\n```\n\n**Structure Requirements:**\n- `hs-collapse-toggle`: Required button element that triggers the collapse\n- `hs-collapse`: Required container for collapsible content\n- `data-hs-collapse`: Required attribute on the toggle button, must be a valid CSS selector pointing to the collapse content\n- Unique `id` attributes for the toggle button and collapse content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the collapse content initially\n- Content visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure collapse behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-collapse` | `hs-collapse-toggle` (button) | string (CSS selector) | `null` | Collapse container selector. Must be a valid CSS selector pointing to the `hs-collapse` element. |\n\n**Example:**\n```html\n<button data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the collapse to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-collapse` | Content container | Identifies the collapsible content area |\n| `hs-collapse-toggle` | Button element | The clickable element that expands/collapses content |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-collapse-open:*` | A modifier that allows you to set Tailwind classes when the collapse is open. |\n\n## JavaScript API\n\nThe `HSCollapse` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a collapse instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed content. Returns `false` if content is already open, `true` otherwise. |\n| `hide()` | None | `boolean` | Collapses the currently open content. Returns `false` if content is already closed, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the collapse instance, removes all generated markup, classes, and event listeners. Use when removing collapse from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCollapse` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCollapse.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSCollapse } \\| null` | Returns the collapse instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCollapse` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if collapse instance is not found. |\n| `HSCollapse.show(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSCollapse.hide(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new collapse instance\nconst collapse = new HSCollapse(document.querySelector('#hs-collapse'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse programmatically\nshowBtn.addEventListener('click', () => {\n  collapse.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSCollapse.show('#hs-collapse');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCollapse instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeCollapse() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying collapse instance**\n```javascript\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-collapse').remove();\n  });\n}\n```\n\n## Events\n\nCollapse instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After collapse content is opened | `HTMLElement` (collapse element) | Fires after the collapse content has been displayed. Use for post-open actions like loading content. |\n| `on:hide` | After collapse content is closed | `HTMLElement` (collapse element) | Fires after the collapse content has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (collapseElement) => {\n    console.log('Collapse opened:', collapseElement);\n    // Perform actions after collapse opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to hide event\n  element.on('hide', (collapseElement) => {\n    console.log('Collapse closed:', collapseElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Programmatic Control\n\nControl collapse from external buttons.\n\n```html\n<button class=\"hs-collapse-toggle\" data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n\n<button id=\"hs-show-collapse\">Show</button>\n<button id=\"hs-hide-collapse\">Hide</button>\n\n<script>\n  const instance = HSCollapse.getInstance('#hs-collapse-content', true);\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-collapse').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-collapse').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/combobox/README.md":"# ComboBox\n\nDynamic suggestions as users type in an input field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/combobox) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/combobox.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe ComboBox component provides an autocomplete input field with dynamic suggestions. It supports both client-side filtering and API-based data fetching, customizable templates, and flexible data rendering options.\n\n**Key Features:**\n- Dynamic autocomplete suggestions\n- Client-side and API-based data fetching\n- Customizable item templates\n- Keyboard navigation support\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install ComboBox plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/combobox\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/combobox */\n@source \"../node_modules/@preline/combobox/*.js\";\n@import \"./node_modules/@preline/combobox/variants.css\";\n@import \"./node_modules/@preline/combobox/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/combobox/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCombobox from \"@preline/combobox/non-auto.mjs\";\n\n  new HSCombobox(document.querySelector(\"#combobox\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/combobox` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/combobox\";\n```\n\n`@preline/combobox/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCombobox from \"@preline/combobox/non-auto\";\n\nHSCombobox.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#combobox\");\nif (el) new HSCombobox(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a combobox component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The combobox shows suggestions as the user types.\n\n```html\n<div class=\"inline-flex relative\" data-hs-combo-box>\n  <div class=\"relative\">\n    <input type=\"text\" role=\"combobox\" aria-expanded=\"false\" value=\"\" data-hs-combo-box-input />\n  </div>\n  \n  <div class=\"absolute z-50\" style=\"display: none;\" data-hs-combo-box-output>\n    <div class=\"cursor-pointer\" tabindex=\"0\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"1\" data-hs-combo-box-value>1</span>\n    </div>\n    <div class=\"cursor-pointer\" tabindex=\"1\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"2\" data-hs-combo-box-value>2</span>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-combo-box`: Required on the container element\n- `data-hs-combo-box-input`: Required on the input element\n- `data-hs-combo-box-output`: Required on the dropdown container\n- `data-hs-combo-box-output-item`: Required on each suggestion item\n- `data-hs-combo-box-search-text`: Required on the searchable text element within each item\n- Proper ARIA attributes (`role=\"combobox\"`, `aria-expanded`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the input initially\n- Add `style=\"display: none;\"` to the output container initially\n- Output visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-combo-box` attribute and various data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-combo-box` | Container | - | - | Activate a ComboBox by specifying on an element. Should be added to the container. |\n| `:gap` | Inside `data-hs-combo-box` | number | `5` | Determines the indent from the input field to the dropdown list in pixels. |\n| `:viewport` | Inside `data-hs-combo-box` | string \\| HTMLElement | `null` | Determines which element will be the parent when calculating the position of the dropdown list. By default it is calculated relative to the document. |\n| `:minSearchLength` | Inside `data-hs-combo-box` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:apiUrl` | Inside `data-hs-combo-box` | string \\| null | `null` | Specifies the API address for fetching suggestions. |\n| `:apiDataPart` | Inside `data-hs-combo-box` | string \\| null | `null` | Determines which part of the returned data will be taken as the main data array. If this parameter is empty, then the root is taken as a basis. |\n| `:apiQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request parameters, such as limit. |\n| `:apiSearchQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines the name of the search parameter. |\n| `:apiHeaders` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request headers. |\n| `:apiSearchPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that specifies the endpoint path for the API request, excluding the base URL. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:apiSearchDefaultPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that defines the default API endpoint path to be used when the input field is empty. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:outputItemTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for a single item in a dropdown list. |\n| `:outputEmptyTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for an empty placeholder. |\n| `:outputLoaderTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for the loader when loading data. |\n| `:preventAutoPosition` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it disables dropdown auto-positioning. |\n| `:preventClientFiltering` | Inside `data-hs-combo-box` | boolean | `true` (if `:apiSearchQuery` is not empty) | If `true`, it disables client-side filtering. |\n| `:keepOriginalOrder` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it keeps the original order of items. |\n| `:preserveSelectionOnEmpty` | Inside `data-hs-combo-box` | boolean | `true` | If `true`, it preserves the selected value when the input is empty. |\n| `data-hs-combo-box-input` | Input element | - | - | Determines which element (input) inside the initialized container will be responsible for entering data. |\n| `data-hs-combo-box-output` | Output container | - | - | Determines which element inside the initialized container will be responsible for data output. |\n| `data-hs-combo-box-toggle` | Toggle element | - | - | Defines an element inside the initialized container that will be responsible for hiding/showing the dropdown list. |\n| `data-hs-combo-box-close` | Close element | - | - | Defines an element inside the initialized container that will be responsible for hiding the dropdown list. |\n| `data-hs-combo-box-open` | Open element | - | - | Defines an element inside the initialized container that will be responsible for showing the dropdown list. |\n| `data-hs-combo-box-output-item` | Item element | - | - | Defines an element inside the initialized container that will be responsible for displaying each individual data element. |\n| `data-hs-combo-box-search-text` | Search text element | - | - | Defines the element within which text will be searched. |\n| `data-hs-combo-box-value` | Value element | - | - | Defines an element whose text will be set as a value when selected. |\n| `data-hs-combo-box-output-item-field` | Item field | string \\| string[] | - | Defines a data field that will be taken to fill the element with text. |\n| `data-hs-combo-box-output-item-attr` | Item attribute | - | - | Defines the attributes that will be filled with the corresponding data. For example, the src attribute of an image. |\n| `:valueFrom` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the field from which data will be taken. |\n| `:attr` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the attribute where the data will be supplied. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-combo-box-active:*` | A modifier that allows you to set Tailwind classes when ComboBox has been opened. |\n| `hs-combo-box-has-value:*` | A modifier that allows you to set Tailwind classes when ComboBox's input has value. |\n| `hs-combo-box-selected:*` | A modifier that allows you to set Tailwind classes when `data-hs-combo-box-output-item` has been selected. |\n| `hs-combo-box-tab-active:*` | A modifier that allows you to set Tailwind classes to the active group item (tab) when `groupingType` is `tabs`. |\n\n## JavaScript API\n\nThe `HSComboBox` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a combobox instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentData()` | None | `object \\| object[]` | Returns the data of the selected element as an object. This data must first be assigned as a value to the data parameter `data-hs-combo-box-item-stored-data` when standard rendering. When rendering via the API, the data is automatically substituted into the data attribute. |\n| `setCurrent()` | None | `void` | Sets the current selection based on the input value. |\n| `open(val)` | `val`: `string` (optional) | `boolean` | Opens the autosuggestion list. Optionally accepts a value to set in the input. Returns `true` if opened successfully, `false` otherwise. |\n| `close(val, data)` | `val`: `string \\| null` (optional)<br>`data`: `object \\| null` (optional) | `boolean` | Closes the autosuggestion list. Optionally accepts a value and data to set. Returns `true` if closed successfully, `false` otherwise. |\n| `recalculateDirection()` | None | `void` | Forces recalculation of autosuggestion list positions. Useful after window resize or layout changes. |\n| `destroy()` | None | `void` | Destroys the combobox instance, removes all generated markup, classes, and event listeners. Use when removing combobox from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSComboBox` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSComboBox.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSComboBox \\| { id: string \\| number, element: HSComboBox } \\| null` | Returns the combobox instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSComboBox` instance. If `isInstance` is `false` or omitted, returns the `HSComboBox` instance directly. Returns `null` if combobox instance is not found. |\n| `HSComboBox.autoInit()` | None | `void` | Reinitializes all ComboBoxes on the page. Useful after dynamically adding comboboxes to DOM. |\n| `HSComboBox.close(target)` | `target`: `HTMLElement \\| string` (CSS selector) | `void` | Closes the combobox identified by target. Accepts a DOM element or CSS selector string. |\n| `HSComboBox.closeCurrentlyOpened()` | None | `void` | Closes an already open ComboBox. Useful for closing any open combobox before opening a new one. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new combobox instance\nconst comboBox = new HSComboBox(document.querySelector('#hs-combo-box'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open combobox programmatically\nopenBtn.addEventListener('click', () => {\n  comboBox.open();\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the combobox instance\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSComboBox instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Get selected data\n  const selectedData = element.getCurrentData();\n  console.log('Selected:', selectedData);\n\n  // Clean up when removing from DOM\n  function removeComboBox() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying combobox instance**\n```javascript\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-combo-box').remove();\n  });\n}\n```\n\n## Events\n\nCombobox instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:select` | When an item is selected from the dropdown | `object` (the selected item data object) | Fires after the user clicks an option in the combobox dropdown. |\n\n### Event Usage Example\n\n```javascript\n// Get combobox instance\nconst instance = HSCombobox.getInstance('#hs-combobox', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for item selection\n  element.on('select', (data) => {\n    console.log('Selected item:', data);\n    // Use selected data to update other parts of the UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: API-based ComboBox\n\nFetch suggestions from an API endpoint.\n\n```html\n<div data-hs-combo-box='{\n  \"apiUrl\": \"https://api.example.com/search\",\n  \"apiSearchQuery\": \"q\",\n  \"minSearchLength\": 2\n}'>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl combobox from external buttons.\n\n```html\n<div id=\"hs-combo-box-first\" data-hs-combo-box>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n\n<button id=\"hs-open-combo-box\">Open</button>\n<button id=\"hs-close-combo-box\">Close</button>\n\n<script>\n  const instance = HSComboBox.getInstance('#hs-combo-box-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-combo-box').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-combo-box').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/copy-markup/README.md":"# Copy Markup\n\nCopy input, select or other markups.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/copy-markup) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/copy-markup.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field.\n\n**Key Features:**\n- One-click element duplication\n- Configurable copy limits\n- Automatic element management\n- Programmatic control via JavaScript API\n- Event system for copy/delete tracking\n- Support for any HTML markup\n\n## Installation\n\nTo get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/copy-markup\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/copy-markup */\n@import \"./node_modules/@preline/copy-markup/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/copy-markup/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCopyMarkup from \"@preline/copy-markup/non-auto.mjs\";\n\n  new HSCopyMarkup(document.querySelector(\"#copy-markup\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/copy-markup` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/copy-markup\";\n```\n\n`@preline/copy-markup/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCopyMarkup from \"@preline/copy-markup/non-auto\";\n\nHSCopyMarkup.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#copy-markup\");\nif (el) new HSCopyMarkup(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element.\n\n```html\n<div id=\"hs-wrapper-for-copy-first\">\n  <input id=\"hs-content-for-copy-first\" type=\"text\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500\" placeholder=\"Enter Name\">\n</div>\n\n<button type=\"button\" data-hs-copy-markup='{\n    \"targetSelector\": \"#hs-content-for-copy-first\",\n    \"wrapperSelector\": \"#hs-wrapper-for-copy-first\",\n    \"limit\": 3\n  }' id=\"hs-copy-content-first\">\n  Add Name\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-copy-markup`: Required on the trigger button, contains configuration options as JSON\n- `targetSelector`: Required, must be a valid CSS selector pointing to the element to copy\n- `wrapperSelector`: Required, must be a valid CSS selector pointing to the container where copies will be added\n- Target element must exist in the DOM\n\n**Initial State:**\n- One instance of the target element exists\n- Copy button is enabled (unless limit is reached)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-copy-markup` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-copy-markup` | Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). |\n| `:targetSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. |\n| `:wrapperSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. |\n| `:limit` | Inside `data-hs-copy-markup` | number | `null` | Specifies how many items you can copy until the button is disabled. `null` means no limit. |\n\n**Example:**\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-input-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field\n</button>\n```\n\n## JavaScript API\n\nThe `HSCopyMarkup` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a copy markup instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `delete(target)` | `target`: `HTMLElement` | `void` | Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. |\n| `destroy()` | None | `void` | Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCopyMarkup` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCopyMarkup.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCopyMarkup \\| { id: string \\| number, element: HSCopyMarkup } \\| null` | Returns the copy markup instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCopyMarkup` instance. If `isInstance` is `false` or omitted, returns the `HSCopyMarkup` instance directly. Returns `null` if copy markup instance is not found. |\n\n### Usage Examples\n\n**Example 1: Deleting copied elements**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const deleteBtn = document.querySelector('#hs-delete-btn');\n\n  deleteBtn.addEventListener('click', () => {\n    // Delete a specific copied element\n    const copiedElement = document.querySelector('#hs-copy-markup-item-first');\n\n    if (copiedElement) {\n      element.delete(copiedElement);\n    }\n  });\n}\n```\n\n**Example 2: Using instance methods (recommended pattern)**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Items:', element.items);\n  console.log('Limit:', element.limit);\n\n  // Delete a copied element programmatically\n  function removeCopiedItem(itemElement) {\n    element.delete(itemElement);\n  }\n\n  // Clean up when removing from DOM\n  function removeCopyMarkup() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying copy markup instance**\n```javascript\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-copy-content').remove();\n  });\n}\n```\n\n## Events\n\nCopy markup instances emit events that can be listened to for copy/delete tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:copy` | When target element was copied | `HTMLElement` (copied element) | Fires when a new copy of the target element is created. Returns the newly created element. |\n| `on:delete` | When target element was deleted | `HTMLElement` (deleted element) | Fires when a copied element is deleted. Returns the deleted element. |\n\n### Event Usage Example\n\n```javascript\n// Get copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to copy event\n  element.on('copy', (copiedElement) => {\n    console.log('Element copied:', copiedElement);\n    // Perform actions after element is copied\n    // e.g., initialize other plugins on the new element, update counters\n  });\n\n  // Listen to delete event\n  element.on('delete', (deletedElement) => {\n    console.log('Element deleted:', deletedElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Limited Copies\n\nLimit the number of copies that can be created.\n\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field (Max 5)\n</button>\n```\n\n### Pattern 2: Dynamic Form Fields\n\nCreate dynamic form fields with copy functionality.\n\n```html\n<div id=\"hs-fields-wrapper\">\n  <input id=\"hs-field-template\" type=\"text\" placeholder=\"Field name\">\n</div>\n\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field-template\",\n  \"wrapperSelector\": \"#hs-fields-wrapper\"\n}'>\n  Add Another Field\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/datatable/README.md":"# Datatables\n\nDatatables solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datatable) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/datatables.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [CSS](#css)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Datatables component provides a powerful data table interface with sorting, searching, pagination, and row selection capabilities. It integrates with DataTables.net for advanced table functionality.\n\n**Key Features:**\n- Column sorting\n- Search functionality\n- Pagination\n- Row selection (single and multiple)\n- Customizable paging controls\n- Integration with DataTables.net\n- Programmatic control via JavaScript API\n- Event system for table interactions\n\n## Installation\n\nTo get started, install DataTable plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i jquery datatables.net-dt @preline/datatable\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/datatable */\n@source \"../node_modules/@preline/datatable/*.js\";\n@import \"./node_modules/@preline/datatable/variants.css\";\n@import \"./node_modules/@preline/datatable/theme.css\";\n\n.dt-layout-row:has(.dt-search),\n.dt-layout-row:has(.dt-length),\n.dt-layout-row:has(.dt-paging) {\n  display: none !important;\n}\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script src=\"./node_modules/@preline/datatable/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script type=\"module\">\n  import HSDataTable from \"@preline/datatable/non-auto.mjs\";\n\n  new HSDataTable(document.querySelector(\"#datatable\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `jQuery` and `DataTable` are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/datatable` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport \"@preline/datatable\";\n```\n\n`@preline/datatable/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport HSDataTable from \"@preline/datatable/non-auto\";\n\nHSDataTable.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datatable\");\nif (el) new HSDataTable(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a datatable component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The table supports sorting, searching, and pagination.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\">\n          Name\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\">\n          Age\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\" class=\"--exclude-from-ordering\">\n          Action\n        </th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>John Brown</td>\n        <td>45</td>\n        <td>\n          <button type=\"button\">Delete</button>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n\n  <div style=\"display: none;\" data-hs-datatable-paging>\n    <button type=\"button\" data-hs-datatable-paging-prev>\n      <span aria-hidden=\"true\">«</span>\n      <span class=\"sr-only\">Previous</span>\n    </button>\n    <div data-hs-datatable-paging-pages></div>\n    <button type=\"button\" data-hs-datatable-paging-next>\n      <span class=\"sr-only\">Next</span>\n      <span aria-hidden=\"true\">»</span>\n    </button>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-datatable`: Required on the container element\n- Standard HTML `<table>` structure with `<thead>` and `<tbody>`\n- Optional paging controls with `data-hs-datatable-paging`\n- Optional search input with `data-hs-datatable-search`\n- Optional row selection checkboxes\n\n**Initial State:**\n- Table displays all data\n- Sorting is enabled on columns (unless excluded)\n- Pagination controls are hidden if not needed\n\n## CSS\n\nPlease add the following CSS to the head. This is necessary because Datatables.net does not allow to add custom markup with options disabled, such as `paging: false` or `searching: false`.\n\n```html\n<style>\n  .dt-layout-row:has(.dt-search),\n  .dt-layout-row:has(.dt-length),\n  .dt-layout-row:has(.dt-paging) {\n    display: none !important;\n  }\n</style>\n```\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-datatable` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datatable` | Container | - | - | Activate a Datatable by specifying on an element. Should be added to the container. |\n| `:pageBtnClasses` | Inside `data-hs-datatable` | string | `\"-\"` | Sets the specified classes to each paging element inside the `data-hs-datatable-paging-pages`. |\n| `:rowSelectingOptions.selectAllSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-all]\"` | A valid CSS selector (the element must be a checkbox or radio button), when the element is changed, all elements specified by `rowSelectingOptions.individualSelector` (or, if not specified, then `data-hs-datatable-row-selecting-individual`) on the current page will be changed. |\n| `:rowSelectingOptions.individualSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-individual]\"` | A valid CSS selector (the element must be a checkbox or radio button), affects the change of the element specified in `rowSelectingOptions.selectAllSelector`, if not all elements are selected on the current page, then it switches to `unchecked` state, if vice versa, then in `checked`. |\n\n**Example:**\n```html\n<div data-hs-datatable='{\n  \"pageBtnClasses\": \"px-3 py-1 border rounded\",\n  \"rowSelectingOptions\": {\n    \"selectAllSelector\": \"#hs-select-all\",\n    \"individualSelector\": \".hs-row-checkbox\"\n  }\n}'>\n  <!-- Table structure -->\n</div>\n```\n\n### Class Options\n\nCSS class modifiers use `--` prefix to control datatable behavior.\n\n| Class Modifier | Target Element | Purpose |\n| --- | --- | --- |\n| `--exclude-from-ordering` | Table header (`<th>`) | A modifier that allows you to disable the ordering functionality in a column. Should be added to the thead column. |\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for specific features to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-datatable-search` | Input element | Defines which element will be responsible for searching in the datatable (must be an `input`). |\n| `data-hs-datatable-page-entities` | Select element | Determines which element will be responsible for switching the number of elements on the page (must be a `select`, depends on the change event). |\n| `data-hs-datatable-paging` | Container element | Defines a paging container, that contains pagination, next and previous buttons. |\n| `data-hs-datatable-paging-prev` | Button element | Defines a previous button element. |\n| `data-hs-datatable-paging-next` | Button element | Defines a next button element. |\n| `data-hs-datatable-row-selecting-all` | Checkbox/Radio element | Defines a \"Select all\" checkbox/radio, if it's not specified in the `rowSelectingOptions.selectAllSelector` parameter. |\n| `data-hs-datatable-row-selecting-individual` | Checkbox/Radio element | Defines a checkbox/radio that will be responsible for selecting a specific row if it is not specified in the `rowSelectingOptions.individualSelector` parameter. |\n| `data-hs-datatable-info` | Container element | Defines an info container. |\n| `data-hs-datatable-info-from` | Element | Defines an element that showing `start` number. |\n| `data-hs-datatable-info-to` | Element | Defines an element that showing `end` number. |\n| `data-hs-datatable-info-length` | Element | Defines an element that showing `recordsTotal` number. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-datatable-ordering-asc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `asc`. |\n| `hs-datatable-ordering-desc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `desc`. |\n\n## JavaScript API\n\nThe `HSDataTable` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a datatable instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the datatable instance, removes all generated markup, classes, and event listeners. Use when removing datatable from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDataTable` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDataTable.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDataTable } \\| null` | Returns the datatable instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDataTable` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datatable instance is not found. |\n\n### Usage Examples\n\n**Example 1: Accessing DataTables.net instance**\n```javascript\n// Get the datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying DataTables.net instance\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Use DataTables.net methods\n    dataTable.search('query').draw();\n  }\n}\n```\n\n**Example 2: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 3: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-datatable').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most events available in the plugin are available in our wrapper.\n\nDatatable instances emit events through the underlying DataTables.net instance. Access the DataTables.net instance via `element.dataTable` to listen to events.\n\n**Common DataTables.net Events:**\n- `draw` - When table is redrawn\n- `order` - When column ordering changes\n- `search` - When search is performed\n- `page` - When page changes\n- `select` - When row is selected\n- `deselect` - When row is deselected\n\n### Event Usage Example\n\n```javascript\n// Get datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Listen to draw event\n    dataTable.on('draw.dt', () => {\n      console.log('Table redrawn!');\n      // Perform actions after table redraws\n      // e.g., update UI, reinitialize plugins\n    });\n\n    // Listen to order event\n    dataTable.on('order.dt', () => {\n      console.log('Column ordering changed!');\n    });\n\n    // Listen to search event\n    dataTable.on('search.dt', () => {\n      console.log('Search performed!');\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Search\n\nAdd search functionality to the datatable.\n\n```html\n<input type=\"text\" data-hs-datatable-search placeholder=\"Search...\">\n\n<div data-hs-datatable>\n  <!-- Table structure -->\n</div>\n```\n\n### Pattern 2: With Row Selection\n\nEnable row selection with checkboxes.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-all>\n        </th>\n        <!-- Other columns -->\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-individual>\n        </td>\n        <!-- Other cells -->\n      </tr>\n    </tbody>\n  </table>\n</div>\n```\n\n### Pattern 3: Exclude Column from Ordering\n\nDisable sorting on specific columns.\n\n```html\n<th scope=\"col\" class=\"--exclude-from-ordering\">\n  Actions\n</th>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/datepicker/README.md":"# Advanced Datepicker\n\nCreate visually appealing and interactive datepickers in combination with the Datepicker JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datepicker) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-datepicker.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Datepicker component provides a comprehensive date selection interface with support for custom templates, multiple date formats, time selection, and integration with Vanilla Calendar Pro. It offers flexible configuration options for styling and behavior.\n\n**Key Features:**\n- Date and time selection\n- Custom select inputs for months, years, and time\n- Multiple date formats and locales\n- Custom templates for arrows and time display\n- Integration with Vanilla Calendar Pro\n- Programmatic control via JavaScript API\n- Utility classes support\n\n## Installation\n\nTo get started, install Datepicker plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select @preline/datepicker\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\nUse `styles.css` with `theme.css` for the default themed appearance:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles.css\";\n@import \"./node_modules/@preline/datepicker/theme.css\";\n```\n\nUse `styles-utility.css` instead when you plan to style the datepicker entirely with Tailwind utility classes. In this case `theme.css` is not needed:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles-utility.css\";\n```\n\n`@preline/select` is required for styling the embedded select dropdowns used in `custom-select` mode.\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/datepicker/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDatepicker from \"@preline/datepicker/non-auto.mjs\";\n\n  new HSDatepicker(document.querySelector(\"#datepicker\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `vanilla-calendar-pro` and the Advanced Select plugin are bundled inside `@preline/datepicker`, so no separate imports are needed.\n\n`@preline/datepicker` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/datepicker\";\n```\n\n`@preline/datepicker/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDatepicker from \"@preline/datepicker/non-auto\";\n\nHSDatepicker.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datepicker\");\nif (el) new HSDatepicker(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced datepicker component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The datepicker appears when the input is focused.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\" placeholder=\"\" \n  readonly\n  data-hs-datepicker='{\n    \"type\": \"default\",\n    \"dateMax\": \"2050-12-31\",\n    \"styles\": {\n      \"week\": \"vc-week\",\n      \"weekDay\": \"vc-week__day\",\n      \"dates\": \"vc-dates\",\n      \"date\": \"vc-date\",\n      \"arrowPrev\": \"vc-arrow vc-arrow_prev\",\n      \"arrowNext\": \"vc-arrow vc-arrow_next\"\n    },\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n**Structure Requirements:**\n- `hs-datepicker`: Required class on the input element\n- `data-hs-datepicker`: Required attribute containing configuration options as JSON\n- `readonly` attribute on the input to prevent manual editing\n- `type=\"text\"` on the input element\n\n**Initial State:**\n- Input field is empty by default\n- Datepicker appears when input is focused or clicked\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most options available in the plugin are available in our wrapper. Check [Vanilla Calendar Pro Options](https://vanilla-calendar.pro/docs/reference/settings/) for more details.\n\nData options are specified in the `data-hs-datepicker` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datepicker` | Input element | - | - | Activate a Datepicker by specifying on an element. Should be added to the element itself. |\n| `:mode` | Inside `data-hs-datepicker` | `\"default\"` \\| `\"custom-select\"` | `\"default\"` | This option allows you to specify the operational mode of the datepicker. `default` uses standard layout provided by Vanilla Calendar Pro. `custom-select` activates custom select inputs for months, years and time elements. |\n| `:applyUtilityClasses` | Inside `data-hs-datepicker` | boolean | `false` | Specifies whether to apply utility classes to the datepicker. If set to `true`, the datepicker receives utility classes based on the `primary` theme. |\n| `:inputModeOptions.dateSeparator` | Inside `data-hs-datepicker` | string | `\".\"` | Specifies the character used to separate the year, month, and day in the date format displayed in the input field. E.g., `/` produces a date format like `2024/01/22`. |\n| `:inputModeOptions.itemsSeparator` | Inside `data-hs-datepicker` | string | `\", \"` | Specifies the character or string used to separate multiple date entries in the input field when multiple dates are selected. E.g., `\\|` produces a format like `2024-01-22 \\| 2024-01-23`. |\n| `:styles.customSelect.shared` | Inside `data-hs-datepicker` | object | - | Defines shared styling configurations for Advanced Custom Select components. These styles are consistently applied to all instances of the Advanced Custom Select within the wrapper. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.months` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"months\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.years` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"years\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.hours` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"hours\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.minutes` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"minutes\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.meridiem` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"meridiem\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:templates.time` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for rendering the time selection interface within the datepicker. |\n| `:templates.arrowPrev` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"previous\" navigation arrow in the datepicker. |\n| `:templates.arrowNext` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"next\" navigation arrow in the datepicker. |\n| `:dateFormat` | Inside `data-hs-datepicker` | string | - | Allows to define a custom date format. |\n| `:dateLocale` | Inside `data-hs-datepicker` | string | - | Allows to define the locale for the datepicker. |\n| `:replaceTodayWithText` | Inside `data-hs-datepicker` | boolean | - | Replace the current day name with the text \"Today\". |\n\n**Example:**\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\",\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"dateLocale\": \"en\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-vc-date-today:*` | A modifier that applies Tailwind classes to the current date (marked as \"today\") using the attribute `data-vc-date-today`. |\n| `hs-vc-date-selected:*` | A modifier that applies Tailwind classes to the selected dates using the attribute `data-vc-date-selected`. |\n| `hs-vc-date-weekend:*` | A modifier that applies Tailwind classes to weekend dates using the attribute `data-vc-date-weekend`. |\n| `hs-vc-week-day-off:*` | A modifier that applies Tailwind classes to weekdays marked as \"day off\" using the attribute `data-vc-week-day-off`. |\n| `hs-vc-date-month-prev:*` | A modifier that applies Tailwind classes to dates belonging to the previous month using the class `data-vc-date-month=\"prev\"`. |\n| `hs-vc-date-month-next:*` | A modifier that applies Tailwind classes to dates belonging to the next month using the attribute `data-vc-date-month=\"next\"`. |\n| `hs-vc-calendar-selected-middle:*` | A modifier that applies Tailwind classes to dates that are in the middle of a selected range using the attribute `data-vc-date-selected=\"middle\"`. |\n| `hs-vc-calendar-selected-first:*` | A modifier that applies Tailwind classes to the first date in a selected range using the attribute `data-vc-date-selected=\"first\"`. |\n| `hs-vc-calendar-selected-last:*` | A modifier that applies Tailwind classes to the last date in a selected range using the attribute `data-vc-date-selected=\"last\"`. |\n| `hs-vc-months-month-selected:*` | A modifier that applies Tailwind classes to the currently selected month using the attribute `data-vc-months-month-selected`. |\n| `hs-vc-years-year-selected:*` | A modifier that applies Tailwind classes to the currently selected year using the attribute `data-vc-years-year-selected`. |\n\n## JavaScript API\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most methods available in the plugin are available through our wrapper. Check [Vanilla Calendar Pro Methods](https://vanilla-calendar.pro/docs/reference/methods/) for more details.\n\nThe `HSDatepicker` object is available in the global `window` object after the plugin is loaded. Most methods from Vanilla Calendar Pro are available through the wrapper.\n\n### Instance Methods\n\nThese methods are called on a datepicker instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `{ selectedDates: DatesArr, selectedTime: string }` | Returns the current state of the datepicker including selected dates and time. |\n| `formatDate(date, format)` | `date`: `string \\| number \\| Date`<br>`format`: `string` (optional) | `string` | Formats a date according to the specified format or the datepicker's default format. |\n| `destroy()` | None | `void` | Destroys the datepicker instance, removes all generated markup, classes, and event listeners. Use when removing datepicker from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDatepicker` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDatepicker.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDatepicker } \\| null` | Returns the datepicker instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDatepicker` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datepicker instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('Selected dates:', state.selectedDates);\n  console.log('Selected time:', state.selectedTime);\n}\n```\n\n**Example 2: Formatting dates**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Format a date\n  const formatted = element.formatDate(new Date(), 'YYYY-MM-DD');\n\n  console.log('Formatted date:', formatted);\n}\n```\n\n**Example 3: Destroying datepicker instance**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('.hs-datepicker').remove();\n  });\n}\n```\n\n## Events\n\nDatepicker instances emit events that can be listened to for date selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected date or time changes | `{ selectedDates: string[], selectedTime: string }` (current selection state) | Fires after the user picks a date or changes the time. The payload matches the shape returned by `getCurrentState()`. |\n\n### Event Usage Example\n\n```javascript\n// Get datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for date or time changes\n  element.on('change', ({ selectedDates, selectedTime }) => {\n    console.log('Selected dates:', selectedDates);\n    console.log('Selected time:', selectedTime);\n    // Update dependent fields, validate form, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Select Mode\n\nUse custom select inputs for months, years, and time.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n### Pattern 2: Custom Date Format\n\nSet a custom date format and separator.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/dropdown/README.md":"# Dropdown\n\nA dropdown menu displays a list of actions and more with JavaScript dropdown plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/dropdown) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/dropdown.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (click, hover, contextmenu)\n- Floating UI integration for smart positioning\n- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/dropdown\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/dropdown */\n@source \"../node_modules/@preline/dropdown/*.js\";\n@import \"./node_modules/@preline/dropdown/variants.css\";\n@import \"./node_modules/@preline/dropdown/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/dropdown/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDropdown from \"@preline/dropdown/non-auto.mjs\";\n\n  new HSDropdown(document.querySelector(\"#dropdown\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/dropdown` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/dropdown\";\n```\n\n`@preline/dropdown/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDropdown from \"@preline/dropdown/non-auto\";\n\nHSDropdown.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#dropdown\");\nif (el) new HSDropdown(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.\n\n```html\n<div class=\"hs-dropdown relative inline-flex\">\n  <button id=\"hs-dropdown-unstyled\" type=\"button\" class=\"hs-dropdown-toggle inline-flex justify-center items-center gap-x-2\" aria-expanded=\"false\" aria-label=\"Menu\">\n    Actions\n  </button>\n\n  <div class=\"hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white\" role=\"menu\" aria-labelledby=\"hs-dropdown-unstyled\">\n    <a class=\"block\" href=\"#\">Newsletter</a>\n    <a class=\"block\" href=\"#\">Purchases</a>\n    <a class=\"block\" href=\"#\">Downloads</a>\n    <a class=\"block\" href=\"#\">Team Account</a>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-dropdown`: Required container wrapper\n- `hs-dropdown-toggle`: Required button element that triggers the dropdown\n- `hs-dropdown-menu`: Required container for dropdown menu items\n- Proper ARIA attributes (`aria-expanded`, `aria-label`, `role=\"menu\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the dropdown menu initially\n- Menu visibility is controlled by `hs-dropdown-open:*` modifier classes\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| Enter | Activates the selected menu item. |\n| ArrowUp / ArrowDown | Focuses previous/next non-disabled item. |\n| Home / End | Focuses first/last non-disabled item. |\n| Esc | Closes any open dropdown menus. |\n| A-Z / a-z | Focuses first item that matches keyboard input. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure dropdown behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-dropdown-transition` | `hs-dropdown` (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control dropdown behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--placement:*]` | `hs-dropdown` (container) | `\"auto\"` \\| `\"auto-start\"` \\| `\"auto-end\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-start\"` \\| `\"right-end\"` \\| `\"left\"` \\| `\"left-start\"` \\| `\"left-end\"` | `\"bottom\"` | Specifies the position of the menu when opened relative to the toggle button. |\n| `[--scope:*]` | `hs-dropdown` (container) | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `[--auto-close:*]` | `hs-dropdown` (container) | `\"inside\"` \\| `\"outside\"` \\| `\"false\"` \\| `\"true\"` | `\"true\"` | Specifies the zone, when clicked, which will close the menu. `inside` closes only when clicking inside, `outside` closes only when clicking outside, `true` closes on any click, `false` prevents auto-close. |\n| `[--has-autofocus:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening a dropdown. Set to `\"false\"` to disable autofocus. |\n| `[--strategy:*]` | `hs-dropdown` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"fixed\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--adaptive:*]` | `hs-dropdown` (container) | `\"none\"` \\| `\"adaptive\"` | `\"adaptive\"` | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |\n| `[--gpu-acceleration:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Disable/enable position calculation using the transform property. Set to `\"true\"` to enable GPU acceleration. |\n| `[--offset:*]` | `hs-dropdown` (container) | number | `10` | Sets the dropdown's bottom or top offset in pixels. |\n| `[--flip:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Flips the menu's placement when it starts to overlap its reference element. Set to `\"false\"` to disable flipping. |\n| `[--trigger:*]` | `hs-dropdown` (container) | `\"hover\"` \\| `\"click\"` \\| `\"contextmenu\"` | `\"click\"` | Event to trigger a dropdown. `hover` shows on mouse enter, `click` shows on click, `contextmenu` shows on right-click. |\n\n**Example:**\n```html\n<div class=\"hs-dropdown --trigger:hover --placement:top --auto-close:false\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the dropdown to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown` | Container wrapper | Groups the toggle and menu elements together |\n| `hs-dropdown-toggle` | Button element | The clickable element that activates the dropdown |\n| `hs-dropdown-menu` | Menu container | The dropdown menu that appears on trigger |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown-toggle-wrapper` | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a \"+\" button inside an existing Dropdown toggle button that opens a modal. |\n| `hs-dropdown-close` | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dropdown-open:*` | The modifier that allows you to set Tailwind classes when the dropdown menu is open. |\n\n## JavaScript API\n\nThe `HSDropdown` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a dropdown instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(target, openedViaKeyboard)` | `target`: `VirtualElement \\| HTMLElement` (optional)<br>`openedViaKeyboard`: `boolean` (optional) | `boolean` | Forces the dropdown menu to open programmatically. Returns `true` if opened successfully, `false` otherwise. |\n| `close(isAnimated)` | `isAnimated`: `boolean` (optional) | `boolean` | Forces the dropdown menu to close programmatically. `isAnimated` indicates whether the dropdown menu is closed with animation. Returns `true` if closed successfully, `false` otherwise. |\n| `forceClearState()` | None | `void` | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |\n| `destroy()` | None | `void` | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDropdown` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDropdown.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSDropdown \\| { id: string \\| number, element: HSDropdown } \\| null` | Returns the dropdown instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDropdown` instance. If `isInstance` is `false` or omitted, returns the `HSDropdown` instance directly. Returns `null` if dropdown instance is not found. |\n| `HSDropdown.open(target, openedViaKeyboard)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector)<br>`openedViaKeyboard`: `boolean` (optional) | `void` | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSDropdown.close(target)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new dropdown instance\nconst dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown programmatically\nopenBtn.addEventListener('click', () => {\n  dropdown.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSDropdown.open('#hs-dropdown');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSDropdown instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Clean up when removing from DOM\n  function removeDropdown() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying dropdown instance**\n```javascript\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-dropdown').remove();\n  });\n}\n```\n\n## Events\n\nDropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After dropdown menu is opened | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |\n| `on:close` | After dropdown menu is closed | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (dropdownInstance) => {\n    console.log('Dropdown opened:', dropdownInstance);\n    // Perform actions after dropdown opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (dropdownInstance) => {\n    console.log('Dropdown closed:', dropdownInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Trigger\n\nShow dropdown on hover instead of click.\n\n```html\n<div class=\"hs-dropdown --trigger:hover\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl dropdown from external buttons.\n\n```html\n<div class=\"hs-dropdown\" id=\"hs-dropdown-first\">\n  <button class=\"hs-dropdown-toggle\">Actions</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n\n<button id=\"hs-open-dropdown\">Open Dropdown</button>\n<button id=\"hs-close-dropdown\">Close Dropdown</button>\n\n<script>\n  const instance = HSDropdown.getInstance('#hs-dropdown-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-dropdown').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-dropdown').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/file-upload/README.md":"# File Upload\n\nTailwind CSS File Upload plugin using Dropzone library to enable drag-and-drop functionality, previews, and progress bars.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/file-upload) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/file-upload.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe File Upload component provides a drag-and-drop file upload interface with preview functionality, progress tracking, and integration with Dropzone.js. It supports multiple file uploads, file type validation, and customizable preview templates.\n\n**Key Features:**\n- Drag-and-drop file upload\n- File previews with thumbnails\n- Upload progress tracking\n- Multiple file support\n- File type and extension validation\n- Integration with Dropzone.js\n- Programmatic control via JavaScript API\n- Event system for upload tracking\n\n## Installation\n\nTo get started, install File Upload plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i lodash dropzone @preline/file-upload\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/file-upload */\n@source \"../node_modules/@preline/file-upload/*.js\";\n@import \"./node_modules/@preline/file-upload/variants.css\";\n@import \"./node_modules/@preline/file-upload/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script src=\"./node_modules/@preline/file-upload/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script type=\"module\">\n  import HSFileUpload from \"@preline/file-upload/non-auto.mjs\";\n\n  new HSFileUpload(document.querySelector(\"#file-upload\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `Dropzone` and `_` (Lodash) are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/file-upload` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport \"@preline/file-upload\";\n```\n\n`@preline/file-upload/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport HSFileUpload from \"@preline/file-upload/non-auto\";\n\nHSFileUpload.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#file-upload\");\nif (el) new HSFileUpload(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a file upload component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component supports drag-and-drop and file previews.\n\n```html\n<div id=\"hs-file-upload-first\" data-hs-file-upload='{\n  \"url\": \"/upload\"\n}'>\n  <template data-hs-file-upload-preview>\n    <button type=\"button\" data-hs-file-upload-remove>\n      Delete!\n    </button>\n    <span data-hs-file-upload-file-icon>\n      <img class=\"hidden\" data-dz-thumbnail />\n    </span>\n    <div>\n      <span data-hs-file-upload-file-name></span>.<span data-hs-file-upload-file-ext></span>\n    </div>\n    <div data-hs-file-upload-file-size></div>\n    <div role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" data-hs-file-upload-progress-bar>\n      <div style=\"width: 0\" data-hs-file-upload-progress-bar-pane></div>\n    </div>\n    <span data-hs-file-upload-progress-bar-value>0</span>%\n  </template>\n\n  <div class=\"cursor-pointer\" data-hs-file-upload-trigger>\n    Drop your file here or browse\n  </div>\n\n  <div data-hs-file-upload-previews></div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-file-upload`: Required on the container element, contains configuration options\n- `data-hs-file-upload-preview`: Required template element for file previews (should be a `<template>` element)\n- `data-hs-file-upload-trigger`: Required clickable element for file selection\n- `data-hs-file-upload-previews`: Required container for file preview items\n- Various data attributes for preview elements (file name, extension, size, progress, etc.)\n\n**Initial State:**\n- Upload trigger is visible\n- Previews container is empty\n- Template is hidden (it's a template element)\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-file-upload` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-file-upload` | Container | - | - | Activate a File Upload by specifying on an element. Should be added to the container. |\n| `:url` | Inside `data-hs-file-upload` | string | - | The URL where files will be uploaded. Required for file upload functionality. |\n| `:extensions` | Inside `data-hs-file-upload` | object | Includes markup. See the code. | A list of extensions and associated icons that will be added inside the container with the `data-hs-file-upload-file-icon` selector if they match the extension name. |\n| `:autoHideTrigger` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then `data-hs-file-upload-trigger` will disappear if at least one item is added to the upload and will appear if more than one item is not in the upload. |\n| `:singleton` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then the plugin will remove any previously uploaded file. It needs for emulate simple input file behavior (only one file at a time). |\n\n**Example:**\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/api/upload\",\n  \"autoHideTrigger\": true,\n  \"singleton\": false\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the file upload to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-trigger` | Clickable element | Determines which element inside the initialized element will act as a clickable element for file selection. |\n| `data-hs-file-upload-previews` | Container element | Determines which element inside the initialized element will act as a wrapper for uploaded items. |\n| `data-hs-file-upload-preview` | Template element | Determines which element inside the initialized element will act as a template for the uploading item. Should be a `template` HTML element. |\n\n### Optional CSS Classes / Data Attributes\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-clear` | Button element | Determines which element inside the initialized element will act as a button that deletes all files. |\n| `data-hs-file-upload-file-icon` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file icon defined in the `extensions` property. |\n| `data-hs-file-upload-file-name` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file name. |\n| `data-hs-file-upload-file-ext` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file extension. |\n| `data-hs-file-upload-file-size` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file size. |\n| `data-hs-file-upload-remove` | Button element inside preview | Determines which element inside the uploading item will act as a remove button. |\n| `data-hs-file-upload-reload` | Button element inside preview | Determines which element inside the uploading item will act as a re-upload button. |\n| `data-hs-file-upload-progress-bar` | Element inside preview | Determines which element inside the uploading item will act as a progressbar. |\n| `data-hs-file-upload-progress-bar-pane` | Element inside preview | Determines which element inside the uploading item will act as a progressbar pane, which width will dynamically increase during the uploading process. |\n| `data-hs-file-upload-progress-bar-value` | Element inside preview | Determines which element inside the uploading item will act as a current progress value, which will dynamically increase during the uploading process. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-file-upload-complete:*` | A modifier that allows you to set Tailwind classes to items that successfully uploaded. |\n\n## JavaScript API\n\nThe `HSFileUpload` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a file upload instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the file upload instance, removes all generated markup, classes, and event listeners. Use when removing file upload from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSFileUpload` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSFileUpload.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSFileUpload } \\| null` | Returns the file upload instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSFileUpload` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if file upload instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Accessing Dropzone instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying Dropzone instance\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Use Dropzone methods\n    dropzone.removeAllFiles();\n  }\n}\n```\n\n**Example 3: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-file-upload').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most events available in the plugin are available in our wrapper.\n\nFile upload instances emit events through the underlying Dropzone instance. Access the Dropzone instance via `element.dropzone` to listen to events.\n\n**Common Dropzone Events:**\n- `addedfile` - When a file is added\n- `removedfile` - When a file is removed\n- `uploadprogress` - During file upload\n- `success` - When file upload succeeds\n- `error` - When file upload fails\n- `complete` - When upload completes (success or error)\n\n### Event Usage Example\n\n```javascript\n// Get file upload instance\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Listen to complete event\n    dropzone.on('complete', (file) => {\n      console.log('File uploaded:', file);\n      // Perform actions after file upload completes\n      // e.g., update UI, show success message\n    });\n\n    // Listen to error event\n    dropzone.on('error', (file, errorMessage) => {\n      console.log('Upload error:', errorMessage);\n      // Handle upload errors\n    });\n\n    // Listen to success event\n    dropzone.on('success', (file, response) => {\n      console.log('Upload successful:', response);\n      // Handle successful upload\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Single File Upload\n\nAllow only one file at a time.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"singleton\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Pattern 2: Auto-hide Trigger\n\nHide trigger after files are added.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"autoHideTrigger\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/input-number/README.md":"# Input Number\n\nInput Number - input idea for Shop systems or so.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/input-number) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/input-number.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Input Number component provides a numeric input field with increment and decrement buttons. It's ideal for quantity selectors, cart systems, or any interface requiring numeric input with step controls.\n\n**Key Features:**\n- Numeric input with step controls\n- Increment/decrement buttons\n- Min/max value constraints\n- Custom step values\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Input Number plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/input-number\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/input-number */\n@source \"../node_modules/@preline/input-number/*.js\";\n@import \"./node_modules/@preline/input-number/variants.css\";\n@import \"./node_modules/@preline/input-number/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/input-number/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSInputNumber from \"@preline/input-number/non-auto.mjs\";\n\n  new HSInputNumber(document.querySelector(\"#input-number\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/input-number` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/input-number\";\n```\n\n`@preline/input-number/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSInputNumber from \"@preline/input-number/non-auto\";\n\nHSInputNumber.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#input-number\");\nif (el) new HSInputNumber(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an input number component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component includes an input field and increment/decrement buttons.\n\n```html\n<div data-hs-input-number>\n  <input type=\"text\" aria-roledescription=\"Number field\" value=\"1\" data-hs-input-number-input>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Decrease\" data-hs-input-number-decrement>\n    Minus\n  </button>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Increase\" data-hs-input-number-increment>\n    Add\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-input-number`: Required on the container element\n- `data-hs-input-number-input`: Required on the input element\n- `data-hs-input-number-increment`: Required on the increment button\n- `data-hs-input-number-decrement`: Required on the decrement button\n- Proper ARIA attributes (`aria-roledescription`, `aria-label`)\n\n**Initial State:**\n- Input field contains the initial value (e.g., `value=\"1\"`)\n- Buttons are enabled/disabled based on min/max constraints\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-input-number` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-input-number` | Container | - | - | Activate an Input Number by specifying on an element. Should be added to the container. |\n| `:min` | Inside `data-hs-input-number` | number \\| `\"-Infinity\"` | `0` | Defines the minimum possible value. `-Infinity` allows you to enter negative values without restrictions. |\n| `:max` | Inside `data-hs-input-number` | number \\| null | `null` | Defines the maximum possible value. `null` means no maximum limit. |\n| `:step` | Inside `data-hs-input-number` | number | `1` | Determines the step by which the value will increase or decrease when buttons are clicked. |\n| `:forceBlankValue` | Inside `data-hs-input-number` | boolean | `false` | Whether the input value should be blank when the value isn't set. When `true`, empty input shows blank instead of default value. |\n\n**Example:**\n```html\n<div data-hs-input-number='{\n  \"min\": 0,\n  \"max\": 100,\n  \"step\": 5,\n  \"forceBlankValue\": false\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the input number to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-input-number-input` | Input element | Identifies the numeric input field |\n| `data-hs-input-number-increment` | Button element | Identifies the increment button |\n| `data-hs-input-number-decrement` | Button element | Identifies the decrement button |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-input-number-disabled:*` | A modifier that allows you to set Tailwind classes when input's value is set to zero (or minimum value). |\n\n## JavaScript API\n\nThe `HSInputNumber` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an input number instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the input number instance, removes all generated markup, classes, and event listeners. Use when removing input number from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSInputNumber` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSInputNumber.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSInputNumber \\| { id: number, element: HSInputNumber } \\| null` | Returns the input number instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSInputNumber` instance. If `isInstance` is `false` or omitted, returns the `HSInputNumber` instance directly. Returns `null` if input number instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.inputValue);\n  console.log('Min value:', element.minInputValue);\n  console.log('Max value:', element.maxInputValue);\n  console.log('Step:', element.step);\n\n  // Clean up when removing from DOM\n  function removeInputNumber() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-input-number').remove();\n  });\n}\n```\n\n## Events\n\nInput number instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When input value was changed | `{ inputValue: number }` | Fires when the input value changes (via buttons or direct input). Returns an object with the `inputValue` containing the current numeric value. |\n\n### Event Usage Example\n\n```javascript\n// Get input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ inputValue }) => {\n    console.log('Input value changed:', inputValue);\n    // Perform actions after value changes\n    // e.g., update cart, calculate totals, validate\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Quantity Selector\n\nCreate a quantity selector for shopping carts.\n\n```html\n<div data-hs-input-number='{\n  \"min\": 1,\n  \"max\": 99,\n  \"step\": 1\n}'>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n```\n\n### Pattern 2: Negative Values Allowed\n\nAllow negative values by setting min to -Infinity.\n\n```html\n<div data-hs-input-number='{\n  \"min\": \"-Infinity\",\n  \"max\": null,\n  \"step\": 1\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Pattern 3: Handling Value Changes\n\nTrack value changes with event listener.\n\n```html\n<div id=\"hs-input-number-first\" data-hs-input-number>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n\n<script>\n  const instance = HSInputNumber.getInstance('#hs-input-number-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('change', ({ inputValue }) => {\n      console.log('Quantity:', inputValue);\n      // Update cart or perform other actions\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/layout-splitter/README.md":"# Layout Splitter\n\nCustomize your layout effortlessly by adjusting block sizes to your preference with Tailwind CSS Layout Splitters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/layout-splitter) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/layout-splitter.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Layout Splitter component provides resizable layout panels with drag handles. It allows users to adjust the size of layout sections horizontally or vertically, creating flexible and customizable interfaces.\n\n**Key Features:**\n- Horizontal and vertical resizing\n- Drag handles for intuitive resizing\n- Minimum size constraints\n- Pre-limit size detection\n- Responsive breakpoint support\n- Programmatic control via JavaScript API\n- Event system for drag tracking\n\n## Installation\n\nTo get started, install Layout Splitter plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/layout-splitter\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/layout-splitter */\n@source \"../node_modules/@preline/layout-splitter/*.js\";\n@import \"./node_modules/@preline/layout-splitter/variants.css\";\n@import \"./node_modules/@preline/layout-splitter/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/layout-splitter/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSLayoutSplitter from \"@preline/layout-splitter/non-auto.mjs\";\n\n  new HSLayoutSplitter(document.querySelector(\"#layout-splitter\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/layout-splitter` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/layout-splitter\";\n```\n\n`@preline/layout-splitter/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSLayoutSplitter from \"@preline/layout-splitter/non-auto\";\n\nHSLayoutSplitter.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#layout-splitter\");\nif (el) new HSLayoutSplitter(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a layout splitter component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The layout allows resizing blocks by dragging the splitter handle.\n\n```html\n<div data-hs-layout-splitter='{\n  \"horizontalSplitterTemplate\": \"<div><span class=\\\"absolute top-1/2 start-1/2 -translate-x-2/4 -translate-y-2/4 block w-[11px] h-5 bg-gray-400\\\"></span></div>\",\n  \"horizontalSplitterClasses\": \"relative flex border-s border-gray-200\"\n}'>\n  <div class=\"flex h-50\" data-hs-layout-splitter-horizontal-group>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"48.2\">Horizontal Block 1</div>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"30.0\">Horizontal Block 2</div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-layout-splitter`: Required on the container element, contains configuration options as JSON\n- `data-hs-layout-splitter-horizontal-group`: Required for horizontal splitting\n- `data-hs-layout-splitter-vertical-group`: Required for vertical splitting\n- `data-hs-layout-splitter-item`: Required on resizable blocks, accepts float number or object for initial size\n\n**Initial State:**\n- Blocks are sized according to `data-hs-layout-splitter-item` values\n- Splitter handles are visible and draggable\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-layout-splitter` attribute and on splitter items.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-layout-splitter` | Container | - | - | Activate a Layout Splitter by specifying on an element. Should be added to the container. |\n| `:horizontalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the horizontal splitter template. HTML markup for the drag handle. |\n| `:horizontalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the horizontal splitter CSS classes. CSS classes must be separated by a space. |\n| `:verticalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the vertical splitter template. HTML markup for the drag handle. |\n| `:verticalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the vertical splitter CSS classes. CSS classes must be separated by a space. |\n| `data-hs-layout-splitter-horizontal-group` | Container element | - | - | Defines a group of elements with horizontal resizable blocks. |\n| `data-hs-layout-splitter-vertical-group` | Container element | - | - | Defines a group of elements with vertical resizable blocks. |\n| `data-hs-layout-splitter-item` | Resizable block | float \\| object | - | Defines a block that can be resized depending on what group it is in. Also accepts a float number as a value to specify the initial width or height of the element. Can be an object with `dynamicSize`, `minSize`, `preLimitSize` properties. |\n| `:dynamicSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the initialized block size. |\n| `:minSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the size at which further narrowing of the block will be prevented. |\n| `:preLimitSize` | Inside `data-hs-layout-splitter-item` | float | - | Determines the size at which the `onPrevPreLimit` event will be called. For example, on this event you can add specific classes to the block to the right or left of the divider. |\n\n**Example:**\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0,\n  \"preLimitSize\": 20.0\n}'>\n  Resizable Block\n</div>\n```\n\n## JavaScript API\n\nThe `HSLayoutSplitter` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a layout splitter instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getSplitterItemSingleParam(item, name)` | `item`: `HTMLElement` (splitter item element)<br>`name`: `string` (parameter name) | `any` | Retrieves the value of a specific parameter (name) from the data attribute of a splitter item (item). The parameter is extracted from the parsed JSON object stored in the `data-hs-layout-splitter-item` attribute. |\n| `getData(el)` | `el`: `HTMLElement` (splitter item element) | `object` | Retrieves detailed information about a splitter item, including its current size, minimum size, and pre-limit size, both in pixels and flex values. Returns a structured object. |\n| `setSplitterItemSize(el, size)` | `el`: `HTMLElement` (splitter item element)<br>`size`: `number` (percentage) | `void` | Updates the size of a specified splitter item (`el`) to the given value (size). The new size is converted into a flex value and applied to the element. The method also updates the corresponding data attribute (`data-hs-layout-splitter-item`). |\n| `updateFlexValues(data)` | `data`: `Array<{ id: string, breakpoints: Record<number, number> }>` | `void` | Updates the flex values of splitter items based on the provided configuration. Each configuration object specifies an element's ID and its size values for different screen widths (breakpoints). The method dynamically adjusts the flex values of the specified elements to match the current screen width. |\n| `destroy()` | None | `void` | Cleans up and removes all event listeners, styles, and data attributes associated with the layout splitter. This method restores the affected elements to their original state and releases resources. |\n\n### Static Methods\n\nThese methods are called directly on the `HSLayoutSplitter` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSLayoutSplitter.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSLayoutSplitter } \\| null` | Returns the layout splitter instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSLayoutSplitter` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if layout splitter instance is not found. |\n| `HSLayoutSplitter.autoInit()` | None | `void` | Reinitializes all Layout Splitters on the page. Useful after dynamically adding layout splitters to DOM. |\n\n### Usage Examples\n\n**Example 1: Getting splitter item data**\n```javascript\n// Get the layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n  // Get item data\n  const data = element.getData(item);\n\n  console.log('Item data:', data);\n}\n```\n\n**Example 2: Setting splitter item size**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n\n  // Set item size to 50%\n  element.setSplitterItemSize(item, 50);\n}\n```\n\n**Example 3: Responsive breakpoints**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  // Update flex values based on window size\n  const updateLayoutSplitter = (splitter) => {\n    splitter.updateFlexValues([\n      {\n        id: \"hs-section-first\",\n        breakpoints: {\n          \"0\": 0,\n          \"1280\": 15.0\n        }\n      },\n      {\n        id: \"hs-section-second\",\n        breakpoints: {\n          \"0\": 0,\n          \"1024\": 25.4,\n          \"1280\": 21.0\n        }\n      },\n      {\n        id: \"hs-section-third\",\n        breakpoints: {\n          \"0\": 100,\n          \"1024\": 74.6,\n          \"1280\": 64.0\n        }\n      }\n    ]);\n  };\n\n  window.addEventListener('resize', () => {\n    if (instance) updateLayoutSplitter(instance.element);\n  });\n}\n```\n\n**Example 4: Destroying layout splitter instance**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-layout-splitter').remove();\n  });\n}\n```\n\n## Events\n\nLayout splitter instances emit events that can be listened to for drag tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:drag` | When the splitter is dragged | `{ prev: HTMLElement, next: HTMLElement, previousSize: number, previousFlexSize: number, nextSize: number, nextFlexSize: number }` | Fires when the splitter is being dragged. Returns an object with previous and next elements and their sizes. |\n| `on:onPrevLimit` | When the previous section size limit is reached | `HTMLElement` (previous element) | Fires when the previous section reaches its minimum size limit. |\n| `on:onPrevPreLimit` | When the previous section size is less than `preLimitSize` | `HTMLElement` (previous element) | Fires when the previous section size is less than the `preLimitSize` parameter value. |\n| `on:onNextLimit` | When the next section size limit is reached | `HTMLElement` (next element) | Fires when the next section reaches its minimum size limit. |\n| `on:onNextPreLimit` | When the next section size is less than `preLimitSize` | `HTMLElement` (next element) | Fires when the next section size is less than the `preLimitSize` parameter value. |\n\n### Event Usage Example\n\n```javascript\n// Get layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to limit events\n  element.on('onPrevLimit', (prev) => {\n    prev.classList.add('hs-prev-limit-reached');\n  });\n\n  element.on('onPrevPreLimit', (prev) => {\n    prev.classList.add('hs-prev-pre-limit-reached');\n  });\n\n  // Listen to drag event\n  element.on('drag', ({ prev, previousFlexSize }) => {\n    if (previousFlexSize > 10) {\n      prev.classList.remove('hs-prev-limit-reached');\n    }\n\n    if (previousFlexSize > 20) {\n      prev.classList.remove('hs-prev-pre-limit-reached');\n    }\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Horizontal Splitter\n\nCreate horizontal resizable panels.\n\n```html\n<div data-hs-layout-splitter>\n  <div data-hs-layout-splitter-horizontal-group>\n    <div data-hs-layout-splitter-item=\"50\">Left Panel</div>\n    <div data-hs-layout-splitter-item=\"50\">Right Panel</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Minimum Size\n\nSet minimum size constraints.\n\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0\n}'>\n  Resizable Panel\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/overlay/README.md":"# Overlay\n\nThe Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/overlay) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/overlay.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Overlay component provides a flexible foundation for creating modals, drawers, sidebars, and other overlay-based UI elements. It handles backdrop management, focus trapping, keyboard navigation, and z-index stacking automatically.\n\n**Key Features:**\n- Modal and drawer support\n- Automatic backdrop management\n- Focus trapping and keyboard navigation (Tab, Esc)\n- Z-index stacking for multiple overlays\n- Responsive breakpoint support\n- Auto-close on screen size changes\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/overlay\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/overlay */\n@source \"../node_modules/@preline/overlay/*.js\";\n@import \"./node_modules/@preline/overlay/variants.css\";\n@import \"./node_modules/@preline/overlay/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/overlay/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSOverlay from \"@preline/overlay/non-auto.mjs\";\n\n  new HSOverlay(document.querySelector(\"#overlay\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/overlay` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/overlay\";\n```\n\n`@preline/overlay/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSOverlay from \"@preline/overlay/non-auto\";\n\nHSOverlay.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#overlay\");\nif (el) new HSOverlay(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an overlay component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The overlay appears when the button is clicked.\n\n```html\n<button type=\"button\" aria-haspopup=\"dialog\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-modal\" data-hs-overlay=\"#hs-unstyled-modal\">\n  Open modal\n</button>\n\n<div id=\"hs-unstyled-modal\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-60 overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"hs-unstyled-modal-label\">\n  <div class=\"hs-overlay-open:opacity-100 hs-overlay-open:duration-500 opacity-0 transition-all sm:max-w-lg sm:w-full m-3 sm:mx-auto\">\n    <div class=\"pointer-events-auto\">\n      <h3 id=\"hs-unstyled-modal-label\">Title</h3>\n      Modal content\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-overlay`: Required on the trigger button, must be a valid CSS selector pointing to the overlay element\n- `hs-overlay`: Required class on the overlay content container\n- `hidden` class on the overlay initially\n- Proper ARIA attributes (`aria-haspopup`, `aria-expanded`, `aria-controls`, `role=\"dialog\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the trigger button initially\n- Add `hidden` class to the overlay initially\n- Overlay visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure overlay behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-overlay-options` | Trigger button | object (JSON) | - | Defines the modal configuration. Should be added to the button (trigger). |\n| `:hiddenClass` | Inside `data-hs-overlay-options` | string | `\"hidden\"` | Defines which classes will be added/removed when modal toggles. |\n| `:emulateScrollbarSpace` | Inside `data-hs-overlay-options` | boolean | `false` | If `true`, then adds right padding to the body equal to the size of the scrollbar. Useful to prevent layout shift when overlay opens. |\n| `:isClosePrev` | Inside `data-hs-overlay-options` | boolean | `true` | Determines whether the previous open modal will be closed when the next one is opened. |\n| `:backdropClasses` | Inside `data-hs-overlay-options` | string | `\"hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80\"` | Defines default backdrop classes. |\n| `:backdropExtraClasses` | Inside `data-hs-overlay-options` | string | - | Allows to add additional classes besides those specified in `backdropClasses`. |\n| `:backdropParent` | Inside `data-hs-overlay-options` | string | - | Allows to specify the selector of the element where the backdrop will be generated. |\n| `:moveOverlayToBody` | Inside `data-hs-overlay-options` | number \\| null | `null` | Moves the overlay to the body tag if the screen width is less than the value specified. |\n| `:isToggleClassesImmediately` | Inside `data-hs-overlay-options` | boolean | `false` | When set to `true`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n| `data-hs-overlay-backdrop-container` | Overlay content | string (CSS selector) | `null` | Backdrop element selector. Should be added to the overlay (content). |\n| `data-hs-overlay-keyboard` | Overlay content | boolean | `true` | When set to `false`, the modal will not close when pressing the ESC button on the keyboard. Should be added to the overlay (content). |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control overlay behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--overlay-backdrop:*]` | Overlay content | `\"static\"` \\| null | `null` | When backdrop is set to `\"static\"`, the modal will not close when clicking outside it. Should be added to the overlay (content). |\n| `[--auto-hide:*]` | Overlay content | number | `0` | Milliseconds for auto-closing a modal. When set to `0`, the modal will not close automatically. Should be added to the overlay (content). |\n| `[--auto-close:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the overlay will close automatically. Should be added to the overlay (content). |\n| `[--auto-close-equality-type:*]` | Overlay content | `'less-than'` \\| null | `null` | This option determines the condition under which an overlay should automatically close based on the window's width. When set to `less-than`, the overlay will close if the window's width is less than or equal to the value defined in `[--auto-close:*]`. Otherwise the overlay will close if the window's width is greater than or equal to the `[--auto-close:*]` threshold. Should be added to the overlay (content). |\n| `[--opened:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the plugin will apply functionality for the initially opened overlay. Should be added to the overlay (content). |\n| `[--body-scroll:*]` | Overlay content | boolean | `true` | When set to `false`, the body scroll will be hidden as soon as the modal opens. Should be added to the overlay (content). |\n| `[--tab-accessibility-limited:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Restricts focus to elements within an overlay (or modal). Should be added to the overlay (content). |\n| `[--has-autofocus:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening an overlay. Should be added to the overlay (content). |\n| `[--has-dynamic-z-index:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, each newly opened element in the group will receive an incremented `z-index` inline, ensuring it appears above previously opened elements with the same base `z-index`. Should be added to the overlay (content). |\n| `[--is-layout-affect:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | Informs the plugin that the instance affects the website layout. Should be added to the overlay (content). |\n| `[--toggle-classes-immediately:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n\n**Example:**\n```html\n<div class=\"hs-overlay --overlay-backdrop:static --auto-hide:5000\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the overlay to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay` | Overlay content container | Identifies the overlay content element |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay-animation-target` | Element inside overlay | Defines an element inside the popup, after the end of whose animation the popup will be completely hidden. Has no parameters. |\n| `autofocus` | Input element inside overlay | Focus the first input in a modal with the `autofocus` attribute on opening. Must be added to an input element inside a modal window. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-overlay-open:*` | Defines classes that will be applied to any elements inside the `hs-overlay` when the overlay is open. |\n| `hs-overlay-backdrop-open:*` | Defines classes that will be applied to backdrop when the overlay is open. |\n| `hs-overlay-layout-open:*` | Defines classes that will be applied to any elements inside the body tag when the overlay is open. |\n\n## JavaScript API\n\nThe `HSOverlay` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an overlay instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(cb)` | `cb`: `Function \\| null` (optional) | `Promise<void>` | Forces the overlay to open programmatically. Optionally accepts a callback function that will be called after opening. Returns a Promise. |\n| `close(forceClose, cb)` | `forceClose`: `boolean` (optional)<br>`cb`: `Function \\| null` (optional) | `Promise<unknown>` | Forces the overlay to close programmatically. `forceClose` bypasses animations and closes immediately. Optionally accepts a callback function. Returns a Promise. |\n| `minify(isMinified, cb)` | `isMinified`: `boolean`<br>`cb`: `Function \\| null` (optional) | `void` | Minimizes or restores the overlay. Useful for drawer/sidebar components. |\n| `updateToggles()` | None | `void` | Updates the toggle buttons. Useful after dynamically adding or removing toggle buttons. |\n| `destroy()` | None | `void` | Destroys the overlay instance, removes all generated markup, classes, and event listeners. Use when removing overlay from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSOverlay` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSOverlay.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSOverlay } \\| null` | Returns the overlay instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSOverlay` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if overlay instance is not found. |\n| `HSOverlay.open(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSOverlay.close(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new overlay instance\nconst modal = new HSOverlay(document.querySelector('#hs-modal'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay programmatically\nopenBtn.addEventListener('click', () => {\n  modal.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSOverlay.open('#hs-modal');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSOverlay instance\n  const openBtn = document.querySelector('#hs-open-btn');\n  const closeBtn = document.querySelector('#hs-close-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  closeBtn.addEventListener('click', () => {\n    element.close();\n  });\n\n  // Clean up when removing from DOM\n  function removeOverlay() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying overlay instance**\n```javascript\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-modal').remove();\n  });\n}\n```\n\n## Events\n\nOverlay instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After overlay is opened | `HSOverlay` (overlay instance) | Fires after the overlay has been displayed. Use for post-open actions like loading content or focusing elements. |\n| `on:close` | After overlay is closed | `HSOverlay` (overlay instance) | Fires after the overlay has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (overlayInstance) => {\n    console.log('Overlay opened:', overlayInstance);\n    // Perform actions after overlay opens\n    // e.g., load content, focus input, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (overlayInstance) => {\n    console.log('Overlay closed:', overlayInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Static Backdrop\n\nPrevent closing when clicking outside the overlay.\n\n```html\n<div class=\"hs-overlay --overlay-backdrop:static\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 2: Auto-close on Screen Size\n\nClose overlay automatically at specific breakpoints.\n\n```html\n<div class=\"hs-overlay --auto-close:md --auto-close-equality-type:less-than\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl overlay from external buttons.\n\n```html\n<button id=\"hs-open-modal\">Open Modal</button>\n<button id=\"hs-close-modal\">Close Modal</button>\n\n<div id=\"hs-modal-first\" class=\"hs-overlay hidden\">\n  <!-- Overlay content -->\n</div>\n\n<script>\n  const instance = HSOverlay.getInstance('#hs-modal-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-modal').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-modal').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/pin-input/README.md":"# PIN Input\n\nA PIN Input plugin that allows to fill in data for inputs.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/pin-input) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/pin-input.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe PIN Input component provides a multi-field input interface for entering PIN codes, verification codes, or similar numeric/alphanumeric sequences. It automatically moves focus between fields and supports paste operations.\n\n**Key Features:**\n- Multiple input fields for PIN entry\n- Automatic focus management\n- Paste support for bulk entry\n- Character validation via regex\n- Programmatic control via JavaScript API\n- Event system for completion tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install PIN Input plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/pin-input\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/pin-input */\n@source \"../node_modules/@preline/pin-input/*.js\";\n@import \"./node_modules/@preline/pin-input/variants.css\";\n@import \"./node_modules/@preline/pin-input/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/pin-input/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSPinInput from \"@preline/pin-input/non-auto.mjs\";\n\n  new HSPinInput(document.querySelector(\"#pin-input\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/pin-input` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/pin-input\";\n```\n\n`@preline/pin-input/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSPinInput from \"@preline/pin-input/non-auto\";\n\nHSPinInput.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#pin-input\");\nif (el) new HSPinInput(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a PIN input component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component consists of multiple input fields that automatically advance focus.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-pin-input`: Required on the container element\n- `data-hs-pin-input-item`: Required on each input field\n- All input fields should be direct children of the container\n\n**Initial State:**\n- Input fields are empty by default\n- Focus starts on the first input field (if `autofocus` class is used)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-pin-input` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-pin-input` | Container | - | - | Activate a Pin Input by specifying on an element. Should be added to the container. |\n| `:availableCharsRE` | Inside `data-hs-pin-input` | RegExp (string) | `\"^[a-zA-Z0-9]+$\"` | Regular expression string for available characters. Only characters matching this regex will be accepted. |\n| `data-hs-pin-input-item` | Input element | - | - | Determines which element inside the initialized container will be responsible for entering data. Should be added to the input. |\n\n**Example:**\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <!-- Input fields -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control PIN input behavior.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `autofocus` | One of the input fields | If one of the fields has this class, it will be focused when the page loads. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-pin-input-active:*` | The class is added to the input when the PIN has been set up (all fields are filled). |\n\n## JavaScript API\n\nThe `HSPinInput` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a PIN input instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the PIN input instance, removes all generated markup, classes, and event listeners. Use when removing PIN input from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSPinInput` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSPinInput.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSPinInput \\| { id: string \\| number, element: HSPinInput } \\| null` | Returns the PIN input instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSPinInput` instance. If `isInstance` is `false` or omitted, returns the `HSPinInput` instance directly. Returns `null` if PIN input instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.currentValue);\n  console.log('Items:', element.items);\n\n  // Clean up when removing from DOM\n  function removePinInput() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-pin-input').remove();\n  });\n}\n```\n\n## Events\n\nPIN input instances emit events that can be listened to for completion tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:completed` | When the PIN has been set up (all fields filled) | `{ currentValue: string }` | Fires when all input fields have been filled. Returns an object with the `currentValue` containing the complete PIN string. |\n\n### Event Usage Example\n\n```javascript\n// Get PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to completed event\n  element.on('completed', ({ currentValue }) => {\n    console.log('PIN completed:', currentValue);\n    // Perform actions after PIN is completed\n    // e.g., validate PIN, submit form, proceed to next step\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Numeric PIN Only\n\nRestrict input to numbers only.\n\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 2: Auto-focus First Field\n\nAutomatically focus the first field on page load.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item class=\"autofocus\">\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 3: Handling Completion\n\nHandle PIN completion with event listener.\n\n```html\n<div id=\"hs-pin-input-first\" data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n\n<script>\n  const instance = HSPinInput.getInstance('#hs-pin-input-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('completed', ({ currentValue }) => {\n      console.log('PIN entered:', currentValue);\n      // Validate or submit the PIN\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/range-slider/README.md":"# Advanced Range Slider\n\nAdvanced Range Slider solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/range-slider) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-range-slider.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Range Slider component provides a flexible range input interface for selecting numeric values within a specified range. It integrates with noUiSlider for advanced functionality including dual handles, custom formatting, and smooth animations.\n\n**Key Features:**\n- Single and dual handle support\n- Custom value formatting\n- Smooth animations and transitions\n- Integration with noUiSlider\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Advanced Range Slider plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i nouislider @preline/range-slider\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/range-slider */\n@source \"../node_modules/@preline/range-slider/*.js\";\n@import \"./node_modules/@preline/range-slider/variants.css\";\n@import \"./node_modules/@preline/range-slider/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script src=\"./node_modules/@preline/range-slider/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script type=\"module\">\n  import HSRangeSlider from \"@preline/range-slider/non-auto.mjs\";\n\n  new HSRangeSlider(document.querySelector(\"#range-slider\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `noUiSlider` is referenced as a global, so expose it on `window` before importing the plugin.\n\n`@preline/range-slider` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport \"@preline/range-slider\";\n```\n\n`@preline/range-slider/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport HSRangeSlider from \"@preline/range-slider/non-auto\";\n\nHSRangeSlider.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#range-slider\");\nif (el) new HSRangeSlider(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced range slider component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The slider allows selecting a value between 0 and 100.\n\n```html\n<div data-hs-range-slider='{\n  \"cssClasses\": {},\n  \"start\": 50,\n  \"connect\": \"lower\",\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n**Structure Requirements:**\n- `data-hs-range-slider`: Required on the container element, contains configuration options as JSON\n- Container element will be automatically populated with slider markup by the plugin\n\n**Initial State:**\n- Slider starts at the value specified in `start` option\n- Range is defined by `min` and `max` in the `range` option\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin.\n\nData options are specified in the `data-hs-range-slider` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-range-slider` | Container element | - | - | Activate a Range Slider by specifying on an element. Should be added to the initialized element. Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin. |\n| `:disabled` | Inside `data-hs-range-slider` | boolean | `false` | Makes the element unavailable for interaction. When `true`, the slider cannot be moved. |\n| `:formatter` | Inside `data-hs-range-slider` | string \\| object | - | Output value formatting options. Can be a string or an object with formatting configuration. |\n| `:formatter:type` | Inside `data-hs-range-slider` | `\"integer\"` \\| `\"thousandsSeparatorAndDecimalPoints\"` \\| null | `null` | Defines an output formatter type. `integer` formats as integer, `thousandsSeparatorAndDecimalPoints` adds thousands separators and decimal points. |\n| `:formatter:prefix` | Inside `data-hs-range-slider` | string | - | Adds a prefix to the output value (e.g., \"$\" for currency). |\n| `:formatter:postfix` | Inside `data-hs-range-slider` | string | - | Adds a postfix to the output value (e.g., \"px\" for pixels). |\n\n**Example:**\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  },\n  \"disabled\": false\n}'></div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-range-slider-disabled:*` | A modifier that allows you to set Tailwind classes when slider has \"disabled\" attribute set to \"true\". |\n\n## JavaScript API\n\nThe `HSRangeSlider` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a range slider instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `formattedValue()` | None | `any` | Returns the current values formatted using the `formatter` option. Returns formatted values as specified in the formatter configuration. |\n| `destroy()` | None | `void` | Destroys the range slider instance, removes all generated markup, classes, and event listeners. Use when removing slider from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRangeSlider` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRangeSlider.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRangeSlider } \\| null` | Returns the range slider instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRangeSlider` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if range slider instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting formatted value**\n```javascript\n// Get the range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get formatted value\n  const formatted = element.formattedValue();\n\n  console.log('Formatted value:', formatted);\n}\n```\n\n**Example 2: Accessing noUiSlider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying noUiSlider instance\n  const noUiSlider = element.el.noUiSlider;\n\n  if (noUiSlider) {\n    // Use noUiSlider methods\n    const currentValue = noUiSlider.get();\n\n    console.log('Current value:', currentValue);\n  }\n}\n```\n\n**Example 3: Destroying range slider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-range-slider').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Note that all [events](https://refreshless.com/nouislider/events-callbacks/) available in the [noUiSlider](https://refreshless.com/nouislider/) plugin are also available in our plugin, just use `el.noUiSlider` to handle events.\n\nRange slider instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | When slider value is updated | `[integer, integer?]` | Fires when the slider value changes. Returns an array with current values. For single handle, array contains one value. For dual handles, array contains two values. |\n\n### Event Usage Example\n\n```javascript\n// Get range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to update event via noUiSlider\n  if (element.el.noUiSlider) {\n    element.el.noUiSlider.on('update', (values) => {\n      console.log('Slider updated:', values);\n      console.log('Formatted value:', element.formattedValue());\n      // Perform actions after slider value changes\n      // e.g., update UI, send to server, filter data\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dual Handle Range\n\nCreate a range slider with two handles for selecting a range.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n### Pattern 2: Formatted Values\n\nAdd prefix and postfix to slider values.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": 50,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  }\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/remove-element/README.md":"# Remove Element\n\nDismiss any content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/remove-element) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/remove-element.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Remove Element component provides a simple way to remove or dismiss elements from the DOM with optional animation. It's commonly used for dismissible alerts, notifications, modals, or any content that should be removable.\n\n**Key Features:**\n- One-click element removal\n- Optional removal animation\n- Programmatic control via JavaScript API\n- Simple configuration\n- Event system support\n\n## Installation\n\nTo get started, install Remove Element plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/remove-element\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/remove-element */\n@source \"../node_modules/@preline/remove-element/*.js\";\n@import \"./node_modules/@preline/remove-element/variants.css\";\n@import \"./node_modules/@preline/remove-element/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/remove-element/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSRemoveElement from \"@preline/remove-element/non-auto.mjs\";\n\n  new HSRemoveElement(document.querySelector(\"#remove-element\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/remove-element` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/remove-element\";\n```\n\n`@preline/remove-element/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSRemoveElement from \"@preline/remove-element/non-auto\";\n\nHSRemoveElement.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#remove-element\");\nif (el) new HSRemoveElement(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a remove element component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will remove the target element.\n\n```html\n<div id=\"hs-dismiss-unstyled-first\" class=\"inline-flex justify-between items-center w-full\">\n  <h3>\n    Remove element card\n  </h3>\n  <button type=\"button\" class=\"inline-flex justify-center items-center size-4\" data-hs-remove-element=\"#hs-dismiss-unstyled-first\">\n    <span class=\"sr-only\">Close</span>\n    <svg class=\"shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-remove-element`: Required on the trigger button, must be a valid CSS selector pointing to the element to remove\n- Target element must exist in the DOM\n- Button can be inside or outside the target element\n\n**Initial State:**\n- Target element is visible\n- Clicking the button removes the element\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-remove-element` | Trigger button | string (CSS selector) | - | The element to be removed. This must be a valid CSS selector. Should be added to the button (trigger). Required. |\n| `data-hs-remove-element-options` | Trigger button | object (JSON) | - | Options that can be added via the data attribute. Should be added to the button (trigger). |\n| `:removeTargetAnimationClass` | Inside `data-hs-remove-element-options` | string | `\"hs-removing\"` | Some valid CSS class that will be applied to the target element before removal. Useful for animations. |\n\n**Example:**\n```html\n<button data-hs-remove-element=\"#hs-alert\" data-hs-remove-element-options='{\n  \"removeTargetAnimationClass\": \"fade-out\"\n}'>\n  Close\n</button>\n```\n\n## JavaScript API\n\nThe `HSRemoveElement` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a remove element instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the remove element instance, removes all generated markup, classes, and event listeners. Use when removing remove element from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRemoveElement` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRemoveElement.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRemoveElement } \\| null` | Returns the remove element instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRemoveElement` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if remove element instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the remove element instance\nconst instance = HSRemoveElement.getInstance('[data-hs-remove-element]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Target element:', element.removeTarget);\n  console.log('Animation class:', element.removeTargetAnimationClass);\n\n  // Clean up when removing from DOM\n  function removeRemoveElement() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-remove-element').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dismissible Alert\n\nCreate a dismissible alert notification.\n\n```html\n<div id=\"hs-alert-first\" class=\"alert\">\n  <p>This is an alert message.</p>\n  <button data-hs-remove-element=\"#hs-alert-first\">×</button>\n</div>\n```\n\n### Pattern 2: With Animation\n\nAdd removal animation using CSS classes.\n\n```html\n<div id=\"hs-card-first\" class=\"card\">\n  <h3>Card Title</h3>\n  <button data-hs-remove-element=\"#hs-card-first\" data-hs-remove-element-options='{\n    \"removeTargetAnimationClass\": \"fade-out\"\n  }'>\n    Remove\n  </button>\n</div>\n\n<style>\n  .fade-out {\n    opacity: 0;\n    transition: opacity 0.3s;\n  }\n</style>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/scroll-nav/README.md":"# Scroll Nav\n\nScroll Nav is a versatile and lightweight JavaScript plugin designed to enhance navigation menus with smooth scrolling capabilities.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scroll-nav) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scroll-nav.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scroll Nav component provides horizontal scrolling navigation with previous/next buttons and automatic centering. It's ideal for navigation menus with many items that need to be scrolled horizontally.\n\n**Key Features:**\n- Horizontal scrolling navigation\n- Previous/next navigation buttons\n- Automatic centering of active items\n- Programmatic control via JavaScript API\n- Smooth scrolling animations\n- Current state tracking\n\n## Installation\n\nTo get started, install Scroll Nav plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scroll-nav\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scroll-nav */\n@source \"../node_modules/@preline/scroll-nav/*.js\";\n@import \"./node_modules/@preline/scroll-nav/variants.css\";\n@import \"./node_modules/@preline/scroll-nav/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scroll-nav/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollNav from \"@preline/scroll-nav/non-auto.mjs\";\n\n  new HSScrollNav(document.querySelector(\"#scroll-nav\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scroll-nav` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scroll-nav\";\n```\n\n`@preline/scroll-nav/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollNav from \"@preline/scroll-nav/non-auto\";\n\nHSScrollNav.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scroll-nav\");\nif (el) new HSScrollNav(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scroll nav component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation items scroll horizontally with previous/next buttons.\n\n```html\n<div data-hs-scroll-nav class=\"relative px-6\">\n  <button type=\"button\" class=\"hs-scroll-nav-prev\">Previous</button>\n\n  <nav class=\"hs-scroll-nav-body flex flex-nowrap overflow-x-auto snap-x snap-mandatory\">\n    <a class=\"snap-start\" href=\"#\">Electronics</a>\n    <a class=\"snap-start\" href=\"#\">Clothing</a>\n    <a class=\"snap-start\" href=\"#\">House & Garden</a>\n    <a class=\"snap-start\" href=\"#\">Baby Care</a>\n    <a class=\"snap-start\" href=\"#\">Home appliances</a>\n    <a class=\"snap-start\" href=\"#\">Sports & Fitness</a>\n    <a class=\"snap-start\" href=\"#\">Constraction & Renovation</a>\n    <a class=\"snap-start\" href=\"#\">Food</a>\n    <a class=\"snap-start\" href=\"#\">Pharmacy</a>\n    <a class=\"snap-start\" href=\"#\">Pet Products</a>\n    <a class=\"snap-start\" href=\"#\">Books</a>\n    <a class=\"snap-start\" href=\"#\">Tourism, fishing, hunting</a>\n    <a class=\"snap-start\" href=\"#\">Furniture</a>\n    <a class=\"snap-start\" href=\"#\">Jewelery</a>\n    <a class=\"snap-start\" href=\"#\">Accessories</a>\n    <a class=\"snap-start\" href=\"#\">Games & Consoles</a>\n    <a class=\"snap-start\" href=\"#\">Stationery</a>\n    <a class=\"snap-start\" href=\"#\">Digital Goods</a>\n    <a class=\"snap-start\" href=\"#\">Music</a>\n  </nav>\n\n  <button type=\"button\" class=\"hs-scroll-nav-next\">Next</button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scroll-nav`: Required on the container element\n- `hs-scroll-nav-prev`: Required class on the previous button\n- `hs-scroll-nav-next`: Required class on the next button\n- `hs-scroll-nav-body`: Required class on the scrollable navigation container\n- Navigation items inside the scrollable container\n\n**Initial State:**\n- Navigation items are visible\n- Previous/next buttons are enabled/disabled based on scroll position\n- Active item can be centered automatically if `autoCentering` is enabled\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-scroll-nav` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scroll-nav` | Container | - | - | Activate a Scroll Nav by specifying on an element. Should be added to the container. |\n| `:autoCentering` | Inside `data-hs-scroll-nav` | boolean | `false` | Defines whether the Scroll Nav should align with the active item when the window loads. When `true`, the active item is automatically centered on page load. |\n\n**Example:**\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the scroll nav to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-scroll-nav-prev` | Previous button | Identifies the previous navigation button |\n| `hs-scroll-nav-next` | Next button | Identifies the next navigation button |\n| `hs-scroll-nav-body` | Scrollable container | Identifies the scrollable navigation container |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scroll-nav-active:*` | The class is applied to the navigation item to style the currently active element. |\n| `hs-scroll-nav-disabled:*` | The class is added to the prev/next buttons for style the disabled state. |\n\n## JavaScript API\n\nThe `HSScrollNav` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scroll nav instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `IScrollNavCurrentState` | Retrieves the currently visible first, last, and center elements within the scrollable navigation container. Returns an object with `first`, `last`, and `center` properties. |\n| `goTo(el, cb)` | `el`: `Element` (target element)<br>`cb`: `Function` (optional callback) | `void` | Smoothly scrolls the navigation container to bring the specified element into view. The callback function is executed after scrolling completes. |\n| `centerElement(el, behavior)` | `el`: `HTMLElement` (target element)<br>`behavior`: `ScrollBehavior` (optional, `\"auto\"` \\| `\"smooth\"`) | `void` | Scrolls the navigation container to position the specified element at the center of the visible area. Default behavior is `\"smooth\"`. |\n| `destroy()` | None | `void` | Destroys the scroll nav instance, removes all generated markup, classes, and event listeners. Use when removing scroll nav from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollNav` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollNav.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollNav } \\| null` | Returns the scroll nav instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollNav` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scroll nav instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the scroll nav instance\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('First visible:', state.first);\n  console.log('Last visible:', state.last);\n  console.log('Center visible:', state.center);\n}\n```\n\n**Example 2: Scrolling to an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Scroll to element\n  element.goTo(targetLink, () => {\n    console.log('Scrolled to element!');\n    // Perform actions after scrolling\n  });\n}\n```\n\n**Example 3: Centering an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Center element\n  element.centerElement(targetLink, 'smooth');\n}\n```\n\n**Example 4: Destroying scroll nav instance**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-scroll-nav').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Auto-centering Active Item\n\nAutomatically center the active item on page load.\n\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Pattern 2: Programmatic Navigation\n\nControl navigation programmatically.\n\n```html\n<div id=\"hs-scroll-nav-first\" data-hs-scroll-nav>\n  <!-- Navigation structure -->\n</div>\n\n<script>\n  const instance = HSScrollNav.getInstance('#hs-scroll-nav-first', true);\n  if (instance) {\n    const { element } = instance;\n    // Scroll to specific item\n    const item = document.querySelector('#hs-item-electronics');\n    \n    element.goTo(item);\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/scrollspy/README.md":"# Scrollspy\n\nAutomatically update navigation or list group components based on scroll position to indicate which link is currently active in the viewport.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scrollspy) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scrollspy.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scrollspy component automatically highlights navigation links based on the scroll position of the page. It tracks which section is currently visible in the viewport and updates the corresponding navigation item accordingly.\n\n**Key Features:**\n- Automatic navigation highlighting based on scroll position\n- Customizable scroll offset\n- Support for custom scrollable containers\n- Programmatic control via JavaScript API\n- Event system for scroll tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install Scrollspy plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scrollspy\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scrollspy */\n@source \"../node_modules/@preline/scrollspy/*.js\";\n@import \"./node_modules/@preline/scrollspy/variants.css\";\n@import \"./node_modules/@preline/scrollspy/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scrollspy/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollspy from \"@preline/scrollspy/non-auto.mjs\";\n\n  new HSScrollspy(document.querySelector(\"#scrollspy\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scrollspy` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scrollspy\";\n```\n\n`@preline/scrollspy/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollspy from \"@preline/scrollspy/non-auto\";\n\nHSScrollspy.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scrollspy\");\nif (el) new HSScrollspy(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scrollspy component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation links are automatically highlighted as you scroll through the sections.\n\n```html\n<div id=\"hs-scrollspy-scrollable-parent-first\" class=\"max-h-96 overflow-y-auto\">\n  <div data-hs-scrollspy=\"#hs-scrollspy-first\" class=\"flex flex-wrap gap-3\">\n    <a class=\"active\" href=\"#hs-first\">First</a>\n    <a href=\"#hs-second\">Second</a>\n    <a href=\"#hs-third\">Third</a>\n    <a href=\"#hs-fourth\">Fourth</a>\n    <a href=\"#hs-fifth\">Fifth</a>\n  </div>\n\n  <div id=\"hs-scrollspy-first\">\n    <div id=\"hs-first\" class=\"h-48\">\n      First\n    </div>\n    <div id=\"hs-second\" class=\"h-48\">\n      Second\n    </div>\n    <div id=\"hs-third\" class=\"h-48\">\n      Third\n    </div>\n    <div id=\"hs-fourth\" class=\"h-48\">\n      Fourth\n    </div>\n    <div id=\"hs-fifth\" class=\"h-48\">\n      Fifth\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scrollspy`: Required on the navigation container, must be a valid CSS selector pointing to the sections container\n- Navigation links with `href` attributes pointing to section IDs\n- Sections with matching `id` attributes\n- `active` class on the initially active navigation link\n\n**Initial State:**\n- Add `active` class to the navigation link that corresponds to the initially visible section\n- Ensure sections have sufficient height to enable scrolling\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure scrollspy behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scrollspy` | Navigation container | string (CSS selector) | - | A container containing sections. This must be a valid CSS selector pointing to the sections container. Should be added to the nav that contains Scrollspy links. |\n| `data-hs-scrollspy-scrollable-parent` | Navigation container | string (CSS selector) | `\"window\"` | Specifies the element to be scrolled. This must be a valid CSS selector. Should be added to the nav that contains Scrollspy links. Defaults to `window` if not specified. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control scrollspy behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--scrollspy-offset:*]` | Navigation container | number | `0` | Adds offset when scrolling to the section and to determine if the section is active. Useful for fixed headers or navigation bars. |\n\n**Example:**\n```html\n<div data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scrollspy-active:*` | A modifier that allows you to set Tailwind classes when the section is active for the corresponding link. |\n\n## JavaScript API\n\nThe `HSScrollspy` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scrollspy instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the scrollspy instance, removes all generated markup, classes, and event listeners. Use when removing scrollspy from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollspy` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollspy.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollspy } \\| null` | Returns the scrollspy instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollspy` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scrollspy instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying scrollspy instance**\n```javascript\nconst instance = HSScrollspy.getInstance('#hs-scrollspy', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Scrollspy links:', element.links);\n  console.log('Scrollspy sections:', element.sections);\n\n  // Clean up when removing from DOM\n  function removeScrollspy() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nScrollspy instances emit events that can be listened to for scroll tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeScroll` | Before scrolling begins | `HSScrollspy` (scrollspy instance) | Fires before scrolling to a section begins. Can return a Promise to delay scrolling until async operations complete. Useful for animations or other preparations. |\n\n### Event Usage Example\n\n```javascript\n// Get scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n  const collapse = HSCollapse.getInstance('[data-hs-collapse=\"#hs-navbar-collapse\"]', true);\n\n  // Listen to beforeScroll event\n  element.on('beforeScroll', (scrollspyInstance) => {\n    return new Promise((resolve) => {\n      if (collapse && collapse.element.el.classList.contains('open')) {\n        collapse.element.hide();\n        HSStaticMethods.afterTransition(collapse.element.content, () => resolve(true));\n      } else {\n        resolve(true);\n      }\n    });\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Scrollable Container\n\nUse scrollspy with a custom scrollable container instead of the window.\n\n```html\n<div id=\"hs-scrollable-container\" class=\"max-h-96 overflow-y-auto\">\n  <nav data-hs-scrollspy=\"#hs-sections\" data-hs-scrollspy-scrollable-parent=\"#hs-scrollable-container\">\n    <a href=\"#hs-section-first\">Section 1</a>\n    <a href=\"#hs-section-second\">Section 2</a>\n  </nav>\n  \n  <div id=\"hs-sections\">\n    <div id=\"hs-section-first\">Content 1</div>\n    <div id=\"hs-section-second\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Offset\n\nAdd offset for fixed navigation bars.\n\n```html\n<nav data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</nav>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/select/README.md":"# Advanced Select\n\nAdvanced select solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/select) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-select.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Select component provides a customizable dropdown select interface with support for search, tags, API integration, and extensive styling options. It's designed for handling large datasets and complex selection scenarios.\n\n**Key Features:**\n- Search functionality\n- Tags mode for multiple selections\n- API integration for dynamic options\n- Custom templates and styling\n- Multiple selection support\n- Option grouping\n- Icons and descriptions for options\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n\n## Installation\n\nTo get started, install Select plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select\n```\n\nSome positioning features require [Floating UI](https://floating-ui.com). Install it if you plan to use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`:\n\n```bash\nnpm i @floating-ui/dom\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, also load the [Floating UI](https://floating-ui.com) UMD bundle before the plugin. It exposes the `FloatingUIDOM` global the plugin relies on.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script src=\"./node_modules/@preline/select/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script type=\"module\">\n  import HSSelect from \"@preline/select/non-auto.mjs\";\n\n  new HSSelect(document.querySelector(\"#select\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, expose `FloatingUIDOM` on `window` before importing the plugin.\n\n`@preline/select` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport \"@preline/select\";\n```\n\n`@preline/select/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport HSSelect from \"@preline/select/non-auto\";\n\nHSSelect.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#select\");\nif (el) new HSSelect(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced select component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component transforms a standard select element into an advanced dropdown.\n\n```html\n<select data-hs-select='{\n    \"placeholder\": \"Select option...\",\n    \"toggleTag\": \"<button type=\\\"button\\\"></button>\",\n    \"toggleClasses\": \"\",\n    \"dropdownClasses\": \"\",\n    \"optionClasses\": \"hs-selected:\"\n  }'>\n  <option>Select option</option>\n  <option>Name</option>\n  <option>Email address</option>\n  <option>Description</option>\n  <option>User ID</option>\n</select>\n```\n\n**Structure Requirements:**\n- `data-hs-select`: Required on the `<select>` element, contains configuration options as JSON\n- Standard `<option>` elements inside the select\n- Optional `<optgroup>` elements for grouping\n\n**Initial State:**\n- Select displays placeholder text when no option is selected\n- Dropdown is closed by default\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-select` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-select` | Select element | - | - | Activate a Custom Select by specifying on an element. Should be added to the select. |\n| `:isOpened` | Inside `data-hs-select` | boolean | `false` | Opens the select if the value is `true`. |\n| `:placeholder` | Inside `data-hs-select` | string | `\"Select...\"` | Define a default placeholder when nothing is selected. |\n| `:hasSearch` | Inside `data-hs-select` | boolean | `false` | Define a search field inside the dropdown if the value is `true`. |\n| `:minSearchLength` | Inside `data-hs-select` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:preventSearchFocus` | Inside `data-hs-select` | boolean | `false` | Sets autofocus for the search field inside a dropdown list if the value is `true`. |\n| `:preventSearchInsideDescription` | Inside `data-hs-select` | boolean | `false` | Prevents searching inside the description when set to `true`. |\n| `:mode` | Inside `data-hs-select` | `'default'` \\| `'tags'` | `'default'` | Define a select mode. `default` for single/multiple selection, `tags` for tag-based selection. |\n| `:scrollToSelected` | Inside `data-hs-select` | boolean | `false` | Scroll to the selected option when the dropdown is opened. |\n| `:value` | Inside `data-hs-select` | string \\| string[] | - | Defines the initial selected value. Use an array when the select is multiple. |\n| `:viewport` | Inside `data-hs-select` | string | - | Defines the viewport selector used when recalculating dropdown direction and available space. |\n| `:toggleTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the select toggle. Note: `data-title` is required if you are using a custom placeholder. |\n| `:toggleClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' toggle. CSS classes must be separated by a space. |\n| `:toggleSeparators` | Inside `data-hs-select` | object | - | Define separators for the selects' toggle. |\n| `:toggleSeparators:items` | Inside `data-hs-select` | string | `\", \"` | Define which separator will be used for separate selected items. |\n| `:toggleSeparators:betweenItemsAndCounter` | Inside `data-hs-select` | string | `\"and\"` | Define which separator will be used for separate selected items and counter text. |\n| `:toggleCountText` | Inside `data-hs-select` | string | - | This option is only available for multiple select. Determines what text will be after the counter. It also activates the counting mode. |\n| `:toggleCountTextPlacement` | Inside `data-hs-select` | `'postfix'` \\| `'prefix'` \\| `'postfix-no-space'` \\| `'prefix-no-space'` | `'postfix'` | Allows to specify where the text specified in the `toggleCountText` parameter will be located relative to the counter. |\n| `:toggleCountTextMinItems` | Inside `data-hs-select` | number | `1` | This option is only available for multiple select. Defines the minimum number of selected items at which the counting mode will be activated. |\n| `:toggleCountTextMode` | Inside `data-hs-select` | `'countAfterLimit'` \\| `'nItemsAndCount'` | `'countAfterLimit'` | This option is only available for multiple select. Controls the display of the contents of the button title. |\n| `:wrapperClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' wrapper. CSS classes must be separated by a space. |\n| `:tagsItemTemplate` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define template for the single tag. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-remove` the target tag will be deleted when click on tag with this data attribute. |\n| `:tagsItemClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the single tags. CSS classes must be separated by a space. |\n| `:tagsInputId` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. This option is useful if there is a need to associate a `label` outside the initialized element with the tags input. |\n| `:tagsInputClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the input. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownTag` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define a markup for the dropdown. |\n| `:dropdownClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"bottom\"` | Specifies the position of the menu when opened. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownAutoPlacement` | Inside `data-hs-select` | boolean | `false` | Automatically determine the placement of the menu based on the available space. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownVerticalFixedPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"bottom\"` \\| null | `null` | Specifies a fixed vertical position for the menu when opened. It will not change when scrolling. |\n| `:dropdownScope` | Inside `data-hs-select` | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `:dropdownDirectionClasses` | Inside `data-hs-select` | `{ top?: string; bottom?: string; }` | `null` | Defines classes that are applied when the dropdown is placed above or below the toggle. |\n| `:dropdownSpace` | Inside `data-hs-select` | number | `10` | Defines the spacing in pixels between the toggle and dropdown. |\n| `:searchTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<input type=\\\"text\\\">\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search input. |\n| `:searchWrapperTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<div></div>\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search wrapper. |\n| `:searchWrapperClasses` | Inside `data-hs-select` | string | `\"bg-white p-2 sticky top-0\"` | This option is only available when `hasSearch: true`. Define CSS classes for the search wrapper. |\n| `:searchId` | Inside `data-hs-select` | string | - | This option is only available when `hasSearch: true`. This option is useful if there is a need to associate a `label` outside the initialized element with the search input. |\n| `:searchLimit` | Inside `data-hs-select` | number | `Infinity` | This option is only available when `hasSearch: true`. If this option is enabled, the search will display only the first 'n' matching items. |\n| `:isSearchDirectMatch` | Inside `data-hs-select` | boolean | `true` | This option is only available when `hasSearch: true`. If the option is disabled, then in the search results you will be able to see non-direct matches by text. For example, if you entered `england` in the search field, then `Eng-land`, `Eng.land`, `Eng_land` will also be shown. |\n| `:searchClasses` | Inside `data-hs-select` | string | `\"block w-[calc(100%-32px)] text-sm border-gray-200 rounded-md focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 py-2 px-3 my-2 mx-4\"` | This option is only available when `:hasSearch` attribute is `true`. Define CSS classes for the search field inside the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:searchPlaceholder` | Inside `data-hs-select` | string | `\"Search...\"` | This option is only available when `:hasSearch` attribute is `true`. Determines placeholder for the search field inside the dropdown. |\n| `:searchNoResultTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<span></span>\"` | This option is only available when `:hasSearch` attribute is `true`. Define a markup for the \"no result\" text. |\n| `:searchNoResultText` | Inside `data-hs-select` | string | `\"No options found...\"` | This option is only available when `:hasSearch` attribute is `true`. Defines the text that will be displayed if no results are found. |\n| `:searchNoResultClasses` | Inside `data-hs-select` | string | `\"px-4 text-sm\"` | This option is only available when `:hasSearch` attribute is `true`. Defines CSS classes for the \"no results\" wrapper. CSS classes must be separated by a space. |\n| `:optionAllowEmptyOption` | Inside `data-hs-select` | boolean | `false` | This option determines whether an empty option should be included in the list. When set to `true`, an additional option with an empty string (`\"\"`) as its value will be displayed, allowing users to select a blank choice if needed. |\n| `:optionTemplate` | Inside `data-hs-select` | string (HTML markup) | - | Define template for the single option. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-description` if target option tag has `data-hs-select-option:description`. |\n| `:optionTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option. |\n| `:optionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option. CSS classes must be separated by a space. |\n| `:optgroupTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option group. |\n| `:optgroupClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option group. CSS classes must be separated by a space. |\n| `:extraMarkup` | Inside `data-hs-select` | string \\| array (HTML markup) | - | Define a markup that could be extra added to the wrapper of the select for the decoration reasons. If it contains the `--prevent-click` class, clicking on this element will not trigger the open function. |\n| `:descriptionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the description inside the single option. CSS classes must be separated by a space. |\n| `:iconClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the icon inside the single option. CSS classes must be separated by a space. |\n| `:isAddTagOnEnter` | Inside `data-hs-select` | boolean | `true` | Determines whether a tag will be added when the enter key is pressed. |\n| `:isSelectedOptionOnTop` | Inside `data-hs-select` | boolean | `false` | Controls whether selected options should appear at the top of the dropdown list. When set to `true`, selected options will be sorted to the top of the list. |\n| `:apiUrl` | Inside `data-hs-select` | string \\| null | `null` | Defines the address where the API is located. |\n| `:apiQuery` | Inside `data-hs-select` | string \\| null | `null` | Defines query parameters that are separated by `?`. |\n| `:apiOptions` | Inside `data-hs-select` | RequestInit \\| null | `null` | Defines options for the fetch function. |\n| `:apiDataPart` | Inside `data-hs-select` | string \\| null | `null` | If data is in some first level parameter, then it allows you to specify the name of this parameter to extract the data. |\n| `:apiSearchQueryKey` | Inside `data-hs-select` | string \\| null | `null` | Defines the key for the query search parameter. |\n| `:apiFieldsMap` | Inside `data-hs-select` | `{ id: string; title: string; val?: string; icon?: string \\| null; description?: string \\| null; page?: string; offset?: string; limit?: string; pageStart?: string; totalPath?: string; [key: string]: unknown; } \\| null` | `null` | Allows you to convert fields from the API to the fields required for the plugin to work. `page`, `offset`, and `limit` customize pagination parameter names for remote data. |\n| `:apiLoadMore` | Inside `data-hs-select` | `boolean \\| { perPage?: number; scrollThreshold?: number; }` | `false` | Defines the options for the load more feature. |\n| `:apiPageStart` | Inside `data-hs-select` | number | - | Defines the first page or offset value used for remote option loading. If omitted, page-based loading starts from `1` and offset-based loading starts from `0`. |\n| `:apiTotalPath` | Inside `data-hs-select` | string \\| null | `null` | Defines the response path used to read the total result count for remote option loading. |\n| `:apiSelectedValues` | Inside `data-hs-select` | `string \\| string[] \\| null` | `null` | Allows to preselect values when loading options from an API. Can be a single value or an array of values for multiple selection. |\n| `:apiIconTag` | Inside `data-hs-select` | string \\| null | `null` | Allows to define an `img` tag that will be used when rendering options and in the trigger button. |\n| `data-hs-select-option` | Option element | object | - | Allows to define the parameters for the single option. Should be added to the option. |\n| `:description` | Inside `data-hs-select-option` | string | - | Allows to define the description of the option inside the element that have `data-description` attribute. |\n| `:icon` | Inside `data-hs-select-option` | string | - | Allows to define the icon of the option inside the element that have `data-icon` attribute. |\n| `:additionalClasses` | Inside `data-hs-select-option` | `[string, string[]][]` | - | Allows to define additional classes. The first string is the selector of the element to apply the classes to, the second string is an array of classes to apply. If the selector is set to `null`, the classes will be applied to the option itself. |\n| `:apiFields` | Inside `data-hs-select-option` | `{ [key: string]: unknown; }` | - | Allows to define the fields of the option from the API. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-select-opened:*` | A modifier that allows you to set Tailwind classes when select has been opened. |\n| `hs-select-disabled:*` | A modifier that allows you to set Tailwind classes when select has \"disabled\" attribute. |\n\n## JavaScript API\n\nThe `HSSelect` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a select instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open()` | None | `void` | Opens the dropdown list programmatically. |\n| `close()` | None | `void` | Closes the dropdown list programmatically. |\n| `setValue(value)` | `value`: `string \\| string[]` | `void` | Set the select value. This should be a string for a simple select and an array if it's a multiple select. |\n| `addOption(items)` | `items`: `{ title: string; val: string; options?: { description: string; icon: string; }; }` or array of such objects | `void` | Adds an option (several possible) to the select. The single option should follow this interface. |\n| `removeOption(val)` | `val`: `string \\| string[]` | `void` | Removes an option by value (several possible). |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown list. Useful when dropdown position needs to be updated after layout changes. |\n| `destroy()` | None | `void` | Destroys the select instance, removes all generated markup, classes, and event listeners. Use when removing select from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSSelect` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSSelect.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSSelect } \\| null` | Returns the select instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSSelect` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if select instance is not found. |\n\n### Usage Examples\n\n**Example 1: Opening and closing dropdown**\n```javascript\n// Get the select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Open dropdown\n  element.open();\n\n  // Close dropdown\n  element.close();\n}\n```\n\n**Example 2: Setting value programmatically**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set value for single select\n  element.setValue('option-value');\n\n  // Set value for multiple select\n  element.setValue(['value-1', 'value-2']);\n}\n```\n\n**Example 3: Adding and removing options**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Add option\n  element.addOption({\n    title: 'New Option',\n    val: 'new-option',\n    options: {\n      description: 'Option description',\n      icon: '<svg>...</svg>'\n    }\n  });\n\n  // Remove option\n  element.removeOption('new-option');\n}\n```\n\n**Example 4: Destroying select instance**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-select').remove();\n  });\n}\n```\n\n## Events\n\nSelect instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected value changes | `string | string[]` (new selected value; array in multi-select / tags mode) | Fires after the user picks an option or removes a tag. |\n| `on:request` | Right before an `apiUrl` request is sent (search or `apiLoadMore` pagination) | `{ url: string }` | Useful for showing a loading indicator. |\n| `on:requestCompleted` | After an `apiUrl` request settles, success or failure | `{ url: string, data?: unknown, error?: unknown }` | `data` holds the parsed response on success, `error` is set on failure. Not fired when a request is superseded/aborted (e.g. a newer search query). |\n\nBoth are also dispatched as DOM `CustomEvent`s (`request.hs.select` / `requestCompleted.hs.select`) with the same payload under `event.detail.payload`, so they can be listened to via `addEventListener` as well as `element.on(...)`.\n\n### Event Usage Example\n\n```javascript\n// Get select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for selection changes\n  element.on('change', (value) => {\n    console.log('Selected value:', value);\n    // value is a string for single-select, string[] for multi-select / tags mode\n  });\n\n  // Listen for API request lifecycle (only relevant when `apiUrl` is set)\n  element.on('request', ({ url }) => {\n    console.log('Requesting:', url);\n    // e.g. show a loading spinner\n  });\n\n  element.on('requestCompleted', ({ data, error }) => {\n    if (error) console.error('Request failed:', error);\n    else console.log('Request completed with:', data);\n    // e.g. hide the loading spinner\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Search Enabled\n\nEnable search functionality in dropdown.\n\n```html\n<select data-hs-select='{\n  \"hasSearch\": true,\n  \"searchPlaceholder\": \"Search options...\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 2: Tags Mode\n\nUse tags mode for multiple selections.\n\n```html\n<select multiple data-hs-select='{\n  \"mode\": \"tags\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 3: API Integration\n\nLoad options from API.\n\n```html\n<select data-hs-select='{\n  \"apiUrl\": \"/api/options\",\n  \"apiFieldsMap\": {\n    \"id\": \"id\",\n    \"title\": \"name\",\n    \"val\": \"value\"\n  }\n}'>\n  <!-- Options -->\n</select>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/stepper/README.md":"# Stepper\n\nDynamic stepper plugin that guides users through the steps of a task.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/stepper) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/stepper.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Stepper component provides a multi-step wizard interface that guides users through a sequential process. It supports both linear (sequential) and non-linear (jumpable) modes, with options for optional steps, error states, and completion tracking.\n\n**Key Features:**\n- Multi-step wizard interface\n- Linear and non-linear navigation modes\n- Optional steps support\n- Error and processing states\n- Completion tracking\n- Programmatic control via JavaScript API\n- Event system for step transitions\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Stepper plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/stepper\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/stepper */\n@source \"../node_modules/@preline/stepper/*.js\";\n@import \"./node_modules/@preline/stepper/variants.css\";\n@import \"./node_modules/@preline/stepper/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/stepper/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStepper from \"@preline/stepper/non-auto.mjs\";\n\n  new HSStepper(document.querySelector(\"#stepper\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/stepper` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/stepper\";\n```\n\n`@preline/stepper/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStepper from \"@preline/stepper/non-auto\";\n\nHSStepper.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#stepper\");\nif (el) new HSStepper(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a stepper component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three steps with navigation buttons.\n\n```html\n<div data-hs-stepper>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 1\n  }'>\n    1 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 2\n  }'>\n    2 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 3\n  }'>\n    3 Step\n  </div>\n  \n  <div data-hs-stepper-content-item='{\n    \"index\": 1\n  }' style=\"display: none;\">\n    First content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 2\n  }' style=\"display: none;\">\n    Second content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 3\n  }' style=\"display: none;\">\n    Third content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"isFinal\": true\n  }' style=\"display: none;\">\n    Final content.\n  </div>\n  \n  <button class=\"hs-stepper-disabled:opacity-50\" type=\"button\" data-hs-stepper-back-btn>\n    Back\n  </button>\n  <button type=\"button\" data-hs-stepper-skip-btn style=\"display: none;\">\n    Skip\n  </button>\n  <button type=\"button\" data-hs-stepper-next-btn>\n    Next\n  </button>\n  <button type=\"button\" data-hs-stepper-finish-btn style=\"display: none;\">\n    Finish\n  </button>\n  <button type=\"reset\" data-hs-stepper-reset-btn style=\"display: none;\">\n    Reset\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-stepper`: Required on the container element\n- `data-hs-stepper-nav-item`: Required on each navigation item\n- `data-hs-stepper-content-item`: Required on each content panel\n- `data-hs-stepper-back-btn`: Optional back button\n- `data-hs-stepper-next-btn`: Optional next button\n- `data-hs-stepper-finish-btn`: Optional finish button\n- `data-hs-stepper-skip-btn`: Optional skip button\n- `data-hs-stepper-reset-btn`: Optional reset button\n\n**Initial State:**\n- First step is active by default (index 1)\n- Content items should have `style=\"display: none;\"` except the first one\n- Navigation buttons visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-stepper`, `data-hs-stepper-nav-item`, and `data-hs-stepper-content-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-stepper` | Container | - | - | Activate a Stepper by specifying on an element. Should be added to the container. |\n| `:currentIndex` | Inside `data-hs-stepper` | number | `1` | Index of the current step. This must be a number between 1 and the maximum number of steps. |\n| `:mode` | Inside `data-hs-stepper` | `\"linear\"` \\| `\"non-linear\"` | `\"linear\"` | The mode of the stepper. In `\"non-linear\"` mode, the user can navigate to any step and nav items are clickable. In `\"linear\"` mode, the user can only navigate to the next/back step. |\n| `:isCompleted` | Inside `data-hs-stepper` | boolean | `false` | Whether the stepper is completed. |\n| `data-hs-stepper-nav-item` | Navigation item | - | - | Activate a Stepper Nav Item by specifying on an element. Should be added to the nav item. |\n| `:index` | Inside `data-hs-stepper-nav-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isOptional` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is optional. |\n| `:isCompleted` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is skipped. |\n| `:hasError` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step has an error. |\n| `data-hs-stepper-content-item` | Content item | - | - | Activate a Stepper Content Item by specifying on an element. Should be added to the content item. |\n| `:index` | Inside `data-hs-stepper-content-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isCompleted` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is skipped. |\n| `:isFinal` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is final. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-stepper-active:*` | Modifies the active step. |\n| `hs-stepper-success:*` | Modifies the completed step. |\n| `hs-stepper-disabled:*` | Modifies the \"back\" button when the very first step is active. |\n| `hs-stepper-skipped:*` | Modifies the skipped step. |\n| `hs-stepper-error:*` | Modifies the step that has error. Error class should be added manually by some event. E.g. after form validation. |\n| `hs-stepper-process:*` | Modifies the step that processing. Process class should be added manually by some event. E.g. after form submit. |\n| `hs-stepper-completed:*` | Modifies all steps are completed. |\n\n## JavaScript API\n\nThe `HSStepper` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a stepper instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setProcessedNavItem(n)` | `n`: `number` | `void` | Set the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `setErrorNavItem(n)` | `n`: `number` | `void` | Set the nav item as error. `n` is the index of the step to which the item belongs. |\n| `unsetProcessedNavItem(n)` | `n`: `number` | `void` | Unset the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `goToNext()` | None | `number` | Go to the next step. Returns the index of the next step. If the current step is the last, returns the index of the current step. |\n| `goToFinish()` | None | `number` | Go to the finish step. Returns the index of the finish step. If the current step is the last, returns the index of the current step. |\n| `disableButtons()` | None | `void` | Disable the \"next\" and \"back\" buttons. |\n| `enableButtons()` | None | `void` | Enable the \"next\" and \"back\" buttons. |\n| `destroy()` | None | `void` | Destroys the stepper instance, removes all generated markup, classes, and event listeners. Use when removing stepper from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStepper` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStepper.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSStepper \\| { id: string \\| number, element: HSStepper } \\| null` | Returns the stepper instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStepper` instance. If `isInstance` is `false` or omitted, returns the `HSStepper` instance directly. Returns `null` if stepper instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new stepper instance\nconst stepper = new HSStepper(document.querySelector('#hs-stepper'));\nlet errorState = 1;\n\nstepper.on('beforeNext', (index) => {\n  if (index === 2) {\n    stepper.setProcessedNavItem(index);\n    \n    setTimeout(() => {\n      stepper.unsetProcessedNavItem(index);\n      stepper.enableButtons();\n      \n      if (errorState) {\n        stepper.goToNext();\n      } else {\n        stepper.setErrorNavItem(index);\n      }\n      \n      errorState = !errorState;\n    }, 2000);\n  }\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSStepper instance\n  let errorState = 1;\n\n  element.on('beforeNext', (index) => {\n    if (index === 2) {\n      element.setProcessedNavItem(index);\n      \n      setTimeout(() => {\n        element.unsetProcessedNavItem(index);\n        element.enableButtons();\n        \n        if (errorState) {\n          element.goToNext();\n        } else {\n          element.setErrorNavItem(index);\n        }\n        \n        errorState = !errorState;\n      }, 2000);\n    }\n  });\n\n  // Clean up when removing from DOM\n  function removeStepper() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Programmatic navigation**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\nif (instance) {\n  const { element } = instance;\n  // Navigate to next step\n  const nextIndex = element.goToNext();\n  console.log('Moved to step:', nextIndex);\n  // Navigate to finish\n  const finishIndex = element.goToFinish();\n\n  console.log('Finished at step:', finishIndex);\n}\n```\n\n**Example 4: Destroying stepper instance**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-stepper').remove();\n  });\n}\n```\n\n## Events\n\nStepper instances emit events that can be listened to for step transitions and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:active` | When the \"active\" class is set up | `currentIndex` (number) | Fires when a step becomes active. |\n| `on:beforeNext` | Before the \"next\" button is clicked | `currentIndex` (number) | Fires before navigating to the next step. Can be used to validate or prevent navigation. |\n| `on:beforeFinish` | Before the \"finish\" button is clicked | `currentIndex` (number) | Fires before finishing the stepper. Can be used to validate or prevent completion. |\n| `on:next` | When the \"next\" button is clicked | `currentIndex` (number) | Fires when navigating to the next step. |\n| `on:back` | When the \"back\" button is clicked | `currentIndex` (number) | Fires when navigating to the previous step. |\n| `on:complete` | When the \"complete\" button is clicked | `currentIndex` (number) | Fires when completing a step. |\n| `on:finish` | When the \"finish\" button is clicked | `currentIndex` (number) | Fires when finishing the stepper. |\n\n### Event Usage Example\n\n```javascript\n// Get stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to step activation\n  element.on('active', (currentIndex) => {\n    console.log('Step activated:', currentIndex);\n    // Perform actions when step becomes active\n    // e.g., load content, initialize form fields\n  });\n\n  // Listen to before next (for validation)\n  element.on('beforeNext', (currentIndex) => {\n    console.log('Before next step:', currentIndex);\n    // Validate current step before proceeding\n    // Can prevent navigation if validation fails\n  });\n\n  // Listen to finish\n  element.on('finish', (currentIndex) => {\n    console.log('Stepper finished:', currentIndex);\n    // Perform final actions\n    // e.g., submit form, show success message\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Non-linear Mode\n\nAllow users to jump to any step.\n\n```html\n<div data-hs-stepper='{\n  \"mode\": \"non-linear\"\n}'>\n  <!-- Stepper content -->\n</div>\n```\n\n### Pattern 2: Error Handling\n\nSet error state on a step after validation fails.\n\n```html\n<div id=\"hs-stepper-first\" data-hs-stepper>\n  <!-- Stepper content -->\n</div>\n\n<script>\n  const instance = HSStepper.getInstance('#hs-stepper-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    // Validate and set error if needed\n    function validateStep(stepIndex) {\n      // Validation logic\n      if (validationFails) {\n        element.setErrorNavItem(stepIndex);\n      }\n    }\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/strong-password/README.md":"# Strong Password\n\nUse strong passwords based on required characters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/strong-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/strong-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Strong Password component provides real-time password strength validation with visual feedback. It checks password requirements such as minimum length, uppercase/lowercase letters, numbers, and special characters, displaying strength indicators and helpful hints.\n\n**Key Features:**\n- Real-time password strength checking\n- Visual strength indicators (strips)\n- Password requirement hints\n- Customizable strength rules\n- Popover mode for hints\n- Programmatic control via JavaScript API\n- Event system for strength tracking\n\n## Installation\n\nTo get started, install Strong Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/strong-password\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/strong-password */\n@source \"../node_modules/@preline/strong-password/*.js\";\n@import \"./node_modules/@preline/strong-password/variants.css\";\n@import \"./node_modules/@preline/strong-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/strong-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStrongPassword from \"@preline/strong-password/non-auto.mjs\";\n\n  new HSStrongPassword(document.querySelector(\"#strong-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/strong-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/strong-password\";\n```\n\n`@preline/strong-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStrongPassword from \"@preline/strong-password/non-auto\";\n\nHSStrongPassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#strong-password\");\nif (el) new HSStrongPassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a strong password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component shows password strength indicators as the user types.\n\n```html\n<div class=\"flex-1\">\n  <input type=\"password\" id=\"hs-strong-password-input-first\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm  focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400\">\n\n  <div data-hs-strong-password='{\n    \"target\": \"#hs-strong-password-input-first\",\n    \"stripClasses\": \"hs-strong-password:opacity-100 hs-strong-password-accepted:bg-green-500 h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n  }' id=\"hs-strong-password-first\" class=\"flex mt-2 -mx-1\"></div>\n</div>\n\n<div class=\"mb-3\">\n  <span id=\"hs-strong-password-weakness-first\"></span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-strong-password`: Required on the container element, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Optional hints container for displaying password requirements\n\n**Initial State:**\n- Password input is empty\n- Strength strips are hidden or inactive\n- Hints are not displayed (unless configured)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-strong-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-strong-password` | Container element | - | - | Activate a Strong Password by specifying on an element. The value should contain at least one lowercase number, one uppercase letter, one number, one special character (!%&@#$^*?_~), and minimum 6 characters. Should be added to the content block that contains the checks. |\n| `:target` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed. This must be a valid CSS selector. Required. |\n| `:hints` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed as hints container. This must be a valid CSS selector. There are some selectors that available for using inside the hints: `[data-hs-strong-password-hints-weakness-text]` determines which element should be observed as weakness text container. It should contain an array of strings, e.g. - [\"Empty\", \"Weak\", \"Medium\", \"Strong\", \"Very Strong\", \"Super Strong\"]. Each text is for the concrete strength value, e.g. \"Medium\" have index 2, and it will be set up if strength is also 2. \"Empty\" have index 0, and it will be shown if field is empty and have no any strength. `[data-hs-strong-password-hints-rule-text]` determines which element should be observed as rule text container. It could have one of the follow value: \"min-length\" \\| \"lowercase\" \\| \"uppercase\" \\| \"numbers\" \\| \"special-characters\". `[data-check]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule check icon container. `[data-uncheck]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule uncheck icon container. |\n| `:stripClasses` | Inside `data-hs-strong-password` | string | - | Defines CSS classes for each strip. These classes are applied to each strength indicator strip. |\n| `:minLength` | Inside `data-hs-strong-password` | number | `6` | Determines minimum password length. |\n| `:mode` | Inside `data-hs-strong-password` | `\"default\"` \\| `\"popover\"` | `\"default\"` | Determines which mode should be used. If `popover` is selected, the hints will be displayed as a popover. |\n| `:popoverSpace` | Inside `data-hs-strong-password` | number | `10` | Available if `mode` is set to `popover`. Determines the space between the popover and the target element. |\n| `:checksExclude` | Inside `data-hs-strong-password` | array | `[]` | Determines which checks should be excluded. It could contain an array of strings, e.g. - [\"min-length\", \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"]. |\n| `:specialCharactersSet` | Inside `data-hs-strong-password` | string | `!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_\\`{|}~` | Determines which special characters should be used for checking. It could contain a string of available characters, e.g. - \"!%&\". |\n\n**Example:**\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"minLength\": 8,\n  \"mode\": \"popover\",\n  \"stripClasses\": \"h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n}'>\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-strong-password:*` | A modifier that allows you to set Tailwind classes for the passed stripes. |\n| `hs-strong-password-accepted:*` | A modifier that allows you to set Tailwind classes when password strength is accepted. |\n\n## JavaScript API\n\nThe `HSStrongPassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a strong password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown hint position. Useful when scrolling or layout changes occur. |\n| `destroy()` | None | `void` | Destroys the strong password instance, removes all generated markup, classes, and event listeners. Use when removing strong password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStrongPassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStrongPassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSStrongPassword } \\| null` | Returns the strong password instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStrongPassword` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if strong password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Recalculating popover position**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = element;\n\n  // Recalculate popover position on scroll\n  document.addEventListener('scroll', () => {\n    element.recalculateDirection();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Strength:', element.strength);\n  console.log('Passed rules:', element.passedRules);\n  console.log('Is opened:', element.isOpened);\n\n  // Clean up when removing from DOM\n  function removeStrongPassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying strong password instance**\n```javascript\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-strong-password').remove();\n  });\n}\n```\n\n## Events\n\nStrong password instances emit events that can be listened to for strength tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When target element was changed | `{ strength: number, rules: string[] }` | Fires when the password input value changes. Returns an object with `strength` (0-4, current level of strength, default is 0) and `rules` (array of passed checks: \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"). |\n\n### Event Usage Example\n\n```javascript\n// Get strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ strength, rules }) => {\n    console.log('Password strength:', strength);\n    console.log('Passed rules:', rules);\n    // Perform actions based on strength\n    // e.g., enable/disable submit button, show messages\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Hints Container\n\nDisplay password requirements in a hints container.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"hints\": \"#hs-hints-container\"\n}'></div>\n\n<div id=\"hs-hints-container\">\n  <div data-hs-strong-password-hints-weakness-text></div>\n  <div data-hs-strong-password-hints-rule-text data-check=\"min-length\"></div>\n</div>\n```\n\n### Pattern 2: Popover Mode\n\nDisplay hints as a popover.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"mode\": \"popover\",\n  \"popoverSpace\": 10\n}'></div>\n```\n\n### Pattern 3: Custom Requirements\n\nExclude certain checks or customize special characters.\n\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"checksExclude\": [\"special-characters\"],\n  \"specialCharactersSet\": \"!@#$%\"\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tabs/README.md":"# Tabs\n\nTabs make it easy to switch between different views.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tabs) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tabs.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tabs component allows users to switch between different content sections within the same context. It supports keyboard navigation, programmatic control, and customizable event types for activation.\n\n**Key Features:**\n- Multiple tab panels in a single group\n- Keyboard navigation support (Arrow keys, Home, End, Enter)\n- Customizable activation events (click, hover)\n- Programmatic control via JavaScript API\n- Event system for tab change tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tabs plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tabs\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tabs */\n@source \"../node_modules/@preline/tabs/*.js\";\n@import \"./node_modules/@preline/tabs/variants.css\";\n@import \"./node_modules/@preline/tabs/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tabs/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTabs from \"@preline/tabs/non-auto.mjs\";\n\n  new HSTabs(document.querySelector(\"#tabs\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tabs` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tabs\";\n```\n\n`@preline/tabs/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTabs from \"@preline/tabs/non-auto\";\n\nHSTabs.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tabs\");\nif (el) new HSTabs(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tabs component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three tabs with corresponding content panels, with the first tab active by default.\n\n```html\n<nav class=\"flex gap-x-2\" aria-label=\"Tabs\" role=\"tablist\">\n  <button type=\"button\" class=\"active\" id=\"hs-unstyled-tabs-item-first\" aria-selected=\"true\" data-hs-tab=\"#hs-unstyled-tabs-first\" aria-controls=\"hs-unstyled-tabs-first\" role=\"tab\">\n    Tab 1\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-second\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-second\" aria-controls=\"hs-unstyled-tabs-second\" role=\"tab\">\n    Tab 2\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-third\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-third\" aria-controls=\"hs-unstyled-tabs-third\" role=\"tab\">\n    Tab 3\n  </button>\n</nav>\n\n<div class=\"mt-3\">\n  <div id=\"hs-unstyled-tabs-first\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-first\">\n    This is the <em>first</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-second\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-second\">\n    This is the <em>second</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-third\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-third\">\n    This is the <em>third</em> item's tab body.\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `role=\"tablist\"`: Required on the container element (nav, div, etc.)\n- `role=\"tab\"`: Required on each tab button\n- `role=\"tabpanel\"`: Required on each content panel\n- `data-hs-tab`: Required on each tab button, must be a valid CSS selector pointing to the corresponding tabpanel\n- Unique `id` attributes for each tab button and its corresponding panel\n- Proper ARIA attributes (`aria-selected`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- To have a tab active by default, add the `active` class to the tab button and set `aria-selected=\"true\"`\n- For inactive tabs, add the `hidden` class to the tabpanel and set `aria-selected=\"false\"`\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| ArrowLeft / ArrowRight | Selects the previous/next non-disabled tab. |\n| ArrowUp / ArrowDown (in vertical mode) | Selects the previous/next non-disabled tab. |\n| Home / End | Selects the first/last non-disabled tab. |\n| Enter | Activates the selected tab. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure tab behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tabs` | Tablist container | object (JSON) | - | Contains configuration options for customizing tab behavior and appearance. |\n| `:eventType` | Inside `data-hs-tabs` | `\"click\"` \\| `\"hover\"` | `\"click\"` | Set `hover` to activate tabs on mouseover, or `click` for manual clicks (the default). |\n| `data-hs-tab` | Tab button (trigger) | string (CSS selector) | - | Activate a tab by specifying the selector of the target tabpanel. This must be a valid CSS selector pointing to the tabpanel element. |\n| `data-hs-tab-select` | Tablist container | string (ID) | - | You can pass a select ID there. Each option of this select must have a value equal to the tab ID (e.g., `<option value=\"#hs-tab-to-select-first\">Tab 1</option>`). When you select this value, the corresponding tab will be opened. |\n\n**Example:**\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n</nav>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tab-active:*` | A modifier that allows you to set Tailwind classes when the tab is active. Can be applied to both the toggle button and the content panel. |\n\n## JavaScript API\n\nThe `HSTabs` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tabs instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the tabs instance, removes all generated markup, classes, and event listeners. Use when removing tabs from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTabs` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTabs.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTabs \\| { id: string \\| number, element: HSTabs } \\| null` | Returns the tabs instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTabs` instance. If `isInstance` is `false` or omitted, returns the `HSTabs` instance directly. Returns `null` if tabs instance is not found. |\n| `HSTabs.open(target)` | `target`: `HTMLElement` | `void` | Opens the tab identified by target. The target should be a tab button element. Static method to programmatically switch tabs. |\n\n### Usage Examples\n\n**Example 1: Destroying tabs instance**\n```javascript\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Opening tab programmatically (static method)**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\nopenBtn.addEventListener('click', () => {\n  const tabButton = document.querySelector('#hs-tab-first');\n  \n  if (tabButton) {\n    HSTabs.open(tabButton);\n  }\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current tab:', element.current);\n  console.log('Current content:', element.currentContent);\n\n  // Clean up when removing from DOM\n  function removeTabs() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nTabs instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When any tab is changed | `{ el: HTMLElement, prev: string, current: string }` | Fires when a tab is switched. Returns an object with:<br>- `el`: The toggle button element that was clicked<br>- `prev`: Previous tab ID<br>- `current`: Current tab ID |\n\n### Event Usage Example\n\n```javascript\n// Get tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ el, prev, current }) => {\n    console.log('Tab changed:', {\n      clickedButton: el,\n      previousTab: prev,\n      currentTab: current\n    });\n    // Perform actions after tab changes\n    // e.g., load content, update UI, track analytics\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Activation\n\nActivate tabs on hover instead of click.\n\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-second\" role=\"tab\">Tab 2</button>\n</nav>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tabs from external buttons.\n\n```html\n<nav id=\"hs-tabs-first\" aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-content-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-content-second\" role=\"tab\">Tab 2</button>\n</nav>\n\n<button id=\"hs-open-tab-first\">Open Tab 1</button>\n<button id=\"hs-open-tab-second\">Open Tab 2</button>\n\n<script>\n  document.querySelector('#hs-open-tab-first').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-first\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n\n  document.querySelector('#hs-open-tab-second').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-second\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/textarea-auto-height/README.md":"# Textarea Autoheight\n\nAutomatically sets the height of the textarea.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/textarea-auto-height) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/textarea-autoheight.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Textarea Autoheight component automatically adjusts the height of a textarea element based on its content. As users type, the textarea expands or contracts to fit the text, eliminating the need for scrollbars.\n\n**Key Features:**\n- Automatic height adjustment based on content\n- Smooth resizing as user types\n- Configurable default height\n- Programmatic control via JavaScript API\n- No scrollbars needed\n\n## Installation\n\nTo get started, install Textarea Autoheight plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/textarea-auto-height\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/textarea-auto-height */\n@import \"./node_modules/@preline/textarea-auto-height/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/textarea-auto-height/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto.mjs\";\n\n  new HSTextareaAutoHeight(document.querySelector(\"#textarea\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/textarea-auto-height` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/textarea-auto-height\";\n```\n\n`@preline/textarea-auto-height/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto\";\n\nHSTextareaAutoHeight.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#textarea\");\nif (el) new HSTextareaAutoHeight(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a textarea autoheight component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The textarea automatically adjusts its height as content is entered.\n\n```html\n<textarea data-hs-textarea-auto-height></textarea>\n```\n\n**Structure Requirements:**\n- `data-hs-textarea-auto-height`: Required on the textarea element\n- Standard `<textarea>` element\n\n**Initial State:**\n- Textarea starts at default height (or configured `defaultHeight`)\n- Height adjusts automatically as content is entered or removed\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-textarea-auto-height` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-textarea-auto-height` | Textarea element | - | - | If this attribute is added to the textarea, then automatic height calculation according to the content is enabled. Should be added to the textarea itself. |\n| `:defaultHeight` | Inside `data-hs-textarea-auto-height` | number | `0` | Sets the default height (when no text is entered). When set to `0`, the textarea uses its natural height. |\n\n**Example:**\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 50\n}'></textarea>\n```\n\n## JavaScript API\n\nThe `HSTextareaAutoHeight` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a textarea autoheight instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the textarea autoheight instance, removes all generated markup, classes, and event listeners. Use when removing textarea autoheight from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTextareaAutoHeight` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTextareaAutoHeight.getInstance(target, isInstance)` | `target`: `HTMLTextAreaElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTextareaAutoHeight \\| { id: string \\| number, element: HSTextareaAutoHeight } \\| null` | Returns the textarea autoheight instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTextareaAutoHeight` instance. If `isInstance` is `false` or omitted, returns the `HSTextareaAutoHeight` instance directly. Returns `null` if textarea autoheight instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the textarea autoheight instance\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Default height:', element.defaultHeight);\n\n  // Clean up when removing from DOM\n  function removeTextareaAutoheight() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-textarea-autoheight').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Default Height\n\nSet a custom default height for the textarea.\n\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 100\n}'></textarea>\n```\n\n### Pattern 2: Multiple Autoheight Textareas\n\nApply autoheight to multiple textareas.\n\n```html\n<textarea id=\"hs-comment-first\" data-hs-textarea-auto-height></textarea>\n<textarea id=\"hs-comment-second\" data-hs-textarea-auto-height></textarea>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/theme-switch/README.md":"# Theme Switch\n\nStyle your site in dark mode with ready made Preline UI's dark mode toggle plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/theme-switch) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/theme-switch.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Toggling dark mode manually](#toggling-dark-mode-manually)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Theme Switch component provides functionality to toggle between light and dark themes. It manages theme state, saves preferences to localStorage, and integrates with Tailwind CSS dark mode functionality.\n\n**Key Features:**\n- Light/dark theme switching\n- System theme detection\n- LocalStorage persistence\n- Tailwind CSS integration\n- Programmatic control via JavaScript API\n- Event system for theme changes\n\n## Installation\n\nTo get started, install Theme Switch plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/theme-switch\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/theme-switch */\n@source \"../node_modules/@preline/theme-switch/*.js\";\n@import \"./node_modules/@preline/theme-switch/variants.css\";\n@import \"./node_modules/@preline/theme-switch/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/theme-switch/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSThemeSwitch from \"@preline/theme-switch/non-auto.mjs\";\n\n  new HSThemeSwitch(document.querySelector(\"#theme-switch\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/theme-switch` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/theme-switch\";\n```\n\n`@preline/theme-switch/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSThemeSwitch from \"@preline/theme-switch/non-auto\";\n\nHSThemeSwitch.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#theme-switch\");\nif (el) new HSThemeSwitch(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a theme switch component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles between light and dark themes.\n\n```html\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:hidden block\" data-hs-theme-click-value=\"dark\">\n  Dark\n</button>\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:inline-flex hidden\" data-hs-theme-click-value=\"light\">\n  Light\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-theme-click-value`: Required on button elements, specifies the theme value (`'default'`, `'dark'`, or `'auto'`)\n- `hs-dark-mode`: Required class for theme-aware styling\n- Buttons can be separate elements or combined into one toggle\n\n**Initial State:**\n- Theme is determined by system preference or saved preference\n- Appropriate button is shown/hidden based on current theme\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-theme-click-value` | Button element | `'default'` \\| `'dark'` \\| `'auto'` | `'default'` | When you click on an element with this attribute, the theme changes to the one specified in the attribute. Should be added to the button (trigger). |\n| `data-hs-theme-switch` | Input element (checkbox/radio) | - | - | When you change an element with this attribute, the theme changes to the opposite one. Element should have change event, e.g. checkbox or radio button. Should be added to the input (checkbox). |\n\n**Example:**\n```html\n<!-- Click-based toggle -->\n<button data-hs-theme-click-value=\"dark\">Dark Mode</button>\n\n<!-- Change-based toggle -->\n<input type=\"checkbox\" data-hs-theme-switch>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dark-mode-active:*` | Defines CSS classes when dark mode is ON |\n| `hs-default-mode-active:*` | Defines CSS classes when default (light) mode is ON |\n| `hs-default-auto-active:*` | Defines CSS classes according to the System theme |\n\n## JavaScript API\n\nThe `HSThemeSwitch` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a theme switch instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setAppearance(theme, isSaveToLocalStorage, isSetDispatchEvent)` | `theme`: `string` (optional, `'dark'` \\| `'light'` \\| `'default'`)<br>`isSaveToLocalStorage`: `boolean` (optional)<br>`isSetDispatchEvent`: `boolean` (optional) | `void` | Sets the appearance/theme programmatically. `theme` specifies the theme to set. `isSaveToLocalStorage` determines whether to save to localStorage (default: `true`). `isSetDispatchEvent` determines whether to dispatch the theme change event (default: `true`). |\n| `destroy()` | None | `void` | Destroys the theme switch instance, removes all generated markup, classes, and event listeners. Use when removing theme switch from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSThemeSwitch` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSThemeSwitch.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSThemeSwitch } \\| null` | Returns the theme switch instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSThemeSwitch` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if theme switch instance is not found. |\n\n### Usage Examples\n\n**Example 1: Setting theme programmatically**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set dark theme\n  element.setAppearance('dark');\n\n  // Set light theme\n  element.setAppearance('light');\n\n  // Set theme without saving to localStorage\n  element.setAppearance('dark', false);\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current theme:', element.theme);\n  console.log('Type:', element.type);\n\n  // Clean up when removing from DOM\n  function removeThemeSwitch() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying theme switch instance**\n```javascript\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('[data-hs-theme-click-value]').remove();\n  });\n}\n```\n\n## Events\n\nTheme switch instances emit global events that can be listened to for theme change tracking.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on-hs-appearance-change` | Every time the mode changes | None | An event that fires every time the mode changes. This is a global window event, not an instance event. |\n\n### Event Usage Example\n\n```javascript\n// Listen to global theme change event\nwindow.addEventListener('on-hs-appearance-change', () => {\n  console.log('Theme changed!');\n  // Perform actions after theme changes\n  // e.g., update UI, reload resources, track analytics\n});\n```\n\n## Common Patterns\n\n### Pattern 1: Click-based Toggle\n\nUse buttons to toggle between themes.\n\n```html\n<button data-hs-theme-click-value=\"dark\">Dark</button>\n<button data-hs-theme-click-value=\"light\">Light</button>\n```\n\n### Pattern 2: Checkbox Toggle\n\nUse a checkbox to toggle theme.\n\n```html\n<input type=\"checkbox\" data-hs-theme-switch>\n<label>Dark Mode</label>\n```\n\n### Pattern 3: Programmatic Control\n\nControl theme programmatically.\n\n```html\n<button id=\"hs-dark-btn\">Dark</button>\n<button id=\"hs-light-btn\">Light</button>\n\n<script>\n  const instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-dark-btn').addEventListener('click', () => {\n      element.setAppearance('dark');\n    });\n\n    document.querySelector('#hs-light-btn').addEventListener('click', () => {\n      element.setAppearance('light');\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/toggle-count/README.md":"# Toggle Count\n\nToggle count from one value to another.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-count) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-count.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Count component provides animated counting functionality that transitions between two numeric values. It's commonly used for pricing displays, statistics, or any scenario where values need to animate between states (e.g., monthly/annual pricing).\n\n**Key Features:**\n- Animated counting between values\n- Configurable animation duration\n- Programmatic control via JavaScript API\n- Smooth transitions\n- Support for multiple count elements\n\n## Installation\n\nTo get started, install Toggle Count plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-count\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-count */\n@import \"./node_modules/@preline/toggle-count/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-count/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSToggleCount from \"@preline/toggle-count/non-auto.mjs\";\n\n  new HSToggleCount(document.querySelector(\"#toggle-count\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-count` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-count\";\n```\n\n`@preline/toggle-count/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSToggleCount from \"@preline/toggle-count/non-auto\";\n\nHSToggleCount.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-count\");\nif (el) new HSToggleCount(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle count component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The count animates between min and max values when the toggle changes.\n\n```html\n<div>\n  <div id=\"hs-toggle-count-first\">\n    <label>\n      Monthly\n      <input id=\"hs-toggle-count-monthly-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\">\n    </label>\n    <label>\n      Annual\n      <input id=\"hs-toggle-count-annual-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\" checked/>\n    </label>\n  </div>\n</div>\n\n<div class=\"space-x-5\">\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 19,\n    \"max\": 29\n  }'>\n    19\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 89,\n    \"max\": 99\n  }'>\n    89\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 129,\n    \"max\": 149\n  }'>\n    129\n  </span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-count`: Required on the element that displays the count, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the toggle element (radio buttons or checkbox)\n- Toggle element (radio buttons or checkbox) that triggers the count change\n- Multiple count elements can share the same target\n\n**Initial State:**\n- Count displays the `min` value initially\n- When toggle changes, count animates to `max` value\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-count` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-count` | Count display element | - | - | Activate a Toggle Count by specifying on an element. Should be added to the container. |\n| `:target` | Inside `data-hs-toggle-count` | string (CSS selector) | - | Determines which element will be observed. This must be a valid selector pointing to the toggle element (radio buttons or checkbox). Required. |\n| `:min` | Inside `data-hs-toggle-count` | number | `0` | Specifies default number (starting value). |\n| `:max` | Inside `data-hs-toggle-count` | number | `0` | Specifies the number to which the count up will go (ending value). |\n| `:duration` | Inside `data-hs-toggle-count` | number | `700` | Counting speed (animation duration in milliseconds). |\n\n**Example:**\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 10,\n  \"max\": 50,\n  \"duration\": 1000\n}'>\n  10\n</span>\n```\n\n## JavaScript API\n\nThe `HSToggleCount` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle count instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `countUp()` | None | `void` | Force count up animation. Animates from `min` to `max` value. |\n| `countDown()` | None | `void` | Force count down animation. Animates from `max` to `min` value. |\n| `destroy()` | None | `void` | Destroys the toggle count instance, removes all generated markup, classes, and event listeners. Use when removing toggle count from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSToggleCount` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSToggleCount.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSToggleCount \\| { id: string \\| number, element: HSToggleCount } \\| null` | Returns the toggle count instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSToggleCount` instance. If `isInstance` is `false` or omitted, returns the `HSToggleCount` instance directly. Returns `null` if toggle count instance is not found. |\n\n### Usage Examples\n\n**Example 1: Force count up**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const countUpBtn = document.querySelector('#hs-count-up-btn');\n\n  countUpBtn.addEventListener('click', () => {\n    element.countUp();\n  });\n}\n```\n\n**Example 2: Force count down**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = element;\n  const countDownBtn = document.querySelector('#hs-count-down-btn');\n\n  countDownBtn.addEventListener('click', () => {\n    element.countDown();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Min value:', element.min);\n  console.log('Max value:', element.max);\n  console.log('Duration:', element.duration);\n  console.log('Is checked:', element.isChecked);\n\n  // Clean up when removing from DOM\n  function removeToggleCount() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle count instance**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-count').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Pricing Toggle\n\nToggle between monthly and annual pricing.\n\n```html\n<div id=\"hs-pricing-toggle\">\n  <input type=\"radio\" name=\"pricing\" id=\"monthly\" checked>\n  <input type=\"radio\" name=\"pricing\" id=\"annual\">\n</div>\n\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-pricing-toggle\",\n  \"min\": 19,\n  \"max\": 199\n}'>\n  $19\n</span>\n```\n\n### Pattern 2: Custom Duration\n\nSet custom animation duration.\n\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 0,\n  \"max\": 100,\n  \"duration\": 2000\n}'>\n  0\n</span>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/toggle-password/README.md":"# Toggle Password\n\nShow or hide password field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Password component provides a button to toggle password visibility in input fields. It allows users to switch between showing and hiding password text, improving usability while maintaining security.\n\n**Key Features:**\n- Toggle password visibility\n- Show/hide password text\n- Support for multiple password fields\n- Grouped toggle support\n- Programmatic control via JavaScript API\n- Event system for visibility tracking\n\n## Installation\n\nTo get started, install Toggle Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-password\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-password */\n@import \"./node_modules/@preline/toggle-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTogglePassword from \"@preline/toggle-password/non-auto.mjs\";\n\n  new HSTogglePassword(document.querySelector(\"#toggle-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-password\";\n```\n\n`@preline/toggle-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTogglePassword from \"@preline/toggle-password/non-auto\";\n\nHSTogglePassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-password\");\nif (el) new HSTogglePassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles password visibility.\n\n```html\n<div class=\"relative\">\n  <input id=\"hs-toggle-password-first\" type=\"password\" name=\"password\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-400\" value=\"12345qwerty\" required>\n  <button type=\"button\" data-hs-toggle-password='{\n      \"target\": \"#hs-toggle-password-first\"\n    }' class=\"absolute inset-y-0 end-0 flex items-center pointer-events-none z-10 p-3.5\">\n    <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n      <path class=\"hs-password-active:hidden\" d=\"M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"/>\n    </svg>\n    <span class=\"hs-password-active:hidden\">\n      <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n        <path d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"></path>\n        <path d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"></path>\n      </svg>\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-password`: Required on the toggle button, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Toggle button can be positioned anywhere (commonly inside a relative container)\n\n**Initial State:**\n- Password input is hidden (type=\"password\")\n- Toggle button shows \"show\" icon\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-password` | Toggle button | - | - | Activate a Toggle Password by specifying on an element. Should be added to the button (trigger). |\n| `:target` | Inside `data-hs-toggle-password` | string (CSS selector) | - | Determines which element will change type on click. This must be a valid CSS selector pointing to the password input field. Required. |\n| `:isShown` | Inside `data-hs-toggle-password` | boolean | `false` | Determines password visibility initially. When `true`, password is shown by default. |\n| `:eventType` | Inside `data-hs-toggle-password` | `'change'` \\| `'click'` | `'click'` | Determines event type. `click` for button clicks, `change` for checkbox/radio changes. |\n| `data-hs-toggle-password-group` | Container element | - | - | This option allows you to group multiple toggle password elements together so that they can be toggled simultaneously. It should be added to the parent element of the toggle password elements. Should be added to the container. |\n\n**Example:**\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": false,\n  \"eventType\": \"click\"\n}'>\n  Toggle\n</button>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-password-active:*` | A modifier that allows you to set Tailwind classes when the password was shown. |\n\n## JavaScript API\n\nThe `HSTogglePassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Toggle the field to type text (show password). Changes input type from `password` to `text`. |\n| `hide()` | None | `void` | Toggle the field to type password (hide password). Changes input type from `text` to `password`. |\n| `destroy()` | None | `void` | Destroys the toggle password instance, removes all generated markup, classes, and event listeners. Use when removing toggle password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTogglePassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTogglePassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTogglePassword \\| { id: string \\| number, element: HSTogglePassword } \\| null` | Returns the toggle password instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTogglePassword` instance. If `isInstance` is `false` or omitted, returns the `HSTogglePassword` instance directly. Returns `null` if toggle password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Showing password programmatically**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n}\n```\n\n**Example 2: Hiding password programmatically**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const hideBtn = document.querySelector('#hs-hide-btn');\n\n  hideBtn.addEventListener('click', () => {\n    element.hide();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Is shown:', element.isShown);\n  console.log('Target:', element.target);\n\n  // Clean up when removing from DOM\n  function removeTogglePassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle password instance**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-password').remove();\n  });\n}\n```\n\n## Events\n\nToggle password instances emit events that can be listened to for visibility tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:toggle` | When password was shown or hidden | `HTMLElement` (target input element) | Fires when password visibility changes. Returns the target password input element. |\n\n### Event Usage Example\n\n```javascript\n// Get toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to toggle event\n  element.on('toggle', (target) => {\n    console.log('Password visibility toggled:', target);\n    // Perform actions after password visibility changes\n    // e.g., track analytics, update UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Grouped Toggle\n\nGroup multiple password fields to toggle simultaneously.\n\n```html\n<div data-hs-toggle-password-group>\n  <input type=\"password\" id=\"password-1\">\n  <button data-hs-toggle-password='{\"target\": \"#password-1\"}'>Toggle</button>\n  \n  <input type=\"password\" id=\"password-2\">\n  <button data-hs-toggle-password='{\"target\": \"#password-2\"}'>Toggle</button>\n</div>\n```\n\n### Pattern 2: Initially Shown\n\nShow password by default.\n\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": true\n}'>\n  Toggle\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tooltip/README.md":"# Tooltip & Popover\n\nA tooltip is a floating, non-actionable label used to explain a user interface element or feature.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tooltip) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tooltip.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tooltip component provides contextual information when users hover over or focus on an element. It supports multiple placement options, customizable triggers, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (hover, focus)\n- Floating UI integration for smart positioning\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility support (ARIA attributes)\n\n## Installation\n\nTo get started, install Tooltip & Popover plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tooltip\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tooltip */\n@source \"../node_modules/@preline/tooltip/*.js\";\n@import \"./node_modules/@preline/tooltip/variants.css\";\n@import \"./node_modules/@preline/tooltip/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tooltip/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTooltip from \"@preline/tooltip/non-auto.mjs\";\n\n  new HSTooltip(document.querySelector(\"#tooltip\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tooltip` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tooltip\";\n```\n\n`@preline/tooltip/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTooltip from \"@preline/tooltip/non-auto\";\n\nHSTooltip.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tooltip\");\nif (el) new HSTooltip(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tooltip component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The tooltip appears when hovering over the button.\n\n```html\n<div class=\"hs-tooltip [--placement:auto] inline-block\">\n  <button type=\"button\" class=\"hs-tooltip-toggle\">\n    Hover me\n    <span class=\"hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-gray-900 text-white\" role=\"tooltip\">\n      Tooltip on top\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-tooltip`: Required container wrapper\n- `hs-tooltip-toggle`: Required element that triggers the tooltip (button, link, etc.)\n- `hs-tooltip-content`: Required element containing the tooltip content\n- Proper ARIA attributes (`role=\"tooltip\"`)\n\n## Configuration Options\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control tooltip behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--trigger:*]` | `hs-tooltip` (container) | `\"focus\"` \\| `\"hover\"` | `\"hover\"` | Event to trigger a tooltip. `hover` shows on mouse enter, `focus` shows on focus. |\n| `[--placement:*]` | `hs-tooltip` (container) | `\"auto\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"auto\"` (top → bottom, left → right) | Tooltip placement relative to the trigger element. If `\"auto\"`, the tooltip will be positioned based on available space. |\n| `[--strategy:*]` | `hs-tooltip` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"absolute\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--prevent-popper:*]` | `hs-tooltip` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Prevents Floating UI positioning calculation issues. Set to `\"true\"` if experiencing positioning problems. |\n\n**Example:**\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the tooltip to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-tooltip` | Container wrapper | Groups the toggle and content elements together |\n| `hs-tooltip-toggle` | Trigger element | The element that activates the tooltip (button, link, etc.) |\n| `hs-tooltip-content` | Content element | The tooltip content that appears on trigger |\n\n## JavaScript API\n\nThe `HSTooltip` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tooltip instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Forces the tooltip to show programmatically. |\n| `hide()` | None | `void` | Forces the tooltip to hide programmatically. |\n| `destroy()` | None | `void` | Destroys the tooltip instance, removes all generated markup, classes, and event listeners. Use when removing tooltip from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTooltip` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTooltip.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSTooltip } \\| HTMLElement \\| null` | Returns the tooltip instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTooltip` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tooltip instance is not found. |\n| `HSTooltip.show(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSTooltip.hide(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new tooltip instance\nconst tooltip = new HSTooltip(document.querySelector('#hs-tooltip'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip programmatically\nshowBtn.addEventListener('click', () => {\n  tooltip.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSTooltip.show(document.querySelector('#hs-tooltip'));\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSTooltip instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeTooltip() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying tooltip instance**\n```javascript\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tooltip').remove();\n  });\n}\n```\n\n## Events\n\nTooltip instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:show` | After tooltip is shown | `HSTooltip` (tooltip instance) | Fires after the tooltip has been displayed. Use for post-show actions like tracking analytics. |\n| `on:hide` | After tooltip is hidden | `HSTooltip` (tooltip instance) | Fires after the tooltip has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to show event\n  element.on('show', (tooltipInstance) => {\n    console.log('Tooltip shown:', tooltipInstance);\n    // Perform actions after tooltip shows\n    // e.g., track analytics, update UI\n  });\n\n  // Listen to hide event\n  element.on('hide', (tooltipInstance) => {\n    console.log('Tooltip hidden:', tooltipInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Focus-based Tooltip\n\nShow tooltip on focus for keyboard accessibility.\n\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Helpful information</span>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tooltip from external buttons.\n\n```html\n<div class=\"hs-tooltip\" id=\"hs-tooltip-first\">\n  <button class=\"hs-tooltip-toggle\">Hover me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n\n<button id=\"hs-show-tooltip\">Show Tooltip</button>\n<button id=\"hs-hide-tooltip\">Hide Tooltip</button>\n\n<script>\n  const instance = HSTooltip.getInstance('#hs-tooltip-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-tooltip').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-tooltip').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","src/plugins/tree-view/README.md":"# Tree View\n\nTree View solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tree-view) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tree-view.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tree View component provides a hierarchical structure for displaying nested data, similar to file explorers. It integrates with the Accordion component to create expandable/collapsible tree nodes and supports both checkbox and button selection modes.\n\n**Key Features:**\n- Hierarchical nested structure\n- Expandable/collapsible nodes (using Accordion)\n- Multiple selection modes (checkbox, button)\n- Auto-select children option for directories\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tree View plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tree-view @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n\n/* @preline/tree-view */\n@source \"../node_modules/@preline/tree-view/*.js\";\n@import \"./node_modules/@preline/tree-view/variants.css\";\n@import \"./node_modules/@preline/tree-view/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n<script src=\"./node_modules/@preline/tree-view/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. Tree View depends on Accordion for expand/collapse behavior, so call `HSAccordion.autoInit()` first and then initialize the specific Tree View element manually.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n  import HSTreeView from \"@preline/tree-view/non-auto.mjs\";\n\n  HSAccordion.autoInit();\n  new HSTreeView(document.querySelector(\"#tree-view\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. Initialize `HSAccordion` first, since it powers the expand/collapse nodes inside the tree.\n\nThe first example uses the auto-init entries. This means the plugins scan the DOM and initialize matching elements automatically.\n\nThe second example uses the manual `non-auto` entries. Use this when you want explicit control over initialization order and timing, or when you need to initialize a specific Tree View instance yourself.\n\n```js\nimport \"@preline/accordion\";\nimport \"@preline/tree-view\";\n```\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\nimport HSTreeView from \"@preline/tree-view/non-auto\";\n\n// Initialize all matching accordion nodes first\nHSAccordion.autoInit();\n\n// Then initialize all matching tree views\nHSTreeView.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tree-view\");\nif (el) new HSTreeView(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tree view component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows a directory structure with expandable folders and files.\n\n```html\n<div role=\"tree\" aria-orientation=\"vertical\" data-hs-tree-view>\n  <div class=\"hs-accordion active\" role=\"treeitem\" aria-expanded=\"true\" id=\"hs-tree-view-heading-first\" data-hs-tree-view-item='{\n    \"value\": \"assets\",\n    \"isDir\": true\n  }'>\n    <div class=\"hs-accordion-heading\">\n      <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-tree-view-collapse-first\">\n        <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path class=\"hs-accordion-active:hidden block\" d=\"M12 5v14\"/></svg>\n      </button>\n      assets\n    </div>\n\n    <div id=\"hs-tree-view-collapse-first\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"group\" aria-labelledby=\"hs-tree-view-heading-first\">\n      <div role=\"treeitem\" data-hs-tree-view-item='{\n        \"value\": \"image.jpg\"\n      }'>image.jpg</div>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-tree-view`: Required on the root container element\n- `role=\"tree\"`: Required on the root container\n- `aria-orientation=\"vertical\"`: Required on the root container\n- `hs-accordion`: Required for each directory/folder node\n- `data-hs-tree-view-item`: Required on each tree item (files and directories)\n- Proper ARIA attributes (`role=\"treeitem\"`, `aria-expanded`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- Directories can be expanded/collapsed using accordion functionality\n- Items can be selected based on the `controlBy` mode\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-tree-view` and `data-hs-tree-view-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tree-view` | Root container | - | - | Activate a Tree View by specifying on an element. Should be added to the wrapper container. |\n| `:controlBy` | Inside `data-hs-tree-view` | `\"checkbox\"` \\| `\"button\"` | `\"button\"` | Tells the plugin which mode is active and processes events accordingly. `checkbox` mode allows multiple selections, `button` mode allows single selection. |\n| `:autoSelectChildren` | Inside `data-hs-tree-view` | boolean | `false` | This option is available if `controlBy` is set to `checkbox` and if the item is a directory (`isDir: true`). When `true`, if a parent directory is selected, all nested items receive the same selection state. |\n| `:isIndeterminate` | Inside `data-hs-tree-view` | boolean | `true` | Adds indeterminate visual style for checkboxes when `controlBy` is set to `checkbox`. Shows when some but not all children are selected. |\n| `data-hs-tree-view-item` | Each tree item | - | - | Determines which element inside the initialized component is the item. Should be added to the item itself. |\n| `:id` | Inside `data-hs-tree-view-item` | string | - | Optional. Desired identifier for the item. |\n| `:value` | Inside `data-hs-tree-view-item` | string | - | The value that will be passed to the resulting array when item is selected. |\n| `:isDir` | Inside `data-hs-tree-view-item` | boolean | `false` | Determines that the element is a directory and processes it accordingly. Directories can contain nested items. |\n\n**Example:**\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true,\n  \"isIndeterminate\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tree-view-selected:*` | A modifier that allows you to set Tailwind classes when item has been selected. |\n| `hs-tree-view-disabled:*` | A modifier that allows you to set Tailwind classes when item has \"disabled\" class. |\n\n## JavaScript API\n\nThe `HSTreeView` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tree view instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `update()` | None | `void` | Updates the element. This is suitable, for example, in case of changing the order of elements or dynamically adding/removing items. |\n| `getSelectedItems()` | None | `ITreeViewItem[]` | Returns a list of selected items as an array of objects with item properties. |\n| `changeItemProp(id, prop, val)` | `id`: `string`<br>`prop`: `string`<br>`val`: `any` | `void` | Changes a property of a specific item. Useful for updating item state programmatically. |\n| `destroy()` | None | `void` | Destroys the tree view instance, removes all generated markup, classes, and event listeners. Use when removing tree view from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTreeView` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTreeView.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSTreeView } \\| null` | Returns the tree view instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTreeView` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tree view instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting selected items**\n```javascript\n// Get the tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get all selected items\n  const selectedItems = element.getSelectedItems();\n\n  console.log('Selected items:', selectedItems);\n}\n```\n\n**Example 2: Updating tree view after dynamic changes**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // After adding or removing items dynamically\n  element.update();\n}\n```\n\n**Example 3: Changing item properties programmatically**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Change item property\n  element.changeItemProp('item-id', 'isSelected', true);\n}\n```\n\n**Example 4: Destroying tree view instance**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tree-view').remove();\n  });\n}\n```\n\n## Events\n\nTree view instances emit events that can be listened to for selection tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:click` | When any item is selected | `{ id: string, value: string, isDir: boolean, path: string, isSelected: boolean }` | Fires when an item is clicked/selected. Returns an object with:<br>- `id`: Item identifier<br>- `value`: Item value<br>- `isDir`: Whether item is a directory<br>- `path`: Path to the item<br>- `isSelected`: Whether item is currently selected |\n\n### Event Usage Example\n\n```javascript\n// Get tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\nif (instance) {\n  const { element } = instance;\n\n  // Listen to click event\n  element.on('click', ({ id, value, isDir, path, isSelected }) => {\n    console.log('Item clicked:', {\n      id,\n      value,\n      isDirectory: isDir,\n      path,\n      isSelected\n    });\n    // Perform actions after item selection\n    // e.g., update UI, track analytics, load content\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Checkbox Mode with Auto-select Children\n\nEnable checkbox selection with automatic child selection.\n\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Pattern 2: Getting Selected Items\n\nRetrieve all selected items programmatically.\n\n```html\n<div id=\"hs-tree-view-first\" data-hs-tree-view>\n  <!-- Tree items -->\n</div>\n\n<button id=\"hs-get-selected\">Get Selected</button>\n\n<script>\n  const instance = HSTreeView.getInstance('#hs-tree-view-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-get-selected').addEventListener('click', () => {\n      const selected = element.getSelectedItems();\n      \n      console.log('Selected items:', selected);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n"},"items":[{"name":"openai.yaml","path":"skills/preline-mcp/agents/openai.yaml","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/preline-mcp/agents/openai.yaml","title":"Agents Skill","category":"anthropic-skill","format":"yaml","content":"interface:\n  display_name: \"Preline MCP\"\n  short_description: \"Add Preline UI components and blocks to HTML files via the Preline MCP server\"\n  default_prompt: \"Use $preline-mcp to add Preline UI components or blocks to the target HTML file via the Preline MCP server. Call components_list first to discover valid section and component slugs, then single_component to get the HTML/CSS/JS, then integrate into the target file.\"\n","isInternal":false,"tokens":96,"sizeBytes":427},{"name":"catalog-map.md","path":"skills/preline-mcp/references/catalog-map.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/preline-mcp/references/catalog-map.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Catalog Map - where intents and elements live\n\n> Generated from the catalog (blocks.json, components.json, constants.ts). Regenerate with `node scripts/gen-catalog-map.mjs`.\n> This is a **routing map**, not authority - always confirm exact slugs against live `blocks_categories` / `components_list` output before inserting.\n\nUse during the **Discover** step (SKILL.md → Composite & Layout Requests): map an abstract intent to a blocks branch, and each named element to a component section.\n\n---\n\n## Blocks - intent ontology (for whole pages / regions / shells)\n\nAbstract request (\"a pricing page\", \"a login screen\", \"an admin shell\", \"a checkout flow\")? Route to a branch, then `blocks_in_category({ mainSection, subSection, category })`. Whole app shells live under **Navigation & Layout → Sidebars & Shells**.\n\n- **Marketing** - Hero, Banners & Landing Intros · Site Navigation & Headers · Trust & Social Proof · Content & Editorial · Pricing & Conversion · Website Foundations\n- **Data Display** - Analytics & KPIs · Operational & Activity Views · Tables & Records\n- **UI Components** - Menus & Selection · Actions, Status & Progress · Lists & Links\n- **Forms** - Core Inputs & Layouts · Creation & Management · Filters & Refinement · Billing, Feedback & Conversion\n- **Authentication** - Sign In & Account Access · Onboarding & Activation\n- **Ecommerce** - Product Discovery · Product Detail & Purchase · Orders & Commerce Utilities\n- **Communication** - Chat & Inbox · Calls & Media · Contacts & Presence\n- **Productivity** - Calendars & Scheduling · Planning Boards\n- **Blog & Articles** - Editorial Pages · Blog Discovery & Sections\n- **Application Pages** - Workspaces & Admin · Profiles & Accounts\n- **Billing & Payments** - Billing & Payments\n- **Feedback & States** - Alerts & Recovery · Actions & Engagement · Consent & Compliance\n- **Search & Command Palettes** - Search & Navigation · Actions & Shortcuts\n- **Navigation & Layout** - Application Headers & Navigation · Sidebars & Shells · Title Bars & Footers · Blank, Error & Utility Layouts\n\n---\n\n## Components - section index (for individual elements)\n\nPass the section to keep the list small: `components_list({ section: \"<slug>\" })`. 📖 = has a `component_documentation` entry (JS API). Any interactive component still needs the Preline core script wired once.\n\n**Form inputs & controls:** `input` · `input-group` · `input-number`📖 · `textarea` · `select` · `advanced-select`📖 · `combobox`📖 · `searchbox` · `checkbox` · `radio` · `switch` · `color-picker` · `range-slider` · `advanced-range-slider`📖 · `datepicker` · `advanced-datepicker`📖 · `time-picker` · `pin-input`📖 · `strong-password`📖 · `toggle-password`📖 · `file-input` · `file-upload`📖 · `file-uploading-progress-form`\n\n**Menus & overlays:** `dropdown`📖 · `mega-menu` · `context-menu` · `modal` · `offcanvas` · `popover` · `tooltip`📖\n\n**Navigation:** `navbar` · `navs` · `sidebar` · `breadcrumb` · `pagination` · `tabs`📖 · `stepper`📖 · `scrollspy`📖 · `tree-view`📖\n\n**Feedback & status:** `alerts` · `toasts` · `toast-notifications` · `badge` · `progress` · `ratings` · `spinners` · `skeleton` · `legend-indicator`\n\n**Data display:** `tables` · `datatables` · `charts` · `datamaps` · `maps` · `list-group` · `lists` · `timeline`\n\n**Content & media:** `card` · `avatar` · `avatar-group` · `blockquote` · `images` · `carousel`📖 · `marquee` · `devices` · `chat-bubbles` · `text-editor` · `typography` · `static-icons`\n\n**Actions & misc:** `buttons` · `button-group` · `links` · `dividers` · `kbd` · `clipboard` · `copy-markup`📖 · `toggle-count`📖 · `dark-mode` · `confetti` · `drag-and-drop` · `collapse`📖 · `accordion`📖\n\n**Layout:** `container` · `grid` · `columns` · `layout-splitter`📖 · `custom-scrollbar`\n\n","isInternal":false,"tokens":1014,"sizeBytes":3907},{"name":"composite-layouts.md","path":"skills/preline-mcp/references/composite-layouts.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/preline-mcp/references/composite-layouts.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Composite & Layout Requests - Detailed Guide\n\nHow to handle any request bigger than a single component - multiple pieces, a region, a full page, an app shell, a wizard. Companion to `SKILL.md` → \"Composite & Layout Requests\".\n\nThe point: **the same five steps apply regardless of domain.** A dashboard shell, an ecommerce product page, a settings screen, a multi-step form, a marketing section, or \"a card with X, Y and Z\" all decompose the same way. Apply the method and let the catalog be the source of truth.\n\nThe mistake to avoid: firing `single_component` / `single_block` ad-hoc and stitching markup as you go. Plan the whole layout first; fetch and integrate one node at a time against that plan.\n\n---\n\n## The method\n\n### 1. Lock cross-cutting constraints first\nResolve anything that applies to the **whole** result before any fetch, because it governs every fetch and edit:\n- **Class system** - design tokens vs utilities. Drives `isUtilityBased` / `theme` on every call; keep it identical across the whole build.\n- **Shared surfaces / color / spacing / density** - anything the request describes as uniform across regions.\n- **Repeated element states** - pinned, selected, active, disabled, collapsed, aligned, icon-only, etc., noted against the element they modify (they change which variant you pick).\n\nDeciding these once prevents inconsistent output halfway through.\n\n### 2. Decompose into a tree\nContainers → regions → leaf components. Write it down. Each leaf is one fetch; grouping leaves by region lets you integrate a region at a time. The shape of the method is identical whether the request is two nested elements or a fifty-element page.\n\n### 3. Discover every node - never guess\n- **Larger structures** (shells, navbars, headers, full sections, pages) → `blocks_categories()`. If a ready-made block already covers the structure, reuse it as the skeleton instead of hand-building.\n- **Individual pieces** → `components_list({ section })`, then `single_component`.\n- Resolve **each named element in the request** to a real slug from the tool output. The \"Where things tend to live\" table below is only a starting point for *which section to open* - the catalog is authoritative, so confirm against its output and never insert an unverified slug.\n\n### 4. Assemble in order - one fetch + integrate at a time\n1. **Outermost / skeleton first.** The shell or top container sets the shared surfaces and - importantly - the script/init placement anchors for everything inside it.\n2. **Fill inward, region by region, leaf by leaf.** Integrate each fully (HTML, CSS in `<head>`, external scripts at the structural anchor, init before `</body>` - see Integration Rules) before fetching the next.\n3. **Honor the locked constraints on every insert.** Keep the classes the MCP returns and change **text only** - except for the specific surfaces/colors/states the user explicitly asked to change.\n4. **Plugins once for the page.** Interactive components share the Preline core script - add it and the init per the placement rules a single time; never duplicate the core script. For a plugin's JS API, events, or options, call `component_documentation`.\n\n### 5. Verify\nRe-read the request and check every named element, state, and constraint is present. Composite requests are easy to under-deliver; the checklist is the guard. Verify **against the request, not by auditing the generated code** - do not grep the project's CSS to confirm token classes exist, and do not run HTML/AST validators over the result (see SKILL.md → Integration Rules). The markup the MCP returned is valid by construction; the only open question is whether you placed every element the user asked for.\n\n---\n\n## Where things live\n\nA request describes UI in plain language; you still discover the real slug. To route an abstract intent (a whole page/region/shell) to the right blocks branch, and each named element to the right component section, use the generated **[catalog-map.md](catalog-map.md)** - it lists the block intent ontology and all component sections grouped by purpose (JS-plugin marked). It's a starting-point map, not authority: always confirm the exact slug against live `blocks_categories` / `components_list` output before inserting.\n\n---\n\n## On surfaces and tokens\n\nWhen the user wants semantic / design-token classes (not raw utilities), keep `isUtilityBased: false` and **preserve the token classes the MCP output already uses** - Preline returns semantic surface tokens, not hardcoded colors. Translate plain-language surface words to the matching tokens you see in the returned markup; do not substitute `bg-white` / `bg-gray-*` / hex, and do not invent token names.\n\nSurface tokens you'll commonly see in the output (recognize and reuse them; exact names can vary by version - match what the catalog emits):\n\n| Plain wording | Token family to look for |\n|---|---|\n| page background | `bg-background` |\n| panel / \"one gray part\" shared by regions | a muted/secondary surface, e.g. `bg-muted` / `bg-secondary` |\n| card / \"layer\" - white with borders | `bg-layer` + `border border-layer-line` (text `text-layer-foreground`) |\n| inset / control surface | `bg-surface` |\n| separators between regions | the matching `*-line` border token |\n\n---\n\n## Worked illustration (one shape of many)\n\nThe five steps are domain-agnostic. The same decomposition covers, for example:\n- **App shell** - outer shell (shared surface) → navbar region(s) → sidebar region → content (distinct surface).\n- **Product page** - gallery → details (title, price, variant pickers, actions) → tabs / reviews.\n- **Settings page** - section nav → form sections → save bar.\n- **Multi-step form** - stepper → step panels → footer actions.\n\nA fuller pass for an app-shell request such as *\"a navbar with a logo, a sidebar toggle, project / team / account dropdowns and dividers, links and a theme switcher; a left sidebar with search and links; navbar + sidebar as one gray panel, content as a white bordered layer; use theme-token classes\":*\n\n**1. Constraints (locked first):**\n- \"theme-token classes\" → `isUtilityBased: false`, no theme passed - and the same setting on every later call.\n- \"navbar + sidebar one gray panel\" → both regions share one muted/secondary surface token; no off-color seams.\n- \"content as a white bordered layer\" → content region uses the layer surface tokens (`bg-layer` + border).\n\n**2. Tree:**\n```\nshell (navbar + left sidebar = shared surface) + content (layer surface)\n├─ navbar · left:  logo · sidebar-toggle · project-dropdown {★pinned A, ★pinned B, Add, Manage}\n│                  · divider · team-dropdown {Add team, ✓selected \"Marketing\"}\n├─ navbar · right: button(s) · text/icon links · divider\n│                  · account-dropdown (avatar trigger) {Upgrade CTA, theme switch, icon links}\n├─ sidebar (below navbar): search input · links · (bottom) icon links\n└─ content: layer surface\n```\n\n**3. Discover (resolve every slug from output):**\n1. `blocks_categories()` → look for an Application / Admin / Dashboard / Navigation shell that already pairs a navbar + sidebar + content. If one fits → `single_block(...)` → use as the skeleton.\n2. If none fits, build the skeleton from the `navbar` + `sidebar` sections.\n3. For each remaining leaf, `components_list({ section })` then `single_component` - sections per the orientation table (`dropdown`, `dividers`, `avatar`, `dark-mode`/`switch`, `input`/`searchbox`, `buttons`, `links`).\n\n**4. Assemble (one fetch + integrate at a time):**\n1. Skeleton → establishes the shared gray surface, the layer content container, and the script anchors.\n2. Navbar-left leaves → navbar-right leaves → sidebar leaves, integrating each fully before the next.\n3. Keep returned classes, edit **text only**; apply only the requested surface changes (shared gray shell, layer content). Set per-element states (pinned/selected) using the component's own selected/checkbox variant.\n4. Wire plugin scripts/init **once** (sidebar toggle, dropdowns, theme switch all need the Preline core script - don't duplicate it). Use `component_documentation` for any plugin's JS API.\n\n**5. Verify** against the request: every named element, every state (pinned, selected), and both surface rules (one gray shell, layer content) present.\n","isInternal":false,"tokens":1862,"sizeBytes":8342},{"name":"mcp-tools-reference.md","path":"skills/preline-mcp/references/mcp-tools-reference.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/preline-mcp/references/mcp-tools-reference.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Preline MCP - Tools Reference\n\nFull parameter specs and placement rules for all 6 MCP tools.\n\n---\n\n## Tool 1: `components_list`\n\nBrowse available component sections and their component IDs.\n\n**Input:**\n- `section` (optional string) - pass the inferred section slug to get only that section. Omit to get all sections.\n\n**When to pass section:** whenever the component type is known (e.g. `components_list({ section: \"buttons\" })`). Omitting section loads 300k+ characters - avoid unless you genuinely don't know the section.\n\n**Returns:** JSON with section IDs, component IDs, titles, descriptions, and optional `relative` metadata.\n\n---\n\n## Tool 2: `single_component`\n\nGet HTML/CSS/JS for one Preline component.\n\n**Input:**\n- `section` (required) - exact section slug from `components_list`. Never guess.\n- `component` (required) - exact component ID from `components_list`. Never guess.\n- `isUtilityBased` (optional, default: `false`) - convert design tokens to Tailwind utility classes\n- `theme` (optional, default: `\"default\"`) - theme for utility conversion\n- `parameters` (optional) - template-specific overrides\n\n**Available themes:**\n\n| Slug | Color |\n|---|---|\n| `default` | Blue |\n| `harvest` | Amber |\n| `retro` | Fuchsia |\n| `moon` | Grayscale |\n| `ocean` | Cyan |\n| `bubblegum` | Pink |\n| `cashmere` | Mauve |\n| `autumn` | Amber/Orange |\n| `olive` | Green |\n\n**Examples:**\n```\nsingle_component({ section: \"accordion\", component: \"accordion-basic-usage-example\" })\nsingle_component({ section: \"alerts\", component: \"alerts-solid-color-variants\", isUtilityBased: true })\nsingle_component({ section: \"datatables\", component: \"datatables-basic-usage-example\" })\n```\n\n**Response sections:**\n- `=== COMPONENT HTML ===` - main markup\n- `<!-- CSS -->` - `<link>` and `<style>` tags\n- `<!-- Scripts -->` - `<script src>` tags\n- `<!-- Init -->` - initialization code\n- Large responses are written to a temp file; read via the Read tool\n\n---\n\n## Tool 3: `component_documentation`\n\nGet JavaScript API docs for plugin components.\n\n**Input:**\n- `component` (required) - plugin component name from the documented list\n\n**Available documented components:**\naccordion, advanced-datepicker, advanced-range-slider, advanced-select, carousel, collapse, combobox, copy-markup, datatable, dropdown, file-upload, input-number, layout-splitter, overlay, pin-input, remove-element, scroll-nav, scrollspy, stepper, strong-password, tabs, textarea-autoheight, theme-switch, toggle-count, toggle-password, tooltip, tree-view\n\n**Returns:** API methods, events, data attributes, configuration options, integration examples.\n\n---\n\n## Tool 4: `blocks_categories`\n\nList all block categories in a hierarchy.\n\n**Input:** none\n\n**Returns:** `mainSection → subSection → category` tree with block counts per category.\n\n**Call first** when the user asks for blocks (ready-made page sections; users may also say \"examples\") or references a broad category.\n\n---\n\n## Tool 5: `blocks_in_category`\n\nList block IDs within one category.\n\n**Input:**\n- `mainSection` (required) - top-level slug from `blocks_categories`\n- `subSection` (required) - subsection slug from `blocks_categories`\n- `category` (required) - category slug from `blocks_categories`\n\n**Returns:** stable block IDs and slug/file mappings. Use IDs to call `single_block`.\n\n---\n\n## Tool 6: `single_block`\n\nGet complete integration-ready HTML/CSS/JS for one block.\n\n**Input:**\n- `mainSection` (required)\n- `subSection` (required)\n- `category` (required)\n- `block` (required) - exact ID from `blocks_in_category`. Accepts comma/newline-separated list (first is returned, rest marked as pending).\n- `context` (optional) - insertion hint\n- `isUtilityBased` (optional, default: `false`)\n- `theme` (optional, default: `\"default\"`)\n\n**Returns:** full block HTML with CSS/JS sections.\n\n---\n\n## Integration Placement Rules\n\n### CSS (`<!-- CSS -->`)\n\n```\n✅ CORRECT\n<head>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← immediately before </head>\n</head>\n\n❌ WRONG\n<body>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← NEVER in <body>\n</body>\n```\n\n### External Scripts (`<!-- Scripts -->`)\n\nThe `<!-- Scripts -->` label is a **response section marker only** - it does NOT indicate where to insert in the target file.\n\n**Placement algorithm:**\n\n1. Open the target file\n2. Starting from `</body>`, scan **upward** line by line\n3. Skip: blank lines, HTML comments (`<!-- ... -->`), non-structural tags (`</script>`, `</style>`, `</link>`, `</noscript>`), any non-HTML syntax\n4. The **first structural closing tag** reached (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`, `</header>`, `</aside>`) is the **anchor**\n5. Insert ALL `<script src>` tags on the line **immediately after** that anchor - nothing between the anchor and the scripts\n\n```html\n<!-- ✅ CORRECT - anchor is </main> -->\n</main>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ✅ CORRECT - anchor is </section> -->\n</section>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ❌ WRONG - non-structural content between anchor and scripts -->\n</main>\n<!-- any comment -->\n<script src=\"lib.js\"></script>\n</body>\n```\n\n### Init (`<!-- Init -->`)\n\n```html\n<!-- Place immediately before </body> -->\n<!-- Wrap in window.addEventListener unless block already contains <script> tags -->\n\n<script>\nwindow.addEventListener('load', () => {\n  // init code here\n});\n</script>\n</body>\n```\n\nIf `<!-- Init -->` already contains `<script>` tags - insert as-is, do NOT wrap again.\n\n---\n\n## Smart Component Selection\n\n`components_list` may include a `relative` object per component:\n\n| Field | Meaning | When to prefer |\n|---|---|---|\n| `isSectionDefault: true` | Recommended showcase for the whole section | User request is vague, no specific variant mentioned |\n| `category: \"<slug>\"` | Logical group (e.g. `\"color-variants\"`, `\"states\"`) | Use to filter by style group |\n| `isCategoryDefault: true` | Recommended default within its category | Request implies a style group (e.g. \"icon button\", \"loading state\") |\n\n**Exception for `isSectionDefault`:** if the component description mentions \"multiple variants side by side\" and the user wants a single element - skip it and pick a single-variant component. If no single-variant exists, use the default but extract only the one matching variant from the HTML.\n\n---\n\n## General Rules\n\n- Customize **text content only** - never CSS classes or HTML structure\n- Remove HTML comments before inserting into the target file\n- For complex components (accordion, table, tree), insert the full structure\n- For simple elements (button, input), insert one instance unless multiple are explicitly requested\n","isInternal":false,"tokens":1613,"sizeBytes":6631},{"name":"SKILL.md","path":"skills/preline-mcp/SKILL.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/preline-mcp/SKILL.md","title":"preline-mcp","category":"anthropic-skill","format":"markdown","content":"---\nname: preline-mcp\ndescription: Add, integrate, or build Preline UI components and blocks into HTML files via the Preline MCP server. Use when the user mentions \"preline\", says \"/preline\", asks to add a UI component, or requests a Preline block (a ready-made page section) using Preline UI.\n---\n\n# Preline MCP\n\n## Intent\n\nUse this skill to integrate Preline UI components and blocks into HTML files via the Preline MCP server. The server exposes 6 tools that return integration-ready HTML, CSS, and JS.\n\nActivate when:\n- User says \"preline\", \"add preline\", \"/preline\", \"use preline\"\n- User asks for a Tailwind CSS component from the Preline library\n- User asks for a block (ready-made page section) or a starter page\n\n## Defaults\n\n- **Never guess slugs.** All section, component, and category identifiers are exact kebab-case strings. Always discover them via `components_list` or `blocks_categories` first.\n- **One at a time.** Retrieve one component or block, integrate it fully, then move to the next.\n- **Always read the target file** before inserting anything.\n- Call `components_list({ section: \"<inferred-slug>\" })` when the component type is known - this keeps the response small. Omit section only when unsure.\n\n## Decision: Component vs Block\n\n| User says | Use |\n|---|---|\n| \"component\" / \"components\" | Component workflow |\n| \"block\" / \"blocks\" (or \"example\" / \"examples\") | Block workflow |\n| Ambiguous | Use judgment; blocks give more complete results |\n\n## Clarifying Vague Requests\n\nAsk the user to pick instead of guessing in either of these cases:\n\n- **Abstract request.** Too abstract to confidently map onto one category/section, let alone one block/component - e.g. \"give me something for a pricing page\", \"I need a nice form\", \"show me a dashboard example\". Start at step 1 below.\n- **Tied candidates.** The request is concrete enough to reach a single category/section, but `blocks_in_category` / `components_list({ section })` still leaves 2+ candidates whose titles + descriptions satisfy every criterion the request named about equally well (same layout/feature/style class), with nothing in the request to break the tie. Skip straight to step 3 below with just the tied candidates.\n\nIf exactly one candidate matches everything the request names (\"a basic accordion\", \"the SaaS hero with tabs\"), skip straight to the normal workflow - don't interrogate the user for things you can already resolve, and don't ask just to be safe when one option is clearly the best fit.\n\nAll clarifying questions and lists are in **English by default** - switch to the user's language only once they write to you in it.\n\n**Blocks:**\n1. **Ask which category fits.** Call `blocks_categories()` and present the relevant `mainSection / subSection / category` paths with their titles and descriptions - the catalog carries a one-line description for every category, so present it directly rather than inventing your own summary.\n2. **Try to resolve in one round.** If the user's reply both names a category *and* describes the item distinctively enough to identify a single match (a specific layout, feature, or style), call `blocks_in_category` to confirm the exact ID and go straight to `single_block` - skip step 3 entirely.\n3. **Otherwise, ask which block fits best.** Call `blocks_in_category({ mainSection, subSection, category })`, list the block titles + descriptions - or just the tied candidates, for the case above - and ask the user to pick the one closest to their needs.\n4. **Fetch and integrate** the chosen block with `single_block`.\n\n**Components - same shape, sections instead of categories:**\n1. Ask which **section** fits (`components_list()` for the full list, or `components_list({ section })` once you can infer one).\n2. If the reply names a section *and* describes the component distinctively enough → resolve directly via `single_component` (lean on `relative` metadata - see Smart Component Selection - to land on the right default/variant).\n3. Otherwise list the components in that section (titles + descriptions, with `relative.category` groupings where present - or just the tied candidates, for the case above) and ask which fits best.\n4. Fetch and integrate with `single_component`.\n\nKeep each round to one focused question with a short, scannable list - that beats an open \"what do you want?\" and beats guessing a slug just to avoid asking.\n\n## Composite & Layout Requests\n\nWhen a request describes **more than a single component** - several pieces, a region, a full page, or an app shell - don't fetch ad-hoc. Plan the whole result first, then fetch and integrate one node at a time. The same steps apply to any shape: a dashboard, a settings page, a product page, a multi-step form, \"a card/section/layout with X, Y and Z\", etc.\n\n1. **Lock cross-cutting constraints first.** Anything that applies to the whole result - class system (theme tokens vs utilities → `isUtilityBased` / `theme`), shared surfaces/colors, spacing/density, repeated element states - decide once, up front. Keep it identical across every later fetch and edit.\n2. **Decompose into a tree:** containers → regions → leaf components. Write it down; each leaf is one fetch.\n3. **Discover every node - route, then confirm.** Use [references/catalog-map.md](references/catalog-map.md) to route: an abstract intent (a whole page/region/shell) → the right `blocks_categories` branch (reuse a ready-made block as the skeleton when one fits); each named element → the right `components_list({ section })`. The map only tells you *where to look* - always confirm the exact slug against the tool output before inserting.\n4. **Assemble outermost-first, one at a time.** The skeleton/outer container sets the shared surfaces and the script/init anchors; then fill inward region by region, integrating each fully (Integration Rules) before the next. Keep the returned classes, change **text only** - except the specific surfaces/colors/states the user asked to change. Apply every structural adaptation to the markup **before** writing it (see *Adapt before you write* in Integration Rules).\n5. **Verify** the finished result against the request, element by element.\n\nSee [references/composite-layouts.md](references/composite-layouts.md) for the expanded method and worked illustrations.\n\n## Workflow: Components\n\n```\n1. components_list({ section: \"<slug>\" })   → get valid component IDs\n2. single_component({ section, component }) → get HTML/CSS/JS\n3. Read target file\n4. Integrate HTML, CSS, scripts, init (see Integration Rules)\n5. Repeat from step 1 only after integration is complete\n```\n\n## Workflow: Blocks\n\n```\n1. blocks_categories()                                    → get hierarchy\n2. blocks_in_category({ mainSection, subSection, category }) → get block IDs\n3. single_block({ mainSection, subSection, category, block }) → get HTML/CSS/JS\n4. Read target file\n5. Integrate HTML, CSS, scripts, init (see Integration Rules)\n6. Repeat from step 1 only after integration is complete\n```\n\n## Integration Rules\n\n**HTML** - insert where the user needs it.\n\n**CSS** (`<!-- CSS -->` section):\n- MUST go inside `<head>`, immediately before `</head>`\n- NEVER place in `<body>` or near `</body>`\n\n**External scripts** (`<!-- Scripts -->` section):\n- The `<!-- Scripts -->` label is a response section marker - NOT a location in the target file\n- Placement algorithm: open target file, scan **upward** from `</body>`, skipping blank lines, comments, and non-structural tags (`</script>`, `</style>`, etc.)\n- The **first structural closing tag** you reach (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`) is the anchor\n- Insert `<script src>` tags after that anchor, ordered around the existing Preline core script - the loaded `<script src>` whose `src` contains `preline` (e.g. `…/preline/dist/index.js`; exact path varies by install):\n  - scripts whose `src` does **not** contain `preline` (third-party libs: lodash, apexcharts, …) → **before** the Preline core script\n  - scripts whose `src` **does** contain `preline` (Preline helpers, e.g. `hs-*-helpers.js`) → **after** the Preline core script\n  - if no Preline core script exists yet, keep order: scripts without `preline`, then scripts with `preline`\n  - inline init `<script>` always comes last, immediately before `</body>`\n\n**Init** (`<!-- Init -->` section):\n- Place immediately before `</body>`\n- Wrap in `window.addEventListener('load', () => { ... })` unless the block already contains `<script>` tags\n\n**Large responses (artifacts):** when a response is written to a temp scratch file, read it once with the Read tool (use offset/limit for big files), copy the needed blocks into the target, then delete the scratch file. Don't pull the whole artifact into context if you only need to place it.\n\n**Adapt before you write.** When the request differs from the fetched markup - regions the user didn't ask for (breadcrumbs, demo menus, placeholder logos), different blocks or labels - produce the final markup in memory first, then write it into the target in ONE edit per region. Never insert fetched markup wholesale and refactor it with a chain of follow-up edits: every such edit re-transfers large markup, bloats context, and desyncs file state.\n\n**Same token = same color.** Theme tokens (`bg-navbar`, `bg-sidebar`, `bg-layer`, …) are consistent across a theme - to give two surfaces the same color, give them the same token class. Never resolve tokens to raw colors by reading the project's CSS.\n\n**Trust the returned markup - do not re-verify it.** The classes, structure, and `data-hs-*` attributes the MCP returns are valid Preline by construction. This is the single biggest time-sink to avoid. Do NOT:\n- grep, parse, or scan the user's compiled CSS (e.g. `main.css`) to \"confirm\" a class exists or to resolve what color a design token produces - Preline classes resolve at the consumer's build step, so absence from any one stylesheet means nothing;\n- write HTML/DOM/AST validators (Python `HTMLParser`, tag-balance checkers, etc.) - SVG and void elements trip naive parsers and produce false errors;\n- re-read a placed artifact or re-open the edited file just to \"double-check\" the generated code.\n\nPlace the markup, change text only, move on. **Verify against the request** (is every element the user named present?), never by auditing the generated code or the project's CSS.\n\n## Smart Component Selection\n\n`components_list` may return a `relative` object per component:\n- `isSectionDefault: true` - recommended default for the section; prefer when the request is vague\n  - Exception: if the description says \"multiple variants\" and the user wants one, pick a single-variant component instead\n- `category` - logical group (e.g. `\"color-variants\"`, `\"states\"`)\n- `isCategoryDefault: true` - recommended default for its category; prefer when the request implies a style group\n\n## Available Themes\n\n`default` (blue) · `harvest` (amber) · `retro` (fuchsia) · `moon` (grayscale) · `ocean` (cyan) · `bubblegum` (pink) · `cashmere` (mauve) · `autumn` (orange) · `olive` (green)\n\nPass via `isUtilityBased: true, theme: \"<name>\"` on `single_component` or `single_block`.\n\n## Key References\n\n- [references/catalog-map.md](references/catalog-map.md) - routing map: block intent ontology (whole pages/shells) + all component sections grouped by purpose, JS-plugin marked. Open this to pick the right `section` / blocks branch before any discovery call.\n- [references/composite-layouts.md](references/composite-layouts.md) - full method for multi-component / page / shell requests, with worked illustrations\n- [references/mcp-tools-reference.md](references/mcp-tools-reference.md) - full parameter specs, placement algorithm detail, all 6 tools\n","frontmatter":{"name":"preline-mcp","description":"Add, integrate, or build Preline UI components and blocks into HTML files via the Preline MCP server. Use when the user mentions \"preline\", says \"/preline\", asks to add a UI component, or requests a Preline block (a ready-made page section) using Preline UI."},"isInternal":false,"tokens":2718,"sizeBytes":11759},{"name":"final-output-style.md","path":"skills/theme-generator/docs/final-output-style.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/docs/final-output-style.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Final Output Style\n\nUse this guidance whenever the user wants a final `<name>.css` file for a Preline-based project.\n\n## Core rule\n\nThe deliverable is one file only: `<name>.css`.\n\nDo not create or edit any other project files.\n\n## What the final theme file should look like\n\n- A standalone `<name>.css` file that can live in the project's themes folder.\n- Theme activation via `data-theme=\"theme-<name>\"` and optional `.dark`.\n- Semantic tokens should prefer assigned variables after the theme palette has been mapped.\n\n## Required conventions\n\n- Do not assume project-specific paths other than the discovered themes directory.\n- Do not assume the project already has other custom theme files.\n- Do not force users to change HTML utility classes.\n- Do not create or edit a shared `theme.css`, docs page, or any other file.\n- Keep semantic tokens in the theme selector blocks, not inside `@theme`.\n- Put any custom brand/gray palette needed for this theme inside `@theme theme-<name> inline { }` in the new theme file.\n- If the repo already exposes shared aliases like `--color-primary-*` from its base theme, do not redefine them inside the custom theme file. Assign `--primary-*` directly from the theme-local palette instead.\n\n## Variable strategy\n\nPrefer assigned variables such as:\n\n- `--color-primary-*`\n- `--color-secondary-*`\n- `--background-*`, `--foreground`, `--border`, `--surface-*`, `--muted-*`\n- Tailwind palette vars like `--color-fuchsia-*`, `--color-orange-*`, `--color-neutral-*`\n- shared Preline palette vars exposed by the project's base theme, if available\n\nAllowed in the new theme file:\n\n- theme-local palette vars like `--color-<theme-name>-*`\n- theme-local gray palette vars like `--color-<theme-name>-gray-*`\n\nBut keep them inside `@theme theme-<name> inline { }` and avoid scattering raw palette references when an assigned semantic variable already exists.\n\nAvoid in semantic, chart, and map tokens:\n\n- raw `oklch(...)` assignments\n- literal hex values\n\n## If a unique palette is needed\n\nDefine it inside `@theme theme-<name> inline { }` in the new theme file.\n\nDo not edit any shared/base theme file.\n\n## Structural pattern\n\nUse this order:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector:\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector:\n   - `[data-theme=\"theme-<name>\"].dark`\n\n## Practical workflow\n\n1. Discover the themes folder.\n2. Generate one new `<name>.css` file.\n3. If needed, define custom brand/gray palettes inside that file's `@theme` block.\n4. Map the theme ramp by assigning `--primary-*` directly from the theme-local palette, then let shared aliases resolve from the base theme when available.\n5. Keep chart and map tokens variable-based.\n6. Re-run the validation checklist before returning.\n","isInternal":false,"tokens":685,"sizeBytes":2866},{"name":"palette-guidance.md","path":"skills/theme-generator/docs/palette-guidance.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/docs/palette-guidance.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Palette guidance\n\nUse this file for color-direction heuristics only.\n\nImportant: references below to custom palettes like `--color-<name>-*` or `--color-<name>-gray-*` describe draft palette exploration and shared-palette design patterns. They do NOT override `docs/final-output-style.md`, which defines how the final `<name>.css` file should be structured.\n\n## Contents\n\n- [Hue reference](#hue-reference-for-workflow-normalization)\n- [Mood and palette guidance](#mood-and-palette-guidance)\n- [Accessibility and contrast guidelines](#accessibility-and-contrast-guidelines-wcag)\n- [Palette types](#palette-types)\n- [Custom gray palettes](#custom-gray-palettes-neutral-matching)\n\n## Hue reference (for workflow normalization)\n\n| Color | Hue Range | Tailwind Gray |\n|-------|-----------|---------------|\n| Red/Coral | 0-30 | neutral |\n| Orange/Amber | 30-60 | stone |\n| Yellow/Gold | 60-90 | stone |\n| Green/Lime/Avocado | 90-150 | stone |\n| Teal/Cyan | 150-200 | zinc |\n| Blue/Azure | 200-260 | slate |\n| Violet/Purple | 260-300 | zinc |\n| Magenta/Pink/Rose | 300-360 | neutral |\n\n## Mood and palette guidance\n\n### Color psychology reference\n\n| Mood | Primary Hues | Neutral Family | Characteristics |\n|------|--------------|----------------|-----------------|\n| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |\n| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |\n| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |\n| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |\n| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |\n| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |\n| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |\n| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |\n| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |\n| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |\n\n### Building cohesive palettes\n\n1. Pick a primary: the hero color for buttons, links, CTAs\n2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate\n3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast\n4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family\n\n### Accessibility and contrast guidelines (WCAG)\n\nEnsure themes meet WCAG 2.1 AA contrast requirements:\n\n| Element | Minimum Ratio | OKLCH Rule of Thumb |\n|---------|---------------|---------------------|\n| Body text on background | 4.5:1 | Lightness difference >= 50% |\n| Large text (18pt+) | 3:1 | Lightness difference >= 40% |\n| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |\n| Focus indicators | 3:1 | Use distinct color + lightness |\n\nOKLCH lightness thresholds:\n\n| Context | Light Mode | Dark Mode |\n|---------|------------|-----------|\n| Background | L >= 95% | L <= 25% |\n| Text on light bg | L <= 45% | -- |\n| Text on dark bg | -- | L >= 85% |\n| Primary button text | Check against primary-500/600 | Check against primary-400 |\n\nReadable primary foreground:\n- Do not decide from hue alone.\n- Compare candidate text colors against the actual generated primary shades used by the theme.\n- For light mode, check the generated `primary-600` and `primary-700` states.\n- For dark mode, check the generated `primary-400` and `primary-500` states.\n- Choose the text color with the stronger worst-case contrast across those states.\n- White usually wins for medium or dark primaries; dark gray should only be used when the generated brand states are genuinely light.\n- In dark mode, it is valid to shift the primary button one step deeper (for example `500/600` instead of `400/500`) when that keeps white text readable and better matches the design system.\n\nReadable inverse and decorative accents:\n- `--foreground-inverse` is not just for neutral inverse surfaces. In Preline it is also used on vivid utility colors such as red/orange badges and chips.\n- In dark mode, prefer keeping `--foreground-inverse` white unless contrast checks prove a darker value is still safe across those saturated utility colors.\n- Do not let dark mode inherit very deep brand steps for decorative tokens such as `--chart-5` when those tokens are likely to appear in gradient text or accent UI.\n- For dark decorative/chart tokens, prefer mid-to-light brand steps that stay clearly visible on dark backgrounds.\n\nQuick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.\n\n## Palette types\n\nWhen generating custom themes, there are TWO distinct types of custom color palettes:\n\n| Palette Type | Example Variable | Purpose | Chroma Level |\n|--------------|------------------|---------|--------------|\n| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |\n| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |\n\n### When to create each palette\n\nALWAYS create TWO palettes in `@theme inline { }`:\n1. Brand palette (`--color-<name>-*`) for primary/accent colors\n2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders\n\nBoth palettes are used in light mode for a cohesive feel.\n\nDark mode behavior depends on user request:\n\n| User Request | Light Mode Uses | Dark Mode Uses |\n|--------------|-----------------|----------------|\n| \"soft rose theme\" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |\n| \"soft rose theme with matching dark mode\" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |\n\nTrigger phrases for custom dark mode palette:\n- \"matching dark mode\"\n- \"cohesive dark mode\"\n- \"consistent dark colors\"\n- \"unified light and dark\"\n- \"matching neutrals\"\n\nIf none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.\n\n### Brand color style (vibrant vs soft)\n\nBrand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:\n\n| User Says | Brand Palette Style | Chroma Level |\n|-----------|---------------------|--------------|\n| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |\n| \"soft\", \"muted\", \"ash\", \"pastel\", \"dusty\", \"desaturated\" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |\n\nExample comparison:\n\n```css\n/* Vibrant brand (default) */\n--color-<name>-500: oklch(55% 0.14 <hue>);\n\n/* Soft/muted brand (if requested) */\n--color-<name>-500: oklch(55% 0.04 <hue>);\n```\n\n## Custom gray palettes (neutral matching)\n\nWhen a theme benefits from unique neutrals, generate a custom neutral palette that harmonizes with the primary color. These are NOT pure grays; they have subtle warmth or coolness.\n\n### Palette characteristics\n\n| Palette Type | Hue Range | Chroma Range | Use With |\n|--------------|-----------|--------------|----------|\n| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |\n| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |\n| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |\n| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |\n\n### OKLCH pattern for gray palettes (bell curve chroma)\n\nGray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:\n- Light mode backgrounds (50-200) look clean with subtle tint\n- Dark mode backgrounds (800-950) look sophisticated, not muddy\n\n```css\n--color-<name>-gray-50:  oklch(98%   0.002 <hue>);  /* Almost neutral */\n--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);\n--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);\n--color-<name>-gray-300: oklch(82.7% 0.012 <hue>);  /* Building up */\n--color-<name>-gray-400: oklch(73%   0.018 <hue>);  /* Approaching peak */\n--color-<name>-gray-500: oklch(62.5% 0.020 <hue>);  /* PEAK chroma */\n--color-<name>-gray-600: oklch(52.8% 0.016 <hue>);  /* Starting to fade */\n--color-<name>-gray-700: oklch(41.4% 0.012 <hue>);  /* Fading */\n--color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */\n--color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */\n--color-<name>-gray-950: oklch(20.1% 0.003 <hue>);  /* Nearly pure dark */\n```\n\nCritical: Dark mode backgrounds (800-950) need very low chroma:\n\n| Shade | Chroma | Why |\n|-------|--------|-----|\n| 950 | 0.003 | Dark backgrounds must be nearly neutral |\n| 900 | 0.005 | Avoids muddy/colored appearance |\n| 800 | 0.008 | Just a hint of warmth/coolness |\n| 500 | 0.020 | Peak, midtones carry color identity |\n| 50 | 0.002 | Light backgrounds stay clean |\n\nKey principles:\n- Chroma peaks at mid-tones (400-600), very low at BOTH extremes\n- Dark end (800-950) must have lower chroma than light end for clean dark mode\n- Hue stays consistent across the scale (±5 degrees variation is acceptable)\n- Lightness follows standard 50-950 scale\n\n### Placement of custom palettes\n\nCustom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:\n\n```css\n@theme theme-<name> inline {\n  /* Custom neutral palette for this theme */\n  --color-<name>-50: oklch(98% 0.003 88);\n  --color-<name>-100: oklch(95.5% 0.005 88);\n  /* ... full 50-950 scale ... */\n}\n```\n\nThen reference these in the theme selector blocks using `var()`:\n\n```css\n[data-theme=\"theme-<name>\"] {\n  --background: var(--color-<name>-50);\n  --background-1: var(--color-<name>-100);\n  /* ... */\n}\n\n[data-theme=\"theme-<name>\"].dark {\n  --background: var(--color-<name>-950);\n  --background-1: var(--color-<name>-900);\n  /* ... */\n}\n```\n\n### When to create custom palettes\n\n| Scenario | Action |\n|----------|--------|\n| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |\n| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |\n| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |\n| User explicitly requests | Always create to match their vision |\n| Theme needs distinctive personality | Create for cohesion |\n\n### Using custom palettes in both modes\n\nSoft gray palettes work seamlessly across light and dark modes:\n- Light mode: use 50-300 for backgrounds, 600-950 for text\n- Dark mode: use 800-950 for backgrounds, 50-300 for text\n\nThis creates consistent warmth/coolness across modes while maintaining readability.\n","isInternal":false,"tokens":2861,"sizeBytes":10556},{"name":"token-reference.md","path":"skills/theme-generator/docs/token-reference.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/docs/token-reference.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Preline Theme Token Reference\n\nThis document provides the complete reference for all semantic tokens available in Preline themes. Use this when generating themes to ensure comprehensive coverage.\n\n---\n\n## Contents\n\n- [Overview](#overview)\n- [Core token groups](#core-token-groups)\n- [Component preset patterns](#component-preset-patterns)\n- [Full token reference by category](#full-token-reference-by-category)\n- [Custom color palettes](#custom-color-palettes)\n- [Default values reference](#default-values-reference)\n- [Theme-scoped overrides](#theme-scoped-overrides)\n- [Color format guidelines](#color-format-guidelines)\n- [Quick token count summary](#quick-token-count-summary)\n\n## Overview\n\nPreline themes use CSS custom properties (variables) as semantic tokens. These tokens are:\n- Defined in theme files under `:root` (light) and `.dark` selectors\n- Mapped to Tailwind utilities via `@theme inline` in the base theme\n- Consumed by Preline components for consistent styling\n\nImportant: Generated themes override token values only. Never redefine the Tailwind mappings.\n\n---\n\n## Core token groups\n\n| Group | Tokens | Used for |\n|-------|--------|----------|\n| Brand | `--primary`, `--primary-*` | Primary actions (buttons, links) |\n| Secondary | `--secondary`, `--secondary-*` | Secondary emphasis |\n| Muted | `--muted`, `--muted-*` | Muted/subdued elements |\n| Destructive | `--destructive`, `--destructive-*` | Destructive/danger actions |\n| Background | `--background`, `--background-*` | App surfaces |\n| Foreground | `--foreground`, `--foreground-*` | Default typography colors |\n| Inverse | `--inverse` | Inverted color scheme |\n| Border | `--border` | Default border color |\n| Border line | `--border-line-*` | Border colors for different shades |\n| Layer | `--layer`, `--layer-*` | Layered elements (e.g. white buttons on white panel) |\n| Surface | `--surface`, `--surface-*` | Elevated surfaces |\n| Navbar | `--navbar`, `--navbar-*` | Navigation bar (background, border, divider) |\n| Navbar nav | `--navbar-nav-*` | Navigation bar items (foreground, hover, active) |\n| Sidebar | `--sidebar`, `--sidebar-*` | Sidebar (background, border, divider) |\n| Sidebar nav | `--sidebar-nav-*` | Sidebar items (foreground, hover, active) |\n| Card | `--card`, `--card-*` | Card component (border, divider, header, footer) |\n| Dropdown | `--dropdown`, `--dropdown-*` | Dropdown menus (items, hover, active) |\n| Select | `--select`, `--select-*` | Select component (items, hover, active) |\n| Overlay | `--overlay`, `--overlay-*` | Modal/overlay backgrounds |\n| Popover | `--popover`, `--popover-*` | Popover component |\n| Tooltip | `--tooltip`, `--tooltip-*` | Tooltip component |\n| Table | `--table-line` | Table borders |\n| Switch | `--switch` | Switch/toggle component |\n| Footer | `--footer`, `--footer-*` | Footer component |\n| Scrollbar | `--scrollbar-*` | Custom scrollbar (track, thumb) |\n| Chart | `--chart-*` | Chart colors (Apexcharts) |\n| Map | `--map-colors-*` | Map colors (jsvectormap) |\n\n---\n\n## Component preset patterns\n\nPreline uses numbered variants for component groups to offer different ready-made looks:\n\n| Pattern | Meaning |\n|---------|---------|\n| `--navbar-*` | Default navbar preset (base style) |\n| `--navbar-1-*` | Variant 1 preset (typically subtle/tinted surface) |\n| `--navbar-2-*` | Variant 2 preset (typically stronger tint or different border) |\n| `*` (no suffix) | Background/surface color of the component |\n| `*-line` | Border color for the component container |\n| `*-divider` | Divider lines inside the component |\n| `*-nav-*` | Nav item colors (foreground, hover, focus, active) |\n| `*-nav-list-divider` | Divider used in nav dropdown/list layouts |\n| `*-inverse` | Inverted/contrast variant |\n\nThis pattern applies to: navbar, sidebar, and similar navigation components.\n\n---\n\n## Full token reference by category\n\n### 1. Global surfaces + text\n\n```css\n/* Background tokens */\n--background          /* Main app background */\n--background-1        /* Secondary background (slightly tinted) */\n--background-2        /* Tertiary background (more tinted) */\n--background-plain    /* Plain white/black background */\n\n/* Foreground tokens */\n--foreground          /* Default text color */\n--foreground-inverse  /* Inverted text color (for dark backgrounds) */\n\n/* Inverse */\n--inverse             /* Inverted surface color */\n```\n\n### 2. Borders (full scale)\n\n```css\n--border              /* Default border color */\n--border-line-inverse /* Inverse border color */\n--border-line-1       /* Lightest border */\n--border-line-2\n--border-line-3\n--border-line-4\n--border-line-5\n--border-line-6\n--border-line-7\n--border-line-8       /* Darkest border */\n```\n\n### 3. Primary ramp + states\n\n```css\n/* Full color ramp (11 shades) */\n--primary-50\n--primary-100\n--primary-200\n--primary-300\n--primary-400\n--primary-500\n--primary-600\n--primary-700\n--primary-800\n--primary-900\n--primary-950\n\n/* State tokens */\n--primary             /* Base primary color */\n--primary-line        /* Primary border (usually transparent) */\n--primary-foreground  /* Text on primary background */\n--primary-hover       /* Hover state */\n--primary-focus       /* Focus state */\n--primary-active      /* Active/pressed state */\n--primary-checked     /* Checked state (checkboxes, radios) */\n```\n\n### 4. Secondary\n\n```css\n--secondary            /* Base secondary color */\n--secondary-line       /* Secondary border */\n--secondary-foreground /* Text on secondary background */\n--secondary-hover      /* Hover state */\n--secondary-focus      /* Focus state */\n--secondary-active     /* Active state */\n```\n\n### 5. Layer\n\n```css\n--layer               /* Layer background */\n--layer-line          /* Layer border */\n--layer-foreground    /* Text on layer */\n--layer-hover         /* Hover state */\n--layer-focus         /* Focus state */\n--layer-active        /* Active state */\n```\n\n### 6. Surface\n\n```css\n--surface             /* Base surface */\n--surface-1           /* Surface variant 1 */\n--surface-2           /* Surface variant 2 */\n--surface-3           /* Surface variant 3 */\n--surface-4           /* Surface variant 4 */\n--surface-5           /* Surface variant 5 */\n--surface-line        /* Surface border */\n--surface-foreground  /* Text on surface */\n--surface-hover       /* Hover state */\n--surface-focus       /* Focus state */\n--surface-active      /* Active state */\n```\n\n### 7. Muted\n\n```css\n--muted               /* Muted background */\n--muted-foreground    /* Muted text (lightest) */\n--muted-foreground-1  /* Muted text variant 1 */\n--muted-foreground-2  /* Muted text variant 2 (darkest) */\n--muted-hover         /* Hover state */\n--muted-focus         /* Focus state */\n--muted-active        /* Active state */\n```\n\n### 8. Destructive\n\n```css\n--destructive            /* Destructive/danger color */\n--destructive-foreground /* Text on destructive background */\n--destructive-hover      /* Hover state */\n--destructive-focus      /* Focus state */\n```\n\n### 9. Navbar (base + 2 variants)\n\n```css\n/* Base navbar */\n--navbar                /* Navbar background */\n--navbar-line           /* Navbar border */\n--navbar-divider        /* Navbar divider */\n--navbar-nav-foreground /* Nav item text */\n--navbar-nav-hover      /* Nav item hover */\n--navbar-nav-focus      /* Nav item focus */\n--navbar-nav-active     /* Nav item active */\n--navbar-nav-list-divider /* Nav list divider */\n--navbar-inverse        /* Inverse navbar background */\n\n/* Navbar variant 1 (subtle/tinted) */\n--navbar-1\n--navbar-1-line\n--navbar-1-divider\n--navbar-1-nav-foreground\n--navbar-1-nav-hover\n--navbar-1-nav-focus\n--navbar-1-nav-active\n--navbar-1-nav-list-divider\n\n/* Navbar variant 2 (stronger tint) */\n--navbar-2\n--navbar-2-line\n--navbar-2-divider\n--navbar-2-nav-foreground\n--navbar-2-nav-hover\n--navbar-2-nav-focus\n--navbar-2-nav-active\n--navbar-2-nav-list-divider\n```\n\n### 10. Sidebar (base + 2 variants)\n\n```css\n/* Base sidebar */\n--sidebar\n--sidebar-line\n--sidebar-divider\n--sidebar-nav-foreground\n--sidebar-nav-hover\n--sidebar-nav-focus\n--sidebar-nav-active\n--sidebar-nav-list-divider\n--sidebar-inverse\n\n/* Sidebar variant 1 */\n--sidebar-1\n--sidebar-1-line\n--sidebar-1-divider\n--sidebar-1-nav-foreground\n--sidebar-1-nav-hover\n--sidebar-1-nav-focus\n--sidebar-1-nav-active\n--sidebar-1-nav-list-divider\n\n/* Sidebar variant 2 */\n--sidebar-2\n--sidebar-2-line\n--sidebar-2-divider\n--sidebar-2-nav-foreground\n--sidebar-2-nav-hover\n--sidebar-2-nav-focus\n--sidebar-2-nav-active\n--sidebar-2-nav-list-divider\n```\n\n### 11. Card\n\n```css\n--card                /* Card background */\n--card-line           /* Card border */\n--card-divider        /* Card divider */\n--card-header         /* Card header background */\n--card-footer         /* Card footer background */\n--card-inverse        /* Inverse card background */\n```\n\n### 12. Dropdown\n\n```css\n--dropdown                /* Dropdown background */\n--dropdown-1              /* Dropdown variant background */\n--dropdown-line           /* Dropdown border */\n--dropdown-divider        /* Dropdown divider */\n--dropdown-header         /* Dropdown header background */\n--dropdown-footer         /* Dropdown footer background */\n--dropdown-item-foreground /* Item text */\n--dropdown-item-hover     /* Item hover */\n--dropdown-item-focus     /* Item focus */\n--dropdown-item-active    /* Item active */\n--dropdown-inverse        /* Inverse dropdown */\n```\n\n### 13. Select\n\n```css\n--select                /* Select background */\n--select-1              /* Select variant background */\n--select-line           /* Select border */\n--select-item-foreground /* Item text */\n--select-item-hover     /* Item hover */\n--select-item-focus     /* Item focus */\n--select-item-active    /* Item active */\n--select-inverse        /* Inverse select */\n```\n\n### 14. Overlay\n\n```css\n--overlay             /* Overlay/modal background */\n--overlay-line        /* Overlay border */\n--overlay-divider     /* Overlay divider */\n--overlay-header      /* Overlay header background */\n--overlay-footer      /* Overlay footer background */\n--overlay-inverse     /* Inverse overlay */\n```\n\n### 15. Popover, tooltip, table, switch\n\n```css\n/* Popover */\n--popover             /* Popover background */\n--popover-line        /* Popover border */\n\n/* Tooltip */\n--tooltip             /* Tooltip background */\n--tooltip-foreground  /* Tooltip text */\n--tooltip-line        /* Tooltip border */\n\n/* Table */\n--table-line          /* Table border */\n\n/* Switch */\n--switch              /* Switch knob color */\n```\n\n### 16. Footer\n\n```css\n--footer              /* Footer background */\n--footer-line         /* Footer border */\n--footer-inverse      /* Inverse footer */\n```\n\n### 17. Scrollbar\n\n```css\n--scrollbar-track         /* Scrollbar track */\n--scrollbar-thumb         /* Scrollbar thumb */\n--scrollbar-track-inverse /* Inverse track */\n--scrollbar-thumb-inverse /* Inverse thumb */\n```\n\n### 18. Charts (Apexcharts)\n\nImportant: keep chart `-hex` tokens variable-based in generated theme source. Do not hardcode literal hex or oklch values into the final theme file.\n\n```css\n/* Primary chart color */\n--chart-primary\n--chart-colors-primary\n--chart-colors-primary-inverse\n--chart-colors-primary-hex\n--chart-colors-primary-hex-inverse\n\n/* Chart palette (1-10) */\n--chart-1 through --chart-10\n--chart-colors-chart-1 through --chart-colors-chart-10\n--chart-colors-chart-1-inverse through --chart-colors-chart-10-inverse\n--chart-colors-chart-1-hex through --chart-colors-chart-10-hex\n--chart-colors-chart-1-hex-inverse through --chart-colors-chart-10-hex-inverse\n\n/* Chart backgrounds */\n--chart-colors-background\n--chart-colors-background-inverse\n--chart-colors-chart-inverse\n\n/* Chart foregrounds */\n--chart-colors-foreground\n--chart-colors-foreground-inverse\n\n/* Chart labels */\n--chart-colors-labels\n--chart-colors-labels-inverse\n--chart-colors-xaxis-labels\n--chart-colors-xaxis-labels-inverse\n--chart-colors-yaxis-labels\n--chart-colors-yaxis-labels-inverse\n\n/* Chart grid */\n--chart-colors-grid-border\n--chart-colors-grid-border-inverse\n\n/* Chart special */\n--chart-colors-bar-ranges\n--chart-colors-bar-ranges-inverse\n--chart-colors-candlestick-upward\n--chart-colors-candlestick-upward-inverse\n--chart-colors-candlestick-downward\n--chart-colors-candlestick-downward-inverse\n```\n\n### 19. Maps (jsvectormap)\n\n```css\n--map-colors-primary\n--map-colors-primary-inverse\n--map-colors-default\n--map-colors-default-inverse\n--map-colors-highlight\n--map-colors-highlight-inverse\n--map-colors-border\n--map-colors-border-inverse\n```\n\n---\n\n## Custom color palettes\n\nThe base theme defines custom color palettes that themes can use:\n\n### Khaki (earthy/warm neutral)\n\n```css\n--color-khaki-50 through --color-khaki-950\n```\n\n### Lavender (soft purple/mauve neutral)\n\n```css\n--color-lavender-50 through --color-lavender-950\n```\n\nThese are useful for creating warm or soft-toned themes instead of using standard gray/neutral.\n\n---\n\n## Default values reference\n\n### Light mode defaults (:root)\n\n```css\n:root {\n  /* Backgrounds */\n  --background: var(--color-white);\n  --background-1: var(--color-gray-50);\n  --background-2: var(--color-gray-100);\n  --background-plain: var(--color-white);\n\n  /* Foregrounds */\n  --foreground: var(--color-gray-800);\n  --foreground-inverse: var(--color-white);\n  --inverse: var(--color-gray-800);\n\n  /* Borders */\n  --border: var(--color-gray-200);\n  --border-line-inverse: var(--color-white);\n  --border-line-1: var(--color-gray-100);\n  --border-line-2: var(--color-gray-200);\n  --border-line-3: var(--color-gray-300);\n  --border-line-4: var(--color-gray-400);\n  --border-line-5: var(--color-gray-500);\n  --border-line-6: var(--color-gray-600);\n  --border-line-7: var(--color-gray-700);\n  --border-line-8: var(--color-gray-800);\n\n  /* Primary (blue by default) */\n  --primary-50: var(--color-blue-50);\n  --primary-100: var(--color-blue-100);\n  --primary-200: var(--color-blue-200);\n  --primary-300: var(--color-blue-300);\n  --primary-400: var(--color-blue-400);\n  --primary-500: var(--color-blue-500);\n  --primary-600: var(--color-blue-600);\n  --primary-700: var(--color-blue-700);\n  --primary-800: var(--color-blue-800);\n  --primary-900: var(--color-blue-900);\n  --primary-950: var(--color-blue-950);\n\n  --primary: var(--color-primary-600);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  --primary-focus: var(--color-primary-700);\n  --primary-active: var(--color-primary-700);\n  --primary-checked: var(--color-primary-600);\n\n  /* Secondary */\n  --secondary: var(--color-gray-900);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-white);\n  --secondary-hover: var(--color-gray-800);\n  --secondary-focus: var(--color-gray-800);\n  --secondary-active: var(--color-gray-800);\n\n  /* Layer */\n  --layer: var(--color-white);\n  --layer-line: var(--color-gray-200);\n  --layer-foreground: var(--color-gray-800);\n  --layer-hover: var(--color-gray-50);\n  --layer-focus: var(--color-gray-50);\n  --layer-active: var(--color-gray-50);\n\n  /* Surface */\n  --surface: var(--color-gray-100);\n  --surface-1: var(--color-gray-200);\n  --surface-2: var(--color-gray-300);\n  --surface-3: var(--color-gray-400);\n  --surface-4: var(--color-gray-500);\n  --surface-5: var(--color-gray-600);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-gray-800);\n  --surface-hover: var(--color-gray-200);\n  --surface-focus: var(--color-gray-200);\n  --surface-active: var(--color-gray-200);\n\n  /* Muted */\n  --muted: var(--color-gray-50);\n  --muted-foreground: var(--color-gray-400);\n  --muted-foreground-1: var(--color-gray-500);\n  --muted-foreground-2: var(--color-gray-600);\n  --muted-hover: var(--color-gray-100);\n  --muted-focus: var(--color-gray-100);\n  --muted-active: var(--color-gray-100);\n\n  /* Destructive */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Navbar (base) */\n  --navbar: var(--color-white);\n  --navbar-line: var(--color-gray-200);\n  --navbar-divider: var(--color-gray-200);\n  --navbar-nav-foreground: var(--color-gray-800);\n  --navbar-nav-hover: var(--color-gray-100);\n  --navbar-nav-focus: var(--color-gray-100);\n  --navbar-nav-active: var(--color-gray-100);\n  --navbar-nav-list-divider: var(--color-gray-200);\n  --navbar-inverse: var(--color-primary-950);\n\n  /* Navbar-1 */\n  --navbar-1: var(--color-gray-50);\n  --navbar-1-line: var(--color-gray-200);\n  --navbar-1-divider: var(--color-gray-200);\n  --navbar-1-nav-foreground: var(--color-gray-800);\n  --navbar-1-nav-hover: var(--color-gray-200);\n  --navbar-1-nav-focus: var(--color-gray-200);\n  --navbar-1-nav-active: var(--color-gray-200);\n  --navbar-1-nav-list-divider: var(--color-gray-200);\n\n  /* Navbar-2 */\n  --navbar-2: var(--color-gray-100);\n  --navbar-2-line: transparent;\n  --navbar-2-divider: var(--color-gray-300);\n  --navbar-2-nav-foreground: var(--color-gray-800);\n  --navbar-2-nav-hover: var(--color-gray-200);\n  --navbar-2-nav-focus: var(--color-gray-200);\n  --navbar-2-nav-active: var(--color-gray-200);\n  --navbar-2-nav-list-divider: var(--color-gray-200);\n\n  /* Sidebar follows same pattern as navbar */\n  /* Card, Dropdown, Select, Overlay, Popover, Tooltip, Table, Switch, Footer, Scrollbar */\n  /* See the project's existing base Preline theme source for complete default values */\n}\n```\n\n### Dark mode defaults (.dark)\n\n```css\n.dark {\n  /* Backgrounds */\n  --background: var(--color-neutral-800);\n  --background-1: var(--color-neutral-900);\n  --background-2: var(--color-neutral-900);\n  --foreground: var(--color-neutral-200);\n  --inverse: var(--color-neutral-950);\n\n  /* Borders */\n  --border: var(--color-neutral-700);\n  --border-line-1: var(--color-neutral-800);\n  --border-line-2: var(--color-neutral-700);\n  --border-line-3: var(--color-neutral-600);\n  --border-line-4: var(--color-neutral-500);\n  --border-line-5: var(--color-neutral-400);\n  --border-line-6: var(--color-neutral-300);\n  --border-line-7: var(--color-neutral-200);\n  --border-line-8: var(--color-neutral-100);\n\n  /* Primary (shifts to 500 in dark mode) */\n  --primary: var(--color-primary-500);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-600);\n  --primary-focus: var(--color-primary-600);\n  --primary-active: var(--color-primary-600);\n  --primary-checked: var(--color-primary-500);\n\n  /* Secondary */\n  --secondary: var(--color-white);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-neutral-800);\n  --secondary-hover: var(--color-neutral-100);\n  --secondary-focus: var(--color-neutral-100);\n  --secondary-active: var(--color-neutral-100);\n\n  /* Layer */\n  --layer: var(--color-neutral-800);\n  --layer-line: var(--color-neutral-700);\n  --layer-foreground: var(--color-white);\n  --layer-hover: var(--color-neutral-700);\n  --layer-focus: var(--color-neutral-700);\n  --layer-active: var(--color-neutral-700);\n\n  /* Surface */\n  --surface: var(--color-neutral-700);\n  --surface-1: var(--color-neutral-600);\n  --surface-2: var(--color-neutral-500);\n  --surface-3: var(--color-neutral-600);\n  --surface-4: var(--color-neutral-500);\n  --surface-5: var(--color-neutral-400);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-neutral-200);\n  --surface-hover: var(--color-neutral-600);\n  --surface-focus: var(--color-neutral-600);\n  --surface-active: var(--color-neutral-600);\n\n  /* Muted */\n  --muted: var(--color-neutral-800);\n  --muted-foreground: var(--color-neutral-500);\n  --muted-foreground-1: var(--color-neutral-400);\n  --muted-foreground-2: var(--color-neutral-300);\n  --muted-hover: var(--color-neutral-700);\n  --muted-focus: var(--color-neutral-700);\n  --muted-active: var(--color-neutral-700);\n\n  /* Destructive (same red, readable in dark) */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Components shift to neutral-800/900 backgrounds */\n  /* Nav items shift to neutral-700 hover states */\n  /* See the project's existing base Preline theme source for complete dark mode values */\n}\n```\n\n---\n\n## Theme-scoped overrides\n\nWhen creating a theme, override values under theme-scoped selectors:\n\n```css\n/* Light mode */\n:root[data-theme=\"theme-<name>\"],\n[data-theme=\"theme-<name>\"] {\n  /* Override tokens here */\n}\n\n/* Dark mode */\n[data-theme=\"theme-<name>\"].dark {\n  /* Override dark-specific tokens here */\n}\n```\n\n---\n\n## Color format guidelines\n\n- For most tokens: use Tailwind color variables `var(--color-<color>-<shade>)`\n- For oklch colors: acceptable for standard tokens\n- For chart `-hex` tokens: keep them as stable variable references in generated source\n- For custom colors: can use hex, rgb, hsl, or oklch\n\n---\n\n## Quick token count summary\n\n| Category | Token Count |\n|----------|-------------|\n| Global surfaces + text | 7 |\n| Borders | 10 |\n| Primary (ramp + states) | 18 |\n| Secondary | 6 |\n| Layer | 6 |\n| Surface | 11 |\n| Muted | 7 |\n| Destructive | 4 |\n| Navbar (3 variants) | 27 |\n| Sidebar (3 variants) | 27 |\n| Card | 6 |\n| Dropdown | 11 |\n| Select | 8 |\n| Overlay | 6 |\n| Popover | 2 |\n| Tooltip | 3 |\n| Table, Switch, Footer | 5 |\n| Scrollbar | 4 |\n| Charts | ~50+ |\n| Maps | 8 |\n| Total | ~220+ tokens\n","isInternal":false,"tokens":5585,"sizeBytes":21372},{"name":"validation-checklist.md","path":"skills/theme-generator/docs/validation-checklist.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/docs/validation-checklist.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Validation checklist (must self-check before returning)\n\n## Final source style\n- [ ] Only one project file is created or edited: `<name>.css`\n- [ ] The final file is a production-style `<name>.css` file\n- [ ] `@theme theme-<name> inline { }` is present\n- [ ] Any custom brand/gray palette lives inside `@theme theme-<name> inline { }` in the new theme file\n- [ ] Semantic tokens prefer assigned vars such as `--color-primary-*`, `--background-*`, `--foreground`, `--border`, `--surface-*`, or shared palette vars when available\n- [ ] The custom theme does not redefine shared aliases like `--color-primary-*` inside `@theme` when the repo base theme already owns them\n- [ ] No raw `oklch(...)` or literal hex values remain in semantic, chart, or map token assignments in the final theme file\n\n## Structure\n- [ ] Theme key consistent: `theme-<name>` everywhere\n- [ ] Light selector uses `:root[data-theme=...]`, `[data-theme=...]`\n- [ ] Dark selector is `[data-theme=\"theme-<name>\"].dark`\n- [ ] Semantic token definitions are in selector blocks (NOT inside @theme block)\n- [ ] No requirement to change HTML utility classes (only `data-theme`, optional `.dark`)\n\n## Palette strategy\n- [ ] Primary ramp maps `--primary-*` directly to theme-local palette vars\n- [ ] Neutral/background tokens use assigned palette vars\n- [ ] If a new custom palette is needed, it is declared inside the new theme file's `@theme` block, not by editing another file\n\n## Token coverage\n- [ ] Full coverage present across global tokens, states, and major component groups\n- [ ] CSS syntax valid (balanced braces, no broken comments)\n- [ ] If behavior overrides exist, they are theme-scoped only\n- [ ] Chart/map tokens use variable references matching shipped theme patterns\n\n## Dark mode consistency\n- [ ] Dark mode uses CSS variables (`var(--color-*)`) not hardcoded color literals\n- [ ] Dark mode uses one consistent grayscale family unless there is a deliberate, reference-backed reason not to\n- [ ] Hover/focus/active states remain readable and distinguishable\n- [ ] `--foreground-inverse` remains readable on vivid utility colors in dark mode (for example red badges/chips)\n- [ ] Dark-mode chart/decorative tokens avoid very deep brand steps when those tokens are used in gradient text or accent UI\n","isInternal":false,"tokens":537,"sizeBytes":2280},{"name":"workflow.md","path":"skills/theme-generator/docs/workflow.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/docs/workflow.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Workflow\n\nUse this file as the default operating guide for the active theme skill.\n\n## Task modes\n\nClassify the request before doing any work:\n\n- `preview`: generate CSS and return it without writing a project file\n- `write`: create one new `<name>.css` theme file\n- `validate`: review or fix an existing generated theme file\n\n## Normalize the request\n\nConvert natural language into the generator's small, explicit input set:\n\n| Input | Required | Notes |\n|------|----------|-------|\n| `name` | yes | kebab-case theme name |\n| `hue` or `primaryColor` | yes | use `primaryColor` only when user gave a brand hex |\n| `style` | no | `vibrant` by default, `soft` for muted/pastel/desaturated requests |\n| `useCustomDarkGray` | no | true only when user explicitly wants matching/cohesive dark neutrals |\n| `tailwindGray` | no | choose `stone`, `neutral`, `zinc`, `slate`, or `gray` based on brand warmth |\n| `fontSans` / `fontSerif` / `fontMono` | no | include only when requested |\n\nUse `docs/palette-guidance.md` for hue ranges, mood mapping, contrast heuristics, and gray-family decisions.\n\n### Natural-language interpretation\n\nUse the user's language to infer the palette direction:\n\n| User language | Default interpretation |\n|--------------|------------------------|\n| warm sunset, cozy, inviting | orange/amber primary, warm neutrals, medium contrast |\n| professional, clean, SaaS | blue/indigo primary, slate or zinc neutrals |\n| playful, candy, youthful | pink/purple/cyan primary, higher contrast |\n| earthy, organic, calm | green/teal primary, stone neutrals |\n| soft, muted, ash, dusty | `style: soft` |\n| matching dark mode, cohesive dark, unified neutrals | `useCustomDarkGray: true` |\n\nWhen the request is vague:\n\n1. Make an opinionated, coherent palette choice.\n2. Keep contrast readable.\n3. Prefer a consistent system over a flashy but uneven palette.\n\nForeground rule:\n- When mapping text or icon colors onto the primary brand color, choose the foreground by contrast against the generated primary states, not by hue bucket alone.\n- Treat `--foreground-inverse` separately from primary foregrounds. In Preline it is reused on hardcoded vivid utility backgrounds such as red badges, so dark mode should keep it readable on saturated colors instead of tinting it into a dark neutral.\n- Treat chart tokens as decorative UI tokens, not just data-series tokens. Dark mode must override any chart shades that would become too dark for gradient text, badges, or chart accents on dark surfaces.\n\n## Discover the target directory\n\nUse the bundled resolver instead of broad repo traversal:\n\n```bash\nnode scripts/find-themes-dir.js\n```\n\nTrusted theme directories are checked in this order:\n\n- `src/assets/css/themes/`\n- `src/css/themes/`\n- `src/styles/themes/`\n- `assets/css/themes/`\n- `styles/themes/`\n- `dist/assets/css/themes/`\n- `npm/preline/css/themes/`\n\nRules:\n\n- Prefer source theme directories over generated outputs.\n- Do not use repo-wide `find .` traversal for this task.\n- If no trusted theme directory is found and the user did not provide a path, ask where the file should be written.\n\n## Generate CSS\n\nUse the local wrapper as the only execution entry point:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --stdout\n```\n\nWrite a theme file with:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --output src/assets/css/themes/seafoam.css\n```\n\nRules:\n\n- Do not use `npx`.\n- Do not interpolate raw user text into shell commands.\n- Do not chain temp-file cleanup or other unrelated shell work into the command.\n- Do not create or edit any project file other than the new theme file.\n\n## Theme structure expectations\n\nThe wrapper should emit a production-style standalone `<name>.css` file:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector\n   - `[data-theme=\"theme-<name>\"].dark`\n\nKeep custom brand and gray palettes inside the file's `@theme` block. Keep semantic tokens in the selector blocks.\n\nSee `docs/final-output-style.md` for the final file rules and `docs/token-reference.md` only when detailed token coverage inspection is necessary.\n\n## Dark-mode decision rule\n\nAlways generate both palettes in `@theme theme-<name> inline { }`:\n\n- `--color-<name>-*` for the brand ramp\n- `--color-<name>-gray-*` for the theme's neutral ramp\n\nDark-mode neutrals depend on user intent:\n\n- Explicit matching/cohesive dark request: use the theme's custom gray palette in dark mode.\n- Default path: keep light mode custom and use a Tailwind gray family in dark mode.\n\nPrimary foreground decision:\n- Use the generated brand shades to determine whether white or dark text is more readable on the primary color in each mode.\n- In dark mode, prefer a deeper primary step with white text when that profile remains readable; only fall back to lighter primary steps with dark text when white would fail.\n\nUse CSS variables in semantic tokens and dark-mode overrides. Do not hardcode raw `oklch(...)` or hex values in semantic, chart, or map tokens.\n\n## Validation focus\n\nBefore returning:\n\n- confirm only one project file was created or edited\n- confirm the theme key is `theme-<name>` everywhere\n- confirm the file has full light and dark token coverage\n- confirm chart and map tokens remain variable-based\n- confirm dark-mode states remain readable\n\nUse `docs/validation-checklist.md` as the final gate.\n\n## Response shape\n\nMatch the response to the mode:\n\n- Preview: return the CSS first, then the enable snippet if useful\n- Write: return the created path, a short summary, and the enable snippet\n- Validate: list findings first, then the applied or recommended fix\n\nEnable snippet:\n\n```html\n<html data-theme=\"theme-<theme-name>\">\n```\n","isInternal":false,"tokens":1373,"sizeBytes":5923},{"name":"examples.md","path":"skills/theme-generator/examples.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/examples.md","title":"Theme-generator Skill","category":"anthropic-skill","format":"markdown","content":"# Examples — Preline Theme Generator\n\nThese examples are palette-and-structure references, not literal final-output templates.\n\nImportant: examples below are structural references. Final delivered theme files should follow `docs/final-output-style.md` and should never require edits to any companion project file.\n\nThese examples define what \"good output\" looks like.\n\n## Contents\n\n- [Example 1 — Calm teal SaaS theme](#example-1--calm-teal-saas-theme-full-theme-clean)\n- [Example 2 — Retro sharp corners](#example-2--retro-sharp-corners-behavior-change-must-be-scoped)\n- [Example 3 — Theme-scoped font token](#example-3--theme-scoped-font-token-no-mapping-changes)\n- [Example 4 — Brand match with exact primary color](#example-4--brand-match-with-exact-primary-color)\n\n## Example 1 — Calm teal SaaS theme (full theme, clean)\n\n**Input**\n- themeName: `seafoam`\n- mood: calm, modern SaaS\n- primaryHue: teal (180)\n- contrast: medium\n- style: vibrant\n- darkNeutralFamily: zinc\n\n**Expected Structure**\n- `@theme theme-seafoam inline { }` contains brand + gray palettes\n- Light mode selector: `:root[data-theme=\"theme-seafoam\"], [data-theme=\"theme-seafoam\"]`\n- Dark mode selector: `[data-theme=\"theme-seafoam\"].dark`\n- Full coverage: global surfaces, borders, primary ramp, states, components\n- Dark mode uses Tailwind zinc for neutrals (no matching dark requested)\n\n### Output (truncated)\n\n```css\n/* ------------------------------ */\n/* -------- Seafoam ------------- */\n/* ------------------------------ */\n\n@theme theme-seafoam inline {\n  /* BRAND PALETTE (vibrant) */\n  --color-seafoam-50: oklch(97% 0.08 180);\n  --color-seafoam-100: oklch(94% 0.10 180);\n  --color-seafoam-200: oklch(88% 0.12 180);\n  --color-seafoam-300: oklch(80% 0.14 180);\n  --color-seafoam-400: oklch(70% 0.16 180);\n  --color-seafoam-500: oklch(60% 0.14 180);\n  --color-seafoam-600: oklch(52% 0.12 180);\n  --color-seafoam-700: oklch(44% 0.10 180);\n  --color-seafoam-800: oklch(36% 0.08 180);\n  --color-seafoam-900: oklch(28% 0.06 180);\n  --color-seafoam-950: oklch(20% 0.05 180);\n  \n  /* GRAY PALETTE (bell curve chroma) */\n  --color-seafoam-gray-50: oklch(98% 0.002 180);\n  --color-seafoam-gray-100: oklch(95.5% 0.004 180);\n  --color-seafoam-gray-200: oklch(89.7% 0.008 180);\n  /* ... 300-700 ... */\n  --color-seafoam-gray-800: oklch(34.6% 0.008 180);\n  --color-seafoam-gray-900: oklch(30.6% 0.005 180);\n  --color-seafoam-gray-950: oklch(20.1% 0.003 180);\n\n}\n\n:root[data-theme=\"theme-seafoam\"],\n[data-theme=\"theme-seafoam\"] {\n  /* Global */\n  --background: var(--color-white);\n  --background-1: var(--color-seafoam-gray-50);\n  --foreground: var(--color-seafoam-gray-800);\n  \n  /* Primary ramp */\n  --primary-50: var(--color-seafoam-50);\n  --primary-100: var(--color-seafoam-100);\n  /* ... full ramp ... */\n  \n  /* Primary states */\n  --primary: var(--color-primary-600);\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  \n  /* Components prefer assigned semantic tokens */\n  --navbar: var(--background-plain);\n  --navbar-line: var(--border);\n  /* ... */\n}\n\n[data-theme=\"theme-seafoam\"].dark {\n  /* Dark mode uses Tailwind zinc */\n  --background: var(--color-zinc-800);\n  --background-1: var(--color-zinc-900);\n  --foreground: var(--color-zinc-200);\n  \n  --primary: var(--color-primary-400);\n  --primary-foreground: var(--color-zinc-900); /* Contrast-selected because the generated light-brand states are too light for white */\n  /* ... */\n}\n```\n\n---\n\n## Example 2 — Retro sharp corners (behavior change must be scoped)\n\n**Input**\n- themeName: `arcade`\n- mood: retro 90s\n- primaryHue: magenta (320)\n- contrast: high\n- radiusStyle: `retro-sharp`\n\n**Expected Structure**\n- Full token coverage\n- Theme sets radius tokens in theme selector (NOT in @theme block)\n- Any \"rounded\" behavior override inside `@layer utilities` scoped to theme\n\n### Output (radius section only)\n\n```css\n:root[data-theme=\"theme-arcade\"],\n[data-theme=\"theme-arcade\"] {\n  /* RETRO SHARP CORNERS */\n  --radius-sm: 0px;\n  --radius-md: 2px;\n  --radius-lg: 4px;\n  --radius-xl: 4px;\n  --radius-2xl: 6px;\n  --radius-3xl: 6px;\n  \n  /* ... rest of tokens ... */\n}\n\n/* Optional: Override rounded utility if needed */\n@layer utilities {\n  :is(:root[data-theme=\"theme-arcade\"], [data-theme=\"theme-arcade\"]) .rounded {\n    border-radius: var(--radius-md);\n  }\n}\n```\n\n---\n\n## Example 3 — Theme-scoped font token (no mapping changes)\n\n**Input**\n- themeName: `leafy-modern`\n- mood: organic, clean\n- primaryHue: emerald (145)\n- fontSans: `\"Roboto\", ui-sans-serif, system-ui, sans-serif`\n\n**Expected Structure**\n- Theme sets `--font-sans` inside theme selector (NOT in @theme block)\n- Theme does NOT import Google Fonts (font loading is separate)\n- User enables via `data-theme=\"theme-leafy-modern\"` only\n\n### Output (font section only)\n\n```css\n:root[data-theme=\"theme-leafy-modern\"],\n[data-theme=\"theme-leafy-modern\"] {\n  /* TYPOGRAPHY */\n  --font-sans: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n  \n  /* ... rest of tokens ... */\n}\n```\n\n---\n\n## Example 4 — Brand match with exact primary color\n\n**Input**\n- themeName: `brand-x`\n- primaryColor: `#2F6BFF` (converts to hue ~225)\n- mood: confident, high clarity\n- contrast: medium-high\n\n**Expected Structure**\n- Script converts hex to hue automatically\n- Primary ramp derived coherently from the brand hue\n- `--primary-foreground` readable (white on this blue)\n- Chart `-hex` tokens remain variable-based placeholders in generated source\n\n### Output (chart section showing variable-based chart tokens)\n\n```css\n:root[data-theme=\"theme-brand-x\"],\n[data-theme=\"theme-brand-x\"] {\n  /* ... other tokens ... */\n  \n  /* CHARTS */\n  --chart-primary: var(--color-primary-500);\n  --chart-colors-primary: var(--color-primary-500);\n  --chart-colors-primary-hex: var(--color-primary-500);\n  --chart-colors-primary-hex-inverse: var(--color-primary-300);\n  \n  --chart-1: var(--color-primary-500);\n  --chart-colors-chart-1-hex: var(--color-primary-500);\n  /* ... */\n}\n```\n","isInternal":false,"tokens":1816,"sizeBytes":5991},{"name":"SKILL.md","path":"skills/theme-generator/SKILL.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/skills/theme-generator/SKILL.md","title":"preline-theme-generator","category":"anthropic-skill","format":"markdown","content":"---\nname: preline-theme-generator\ndescription: Generate, preview, save, or validate Preline theme CSS using the bundled local generator in this skill. Use when the user asks to create a new Preline theme, match a brand color, translate a mood into a theme, generate light and dark theme CSS, preview theme output without writing files, or review and fix generated theme tokens. Do not use for generic non-Preline CSS work or arbitrary command execution.\n---\n\n# Preline Theme Generator\n\nUse the bundled local scripts. Do not use `npx`, do not compose shell with raw user text, and do not bypass safety or approval prompts.\n\n## Read Order\n\n1. Read `docs/workflow.md`.\n2. Read `docs/palette-guidance.md`.\n3. Read `docs/final-output-style.md` before writing a final theme file.\n4. Read `docs/validation-checklist.md` before closing the task.\n5. Read `examples.md` only if the user wants stylistic guidance or sample outputs.\n6. Read `docs/token-reference.md` only if you need to inspect token coverage in detail.\n\n## Workflow\n\nFollow `docs/workflow.md` for the full operating path.\n\nKey requirements:\n\n1. Identify whether the task is preview, write, or validate.\n2. Normalize the request into explicit generator inputs.\n3. Resolve the target directory with `scripts/find-themes-dir.js`.\n4. Generate CSS only through `scripts/run-theme-generator.js`.\n5. Create or edit only the new theme file.\n6. Validate with `docs/validation-checklist.md`.\n7. Return the result in the mode-appropriate format.\n\n## Security Constraints\n\n- Never use wording that attempts to bypass tool or approval safeguards.\n- Never use `npx` or any network-fetched package for this skill.\n- Never interpolate raw user text directly into shell commands.\n- Never use broad `find .` traversal for path discovery; use `scripts/find-themes-dir.js` or a user-confirmed path.\n- Never write outside a confirmed theme directory.\n- Use `scripts/run-theme-generator.js` as the only execution entry point for generation.\n- Never create or edit additional project files as part of theme generation.\n\n## Common Prompts\n\n- \"Create a sunset theme.\"\n- \"Match this brand color: #2F6BFF.\"\n- \"Generate a cyberpunk dark theme with matching dark mode.\"\n- \"Preview the CSS before writing a file.\"\n- \"Review this generated theme and fix token coverage.\"\n\n## Key References\n\n- `docs/workflow.md`\n- `docs/palette-guidance.md`\n- `docs/final-output-style.md`\n- `docs/validation-checklist.md`\n- `examples.md`\n- `docs/token-reference.md`\n","frontmatter":{"name":"preline-theme-generator","description":"Generate, preview, save, or validate Preline theme CSS using the bundled local generator in this skill. Use when the user asks to create a new Preline theme, match a brand color, translate a mood into a theme, generate light and dark theme CSS, preview theme output without writing files, or review and fix generated theme tokens. Do not use for generic non-Preline CSS work or arbitrary command execution."},"isInternal":false,"tokens":557,"sizeBytes":2474},{"name":"README.md","path":"src/plugins/accordion/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/accordion/README.md","title":"accordion Documentation","category":"plugin-manifest","format":"markdown","content":"# Accordion\n\nBuild vertically collapsing accordions in combination with Accordion JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/accordion) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/accordion.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Accordion component allows you to create collapsible content sections that can be expanded or collapsed by clicking on toggle buttons. It supports multiple accordion items within a group, with options for keeping items open, ensuring at least one item stays open, and treeview mode for hierarchical structures.\n\n**Key Features:**\n- Multiple accordion items in a single group\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Treeview mode support\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Accordion plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n@import \"./node_modules/@preline/accordion/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n\n  new HSAccordion(document.querySelector(\"#accordion\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/accordion` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/accordion\";\n```\n\n`@preline/accordion/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\n\nHSAccordion.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#accordion\");\nif (el) new HSAccordion(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an accordion component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three accordion items within a group, with the first item open by default.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion active\" id=\"hs-unstyled-heading-one\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-unstyled-collapse-one\">\n      Accordion #1\n    </button>\n    <div id=\"hs-unstyled-collapse-one\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-one\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-two\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-two\">\n      Accordion #2\n    </button>\n    <div id=\"hs-unstyled-collapse-two\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-two\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-three\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-three\">\n      Accordion #3\n    </button>\n    <div id=\"hs-unstyled-collapse-three\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-three\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-accordion-group`: Required wrapper container for all accordion items\n- `hs-accordion`: Required container for each accordion item\n- `hs-accordion-toggle`: Required button element that triggers expand/collapse\n- `hs-accordion-content`: Required container for collapsible content\n- Unique `id` attributes for each accordion item and its content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- To have an accordion item open by default, add the `active` class to the `hs-accordion` element\n- For closed items, add the `hidden` class to the `hs-accordion-content` element\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure accordion behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-accordion-always-open` | `hs-accordion-group` | boolean attribute | `false` (absent) | When present (boolean attribute), accordion items stay open when another item is opened. By default, opening one item closes others. Presence of attribute = `true`, absence = `false`. |\n\n**Example:**\n```html\n<!-- Boolean attribute: presence = true, absence = false -->\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <!-- Multiple items can be open simultaneously -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `--stop-propagation:*` | `hs-accordion-toggle` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, interrupts event propagation when toggle button is clicked. Useful when accordion is nested inside clickable elements. |\n| `--keep-one-open:*` | `hs-accordion-group` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, ensures at least one accordion item remains open at all times. Prevents closing the last open item. |\n\n**Example:**\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <!-- At least one item will always be open -->\n</div>\n\n<button class=\"hs-accordion-toggle --stop-propagation:true\">\n  <!-- Click event won't bubble up -->\n</button>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the accordion to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-group` | Container wrapper | Groups multiple accordion items together |\n| `hs-accordion` | Each accordion item container | Identifies an individual accordion item |\n| `hs-accordion-toggle` | Button element | The clickable element that expands/collapses content |\n| `hs-accordion-content` | Content container | The collapsible content area |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-treeview-root` | Parent container | When present, activates treeview mode for hierarchical accordion structures |\n| `hs-accordion-selectable` | Elements inside treeview | When treeview mode is active, elements with this class become selectable |\n\n**Treeview Mode Example:**\n```html\n<div class=\"hs-accordion-treeview-root\">\n  <div class=\"hs-accordion-group\">\n    <div class=\"hs-accordion\">\n      <button class=\"hs-accordion-toggle\">Parent</button>\n      <div class=\"hs-accordion-content\">\n        <div class=\"hs-accordion-selectable\">Selectable item</div>\n      </div>\n    </div>\n  </div>\n</div>\n```\n\n## JavaScript API\n\nThe `HSAccordion` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an accordion instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed accordion item. Returns `false` if item is already open, `true` otherwise. If other items are open and `always-open` is not set, they will be closed. |\n| `hide()` | None | `boolean` | Collapses the currently open accordion item. Returns `false` if item is already closed or if `keep-one-open` is enabled and this is the last open item, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the accordion instance, removes all generated markup, classes, and event listeners. Use when removing accordion from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSAccordion` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSAccordion.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSAccordion } \\| HTMLElement \\| null` | Returns the accordion instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSAccordion` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if accordion instance is not found. |\n| `HSAccordion.show(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. Only opens if item is not already open. |\n| `HSAccordion.hide(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. Only closes if item is open and `keep-one-open` allows it. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new accordion instance\nconst accordion = new HSAccordion(document.querySelector('#hs-accordion'));\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion programmatically\nshowBtn.addEventListener('click', () => {\n  accordion.show();\n});\n\n// Close accordion programmatically\nhideBtn.addEventListener('click', () => {\n  accordion.hide();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSAccordion.show('#hs-accordion');\n});\n\n// Close accordion using static method\nhideBtn.addEventListener('click', () => {\n  HSAccordion.hide('#hs-accordion');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSAccordion instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeAccordion() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying accordion instance**\n```javascript\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-accordion').remove();\n  });\n}\n```\n\n## Events\n\nAccordion instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeOpen` | Before accordion item opens | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts opening. Can be used to cancel or modify behavior. |\n| `on:open` | After accordion item opens | `HTMLElement` (current accordion element) | Fires after the accordion item has fully opened. Use for post-open actions like loading content. |\n| `on:beforeClose` | Before accordion item closes | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts closing. Can be used to cancel or modify behavior. |\n| `on:close` | After accordion item closes | `HTMLElement` (current accordion element) | Fires after the accordion item has fully closed. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (accordionElement) => {\n    console.log('Accordion opened:', accordionElement);\n    // Perform actions after accordion opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (accordionElement) => {\n    console.log('Accordion closed:', accordionElement);\n    // Perform cleanup or state updates\n  });\n\n  // Listen to beforeOpen event (can be used to cancel)\n  element.on('beforeOpen', (accordionElement) => {\n    console.log('About to open:', accordionElement);\n    // Can add conditional logic here\n  });\n\n  // Listen to beforeClose event (can be used to cancel)\n  element.on('beforeClose', (accordionElement) => {\n    console.log('About to close:', accordionElement);\n    // Can add conditional logic here\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Multiple Open Items\n\nAllow multiple accordion items to be open simultaneously.\n\n```html\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <div class=\"hs-accordion\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content hidden\">Content 1</div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: Always Keep One Open\n\nEnsure at least one item is always open.\n\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <div class=\"hs-accordion active\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content\">Content 1</div>\n  </div>\n  \n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl accordion from external buttons.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion\" id=\"hs-accordion-first\">\n    <button class=\"hs-accordion-toggle\">Content</button>\n    <div class=\"hs-accordion-content hidden\">Content here</div>\n  </div>\n</div>\n\n<button id=\"hs-open-all\">Open All</button>\n<button id=\"hs-close-all\">Close All</button>\n\n<script>\n  const instance = HSAccordion.getInstance('#hs-accordion-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-all').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-close-all').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3847,"sizeBytes":16628},{"name":"README.md","path":"src/plugins/carousel/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/carousel/README.md","title":"carousel Documentation","category":"plugin-manifest","format":"markdown","content":"# Carousel\n\nBuild carousels and sliders with Carousel JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/carousel) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/carousel.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Carousel component provides a flexible, feature-rich slider for displaying multiple items in a scrollable container. It supports autoplay, infinite loop, drag gestures, responsive breakpoints, and multiple display modes.\n\n**Key Features:**\n- Multiple slides in a scrollable container\n- Autoplay and infinite loop support\n- Drag/swipe gestures for navigation\n- Responsive breakpoints for different screen sizes\n- Multiple display modes (default, snap, bounded)\n- Programmatic control via JavaScript API\n- Keyboard navigation support\n- Customizable pagination dots\n\n## Installation\n\nTo get started, install Carousel plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/carousel\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/carousel */\n@source \"../node_modules/@preline/carousel/*.js\";\n@import \"./node_modules/@preline/carousel/variants.css\";\n@import \"./node_modules/@preline/carousel/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/carousel/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCarousel from \"@preline/carousel/non-auto.mjs\";\n\n  new HSCarousel(document.querySelector(\"#carousel\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/carousel` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/carousel\";\n```\n\n`@preline/carousel/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCarousel from \"@preline/carousel/non-auto\";\n\nHSCarousel.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#carousel\");\nif (el) new HSCarousel(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a carousel component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The carousel displays three slides with previous/next navigation buttons.\n\n```html\n<div data-hs-carousel='{\n    \"loadingClasses\": \"opacity-0\"\n  }' class=\"relative\">\n  <div class=\"hs-carousel relative overflow-hidden w-full min-h-64\">\n    <div class=\"hs-carousel-body absolute top-0 bottom-0 start-0 flex flex-nowrap transition-transform duration-700 opacity-0\">\n      <div class=\"hs-carousel-slide\">\n        1\n      </div>\n      <div class=\"hs-carousel-slide\">\n        2\n      </div>\n      <div class=\"hs-carousel-slide\">\n        3\n      </div>\n    </div>\n  </div>\n\n  <button type=\"button\" class=\"hs-carousel-prev\">\n    <span aria-hidden=\"true\">«</span>\n    <span class=\"sr-only\">Previous</span>\n  </button>\n  <button type=\"button\" class=\"hs-carousel-next\">\n    <span class=\"sr-only\">Next</span>\n    <span aria-hidden=\"true\">»</span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-carousel`: Required on the container element, contains configuration options\n- `hs-carousel`: Required wrapper for slides collection, must contain `overflow-hidden` class\n- `hs-carousel-body`: Required slides container\n- `hs-carousel-slide`: Required for each slide element\n- `hs-carousel-prev`: Optional previous slide button\n- `hs-carousel-next`: Optional next slide button\n- `hs-carousel-pagination`: Optional pagination container\n\n**Initial State:**\n- Carousel body typically starts with `opacity-0` which is removed after loading\n- First slide is active by default (index 0)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-carousel` attribute as a JSON object.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `currentIndex` | number | `0` | Specifies the index of the current slide initially (from 0 to slides quantity). |\n| `loadingClasses` | string | `\"opacity-0\"` | Specifies which classes should be removed after the carousel is loaded. CSS classes should be separated with space. |\n| `dotsItemClasses` | string | - | Specifies which classes will be added to the point elements (which are generated automatically). CSS classes should be separated with space. |\n| `isAutoHeight` | boolean | `false` | Sets the height of the carousel to the height of the current slide. |\n| `isAutoPlay` | boolean | `false` | Enables autoplay. Slides advance automatically at the interval specified by `speed`. |\n| `isInfiniteLoop` | boolean | `true` | Enables infinite loop. When reaching the last slide, it wraps to the first slide. |\n| `isCentered` | boolean | `false` | Enables centered mode. This mode adds space on the sides to center the slides. |\n| `isSnap` | boolean | `false` | Enables a mode in which you can scroll the contents of the slider using the scroll along the x-axis, while the active slider is centered relative to the slider. |\n| `isScrollBlocked` | boolean | `false` | Prevents the user from scrolling the slider. Useful when you need to prevent scrolling while the carousel is active. |\n| `isDraggable` | boolean | `false` | Adds the ability to change slides using dragging. Requires adding `hs-carousel:dragging:transition-none` class to the `hs-carousel-body`. Not working with `isSnap: true` option. |\n| `isRTL` | boolean | `false` | Turns on RTL (right-to-left) mode. |\n| `hasSnapSpacers` | boolean | `true` | Adds additional elements to create a constant centering effect. |\n| `slidesQty` | object \\| number | `1` | Allows to set the number of slides to display at a certain screen resolution if passed as an object (e.g., `{\"xs\": 1, \"md\": 2, \"lg\": 3}`). If passed as a number, the specified number of slides will be displayed for all screen resolutions. |\n| `slideBy` | object \\| number | `1` | Specifies how many slides to move when navigating through the carousel. Can be a number for all screen sizes or an object with breakpoint-specific values. |\n| `speed` | number | `4000` | Autoplay animation speed in milliseconds. Available if `isAutoplay: true`. |\n| `updateDelay` | number | `0` | Allows you to delay the update function when resizing a window. Suitable for a slider with images, for more accurate calculation of the size of the images. |\n| `mode` | `\"default\"` \\| `\"snap\"` \\| `\"bounded\"` | `\"default\"` | Defines the carousel's behavior mode. `default` for standard sliding, `snap` for scroll-based navigation with snap points, and `bounded` for a fixed-width container with responsive slides. |\n| `boundedOptions` | object | `null` | Configuration options for the bounded mode carousel. Includes `maxWidth` to set the maximum width of the carousel container, `slidesGap` to define spacing between slides, and `spacersWidth` to control the width of side spacers. When `spacersWidth` is set to `\"auto\"`, it automatically calculates equal spacing on both sides. |\n\n**Example:**\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 3000,\n  \"slidesQty\": {\"xs\": 1, \"md\": 2, \"lg\": 3},\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the carousel to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel` | Wrapper container | A wrapper for a collection of slides. Must contain the `overflow-hidden` class for correct functionality. |\n| `hs-carousel-body` | Slides container | Container for all slide elements. |\n| `hs-carousel-slide` | Each slide element | Identifies an individual slide. |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel-prev` | Previous button | Previous slide navigation button. |\n| `hs-carousel-next` | Next button | Next slide navigation button. |\n| `hs-carousel-pagination` | Pagination container | Pagination container. If it doesn't contain nested elements, dots are generated automatically. If it contains elements with `hs-carousel-pagination-item` class, they will act as dots. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-carousel-active:*` | A modifier that allows you to set Tailwind classes when the active slide was shown. |\n| `hs-carousel-disabled:*` | A modifier that allows you to set Tailwind classes for arrow buttons when they are `disabled`. |\n| `hs-carousel:dragging:*` | A modifier that allows you to set Tailwind classes for `hs-carousel-body` when dragging. |\n\n## JavaScript API\n\nThe `HSCarousel` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a carousel instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `goToPrev()` | None | `void` | Navigates to the previous slide. |\n| `goToNext()` | None | `void` | Navigates to the next slide. |\n| `goTo(index)` | `index`: `number` | `void` | Navigates to the slide at the specified index. Index starts from 0. |\n| `recalculateWidth()` | None | `void` | Recalculates the width of the carousel. Useful after dynamically adding or removing slides, or when window is resized. |\n| `destroy()` | None | `void` | Destroys the carousel instance, removes all generated markup, classes, and event listeners. Use when removing carousel from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCarousel` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCarousel.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCarousel \\| { id: string \\| number, element: HSCarousel } \\| null` | Returns the carousel instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCarousel` instance. If `isInstance` is `false` or omitted, returns the `HSCarousel` instance directly. Returns `null` if carousel instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new carousel instance\nconst carousel = new HSCarousel(document.querySelector('#hs-carousel'));\nconst goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n\n// Navigate to slide by index\ngoToSecondBtn.addEventListener('click', () => {\n  carousel.goTo(1); // Go to second slide (index 1)\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCarousel instance\n  const goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n  const prevBtn = document.querySelector('#hs-prev-btn');\n  const nextBtn = document.querySelector('#hs-next-btn');\n\n  // Navigate to specific slide\n  goToSecondBtn.addEventListener('click', () => {\n    element.goTo(1);\n  });\n\n  // Navigate to previous slide\n  prevBtn.addEventListener('click', () => {\n    element.goToPrev();\n  });\n\n  // Navigate to next slide\n  nextBtn.addEventListener('click', () => {\n    element.goToNext();\n  });\n\n  // Recalculate width after dynamic changes\n  function updateCarousel() {\n    element.recalculateWidth();\n  }\n\n  // Clean up when removing from DOM\n  function removeCarousel() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying carousel instance**\n```javascript\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-carousel').remove();\n  });\n}\n```\n\n## Events\n\nCarousel instances emit events that can be listened to for slide lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | After the active slide changes | `number` (zero-based index of the current slide) | Fires after navigating to the next or previous slide, or after `goTo()` is called. |\n\n### Event Usage Example\n\n```javascript\n// Get carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for slide changes\n  element.on('update', (currentIndex) => {\n    console.log('Active slide:', currentIndex);\n    // Update external indicators, counters, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Responsive Carousel\n\nDisplay different number of slides at different breakpoints.\n\n```html\n<div data-hs-carousel='{\n  \"slidesQty\": {\"xs\": 1, \"sm\": 2, \"md\": 3, \"lg\": 4}\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 2: Autoplay Carousel\n\nAutomatically advance slides at regular intervals.\n\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 5000,\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl carousel from external buttons.\n\n```html\n<div id=\"hs-carousel-first\" data-hs-carousel>\n  <!-- Carousel content -->\n</div>\n\n<button id=\"hs-go-to-first\">Go to First</button>\n<button id=\"hs-go-to-last\">Go to Last</button>\n\n<script>\n  const instance = HSCarousel.getInstance('#hs-carousel-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-go-to-first').addEventListener('click', () => {\n      element.goTo(0);\n    });\n\n    document.querySelector('#hs-go-to-last').addEventListener('click', () => {\n      // Assuming 5 slides (indices 0-4)\n      element.goTo(4);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3611,"sizeBytes":15174},{"name":"README.md","path":"src/plugins/collapse/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/collapse/README.md","title":"collapse Documentation","category":"plugin-manifest","format":"markdown","content":"# Collapse\n\nToggle the visibility of content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/collapse) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/collapse.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Collapse component allows you to show and hide content with smooth transitions. It's a simple, lightweight solution for creating expandable/collapsible sections without the complexity of accordion groups.\n\n**Key Features:**\n- Simple show/hide functionality\n- Smooth CSS transitions\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Collapse plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/collapse\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/collapse */\n@source \"../node_modules/@preline/collapse/*.js\";\n@import \"./node_modules/@preline/collapse/variants.css\";\n@import \"./node_modules/@preline/collapse/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/collapse/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCollapse from \"@preline/collapse/non-auto.mjs\";\n\n  new HSCollapse(document.querySelector(\"#collapse\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/collapse` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/collapse\";\n```\n\n`@preline/collapse/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCollapse from \"@preline/collapse/non-auto\";\n\nHSCollapse.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#collapse\");\nif (el) new HSCollapse(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a collapse component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The content is hidden by default and expands when the button is clicked.\n\n```html\n<button type=\"button\" class=\"hs-collapse-toggle\" id=\"hs-unstyled-collapse\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-heading\" data-hs-collapse=\"#hs-unstyled-collapse-heading\" role=\"region\">\n  Collapse\n</button>\n<div id=\"hs-unstyled-collapse-heading\" class=\"hs-collapse hidden w-full overflow-hidden transition-[height] duration-300\" aria-labelledby=\"hs-unstyled-collapse\">\n  This is a collapse body. The content expands and collapses when the toggle button is clicked, with smooth height transitions.\n</div>\n```\n\n**Structure Requirements:**\n- `hs-collapse-toggle`: Required button element that triggers the collapse\n- `hs-collapse`: Required container for collapsible content\n- `data-hs-collapse`: Required attribute on the toggle button, must be a valid CSS selector pointing to the collapse content\n- Unique `id` attributes for the toggle button and collapse content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the collapse content initially\n- Content visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure collapse behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-collapse` | `hs-collapse-toggle` (button) | string (CSS selector) | `null` | Collapse container selector. Must be a valid CSS selector pointing to the `hs-collapse` element. |\n\n**Example:**\n```html\n<button data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the collapse to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-collapse` | Content container | Identifies the collapsible content area |\n| `hs-collapse-toggle` | Button element | The clickable element that expands/collapses content |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-collapse-open:*` | A modifier that allows you to set Tailwind classes when the collapse is open. |\n\n## JavaScript API\n\nThe `HSCollapse` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a collapse instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed content. Returns `false` if content is already open, `true` otherwise. |\n| `hide()` | None | `boolean` | Collapses the currently open content. Returns `false` if content is already closed, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the collapse instance, removes all generated markup, classes, and event listeners. Use when removing collapse from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCollapse` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCollapse.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSCollapse } \\| null` | Returns the collapse instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCollapse` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if collapse instance is not found. |\n| `HSCollapse.show(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSCollapse.hide(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new collapse instance\nconst collapse = new HSCollapse(document.querySelector('#hs-collapse'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse programmatically\nshowBtn.addEventListener('click', () => {\n  collapse.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSCollapse.show('#hs-collapse');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCollapse instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeCollapse() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying collapse instance**\n```javascript\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-collapse').remove();\n  });\n}\n```\n\n## Events\n\nCollapse instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After collapse content is opened | `HTMLElement` (collapse element) | Fires after the collapse content has been displayed. Use for post-open actions like loading content. |\n| `on:hide` | After collapse content is closed | `HTMLElement` (collapse element) | Fires after the collapse content has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (collapseElement) => {\n    console.log('Collapse opened:', collapseElement);\n    // Perform actions after collapse opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to hide event\n  element.on('hide', (collapseElement) => {\n    console.log('Collapse closed:', collapseElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Programmatic Control\n\nControl collapse from external buttons.\n\n```html\n<button class=\"hs-collapse-toggle\" data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n\n<button id=\"hs-show-collapse\">Show</button>\n<button id=\"hs-hide-collapse\">Hide</button>\n\n<script>\n  const instance = HSCollapse.getInstance('#hs-collapse-content', true);\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-collapse').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-collapse').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2497,"sizeBytes":11132},{"name":"README.md","path":"src/plugins/combobox/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/combobox/README.md","title":"combobox Documentation","category":"plugin-manifest","format":"markdown","content":"# ComboBox\n\nDynamic suggestions as users type in an input field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/combobox) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/combobox.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe ComboBox component provides an autocomplete input field with dynamic suggestions. It supports both client-side filtering and API-based data fetching, customizable templates, and flexible data rendering options.\n\n**Key Features:**\n- Dynamic autocomplete suggestions\n- Client-side and API-based data fetching\n- Customizable item templates\n- Keyboard navigation support\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install ComboBox plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/combobox\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/combobox */\n@source \"../node_modules/@preline/combobox/*.js\";\n@import \"./node_modules/@preline/combobox/variants.css\";\n@import \"./node_modules/@preline/combobox/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/combobox/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCombobox from \"@preline/combobox/non-auto.mjs\";\n\n  new HSCombobox(document.querySelector(\"#combobox\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/combobox` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/combobox\";\n```\n\n`@preline/combobox/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCombobox from \"@preline/combobox/non-auto\";\n\nHSCombobox.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#combobox\");\nif (el) new HSCombobox(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a combobox component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The combobox shows suggestions as the user types.\n\n```html\n<div class=\"inline-flex relative\" data-hs-combo-box>\n  <div class=\"relative\">\n    <input type=\"text\" role=\"combobox\" aria-expanded=\"false\" value=\"\" data-hs-combo-box-input />\n  </div>\n  \n  <div class=\"absolute z-50\" style=\"display: none;\" data-hs-combo-box-output>\n    <div class=\"cursor-pointer\" tabindex=\"0\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"1\" data-hs-combo-box-value>1</span>\n    </div>\n    <div class=\"cursor-pointer\" tabindex=\"1\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"2\" data-hs-combo-box-value>2</span>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-combo-box`: Required on the container element\n- `data-hs-combo-box-input`: Required on the input element\n- `data-hs-combo-box-output`: Required on the dropdown container\n- `data-hs-combo-box-output-item`: Required on each suggestion item\n- `data-hs-combo-box-search-text`: Required on the searchable text element within each item\n- Proper ARIA attributes (`role=\"combobox\"`, `aria-expanded`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the input initially\n- Add `style=\"display: none;\"` to the output container initially\n- Output visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-combo-box` attribute and various data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-combo-box` | Container | - | - | Activate a ComboBox by specifying on an element. Should be added to the container. |\n| `:gap` | Inside `data-hs-combo-box` | number | `5` | Determines the indent from the input field to the dropdown list in pixels. |\n| `:viewport` | Inside `data-hs-combo-box` | string \\| HTMLElement | `null` | Determines which element will be the parent when calculating the position of the dropdown list. By default it is calculated relative to the document. |\n| `:minSearchLength` | Inside `data-hs-combo-box` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:apiUrl` | Inside `data-hs-combo-box` | string \\| null | `null` | Specifies the API address for fetching suggestions. |\n| `:apiDataPart` | Inside `data-hs-combo-box` | string \\| null | `null` | Determines which part of the returned data will be taken as the main data array. If this parameter is empty, then the root is taken as a basis. |\n| `:apiQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request parameters, such as limit. |\n| `:apiSearchQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines the name of the search parameter. |\n| `:apiHeaders` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request headers. |\n| `:apiSearchPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that specifies the endpoint path for the API request, excluding the base URL. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:apiSearchDefaultPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that defines the default API endpoint path to be used when the input field is empty. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:outputItemTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for a single item in a dropdown list. |\n| `:outputEmptyTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for an empty placeholder. |\n| `:outputLoaderTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for the loader when loading data. |\n| `:preventAutoPosition` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it disables dropdown auto-positioning. |\n| `:preventClientFiltering` | Inside `data-hs-combo-box` | boolean | `true` (if `:apiSearchQuery` is not empty) | If `true`, it disables client-side filtering. |\n| `:keepOriginalOrder` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it keeps the original order of items. |\n| `:preserveSelectionOnEmpty` | Inside `data-hs-combo-box` | boolean | `true` | If `true`, it preserves the selected value when the input is empty. |\n| `data-hs-combo-box-input` | Input element | - | - | Determines which element (input) inside the initialized container will be responsible for entering data. |\n| `data-hs-combo-box-output` | Output container | - | - | Determines which element inside the initialized container will be responsible for data output. |\n| `data-hs-combo-box-toggle` | Toggle element | - | - | Defines an element inside the initialized container that will be responsible for hiding/showing the dropdown list. |\n| `data-hs-combo-box-close` | Close element | - | - | Defines an element inside the initialized container that will be responsible for hiding the dropdown list. |\n| `data-hs-combo-box-open` | Open element | - | - | Defines an element inside the initialized container that will be responsible for showing the dropdown list. |\n| `data-hs-combo-box-output-item` | Item element | - | - | Defines an element inside the initialized container that will be responsible for displaying each individual data element. |\n| `data-hs-combo-box-search-text` | Search text element | - | - | Defines the element within which text will be searched. |\n| `data-hs-combo-box-value` | Value element | - | - | Defines an element whose text will be set as a value when selected. |\n| `data-hs-combo-box-output-item-field` | Item field | string \\| string[] | - | Defines a data field that will be taken to fill the element with text. |\n| `data-hs-combo-box-output-item-attr` | Item attribute | - | - | Defines the attributes that will be filled with the corresponding data. For example, the src attribute of an image. |\n| `:valueFrom` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the field from which data will be taken. |\n| `:attr` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the attribute where the data will be supplied. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-combo-box-active:*` | A modifier that allows you to set Tailwind classes when ComboBox has been opened. |\n| `hs-combo-box-has-value:*` | A modifier that allows you to set Tailwind classes when ComboBox's input has value. |\n| `hs-combo-box-selected:*` | A modifier that allows you to set Tailwind classes when `data-hs-combo-box-output-item` has been selected. |\n| `hs-combo-box-tab-active:*` | A modifier that allows you to set Tailwind classes to the active group item (tab) when `groupingType` is `tabs`. |\n\n## JavaScript API\n\nThe `HSComboBox` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a combobox instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentData()` | None | `object \\| object[]` | Returns the data of the selected element as an object. This data must first be assigned as a value to the data parameter `data-hs-combo-box-item-stored-data` when standard rendering. When rendering via the API, the data is automatically substituted into the data attribute. |\n| `setCurrent()` | None | `void` | Sets the current selection based on the input value. |\n| `open(val)` | `val`: `string` (optional) | `boolean` | Opens the autosuggestion list. Optionally accepts a value to set in the input. Returns `true` if opened successfully, `false` otherwise. |\n| `close(val, data)` | `val`: `string \\| null` (optional)<br>`data`: `object \\| null` (optional) | `boolean` | Closes the autosuggestion list. Optionally accepts a value and data to set. Returns `true` if closed successfully, `false` otherwise. |\n| `recalculateDirection()` | None | `void` | Forces recalculation of autosuggestion list positions. Useful after window resize or layout changes. |\n| `destroy()` | None | `void` | Destroys the combobox instance, removes all generated markup, classes, and event listeners. Use when removing combobox from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSComboBox` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSComboBox.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSComboBox \\| { id: string \\| number, element: HSComboBox } \\| null` | Returns the combobox instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSComboBox` instance. If `isInstance` is `false` or omitted, returns the `HSComboBox` instance directly. Returns `null` if combobox instance is not found. |\n| `HSComboBox.autoInit()` | None | `void` | Reinitializes all ComboBoxes on the page. Useful after dynamically adding comboboxes to DOM. |\n| `HSComboBox.close(target)` | `target`: `HTMLElement \\| string` (CSS selector) | `void` | Closes the combobox identified by target. Accepts a DOM element or CSS selector string. |\n| `HSComboBox.closeCurrentlyOpened()` | None | `void` | Closes an already open ComboBox. Useful for closing any open combobox before opening a new one. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new combobox instance\nconst comboBox = new HSComboBox(document.querySelector('#hs-combo-box'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open combobox programmatically\nopenBtn.addEventListener('click', () => {\n  comboBox.open();\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the combobox instance\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSComboBox instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Get selected data\n  const selectedData = element.getCurrentData();\n  console.log('Selected:', selectedData);\n\n  // Clean up when removing from DOM\n  function removeComboBox() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying combobox instance**\n```javascript\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-combo-box').remove();\n  });\n}\n```\n\n## Events\n\nCombobox instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:select` | When an item is selected from the dropdown | `object` (the selected item data object) | Fires after the user clicks an option in the combobox dropdown. |\n\n### Event Usage Example\n\n```javascript\n// Get combobox instance\nconst instance = HSCombobox.getInstance('#hs-combobox', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for item selection\n  element.on('select', (data) => {\n    console.log('Selected item:', data);\n    // Use selected data to update other parts of the UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: API-based ComboBox\n\nFetch suggestions from an API endpoint.\n\n```html\n<div data-hs-combo-box='{\n  \"apiUrl\": \"https://api.example.com/search\",\n  \"apiSearchQuery\": \"q\",\n  \"minSearchLength\": 2\n}'>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl combobox from external buttons.\n\n```html\n<div id=\"hs-combo-box-first\" data-hs-combo-box>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n\n<button id=\"hs-open-combo-box\">Open</button>\n<button id=\"hs-close-combo-box\">Close</button>\n\n<script>\n  const instance = HSComboBox.getInstance('#hs-combo-box-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-combo-box').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-combo-box').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3940,"sizeBytes":16144},{"name":"README.md","path":"src/plugins/copy-markup/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/copy-markup/README.md","title":"copy-markup Documentation","category":"plugin-manifest","format":"markdown","content":"# Copy Markup\n\nCopy input, select or other markups.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/copy-markup) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/copy-markup.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field.\n\n**Key Features:**\n- One-click element duplication\n- Configurable copy limits\n- Automatic element management\n- Programmatic control via JavaScript API\n- Event system for copy/delete tracking\n- Support for any HTML markup\n\n## Installation\n\nTo get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/copy-markup\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/copy-markup */\n@import \"./node_modules/@preline/copy-markup/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/copy-markup/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCopyMarkup from \"@preline/copy-markup/non-auto.mjs\";\n\n  new HSCopyMarkup(document.querySelector(\"#copy-markup\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/copy-markup` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/copy-markup\";\n```\n\n`@preline/copy-markup/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCopyMarkup from \"@preline/copy-markup/non-auto\";\n\nHSCopyMarkup.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#copy-markup\");\nif (el) new HSCopyMarkup(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element.\n\n```html\n<div id=\"hs-wrapper-for-copy-first\">\n  <input id=\"hs-content-for-copy-first\" type=\"text\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500\" placeholder=\"Enter Name\">\n</div>\n\n<button type=\"button\" data-hs-copy-markup='{\n    \"targetSelector\": \"#hs-content-for-copy-first\",\n    \"wrapperSelector\": \"#hs-wrapper-for-copy-first\",\n    \"limit\": 3\n  }' id=\"hs-copy-content-first\">\n  Add Name\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-copy-markup`: Required on the trigger button, contains configuration options as JSON\n- `targetSelector`: Required, must be a valid CSS selector pointing to the element to copy\n- `wrapperSelector`: Required, must be a valid CSS selector pointing to the container where copies will be added\n- Target element must exist in the DOM\n\n**Initial State:**\n- One instance of the target element exists\n- Copy button is enabled (unless limit is reached)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-copy-markup` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-copy-markup` | Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). |\n| `:targetSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. |\n| `:wrapperSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. |\n| `:limit` | Inside `data-hs-copy-markup` | number | `null` | Specifies how many items you can copy until the button is disabled. `null` means no limit. |\n\n**Example:**\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-input-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field\n</button>\n```\n\n## JavaScript API\n\nThe `HSCopyMarkup` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a copy markup instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `delete(target)` | `target`: `HTMLElement` | `void` | Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. |\n| `destroy()` | None | `void` | Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCopyMarkup` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCopyMarkup.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCopyMarkup \\| { id: string \\| number, element: HSCopyMarkup } \\| null` | Returns the copy markup instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCopyMarkup` instance. If `isInstance` is `false` or omitted, returns the `HSCopyMarkup` instance directly. Returns `null` if copy markup instance is not found. |\n\n### Usage Examples\n\n**Example 1: Deleting copied elements**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const deleteBtn = document.querySelector('#hs-delete-btn');\n\n  deleteBtn.addEventListener('click', () => {\n    // Delete a specific copied element\n    const copiedElement = document.querySelector('#hs-copy-markup-item-first');\n\n    if (copiedElement) {\n      element.delete(copiedElement);\n    }\n  });\n}\n```\n\n**Example 2: Using instance methods (recommended pattern)**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Items:', element.items);\n  console.log('Limit:', element.limit);\n\n  // Delete a copied element programmatically\n  function removeCopiedItem(itemElement) {\n    element.delete(itemElement);\n  }\n\n  // Clean up when removing from DOM\n  function removeCopyMarkup() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying copy markup instance**\n```javascript\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-copy-content').remove();\n  });\n}\n```\n\n## Events\n\nCopy markup instances emit events that can be listened to for copy/delete tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:copy` | When target element was copied | `HTMLElement` (copied element) | Fires when a new copy of the target element is created. Returns the newly created element. |\n| `on:delete` | When target element was deleted | `HTMLElement` (deleted element) | Fires when a copied element is deleted. Returns the deleted element. |\n\n### Event Usage Example\n\n```javascript\n// Get copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to copy event\n  element.on('copy', (copiedElement) => {\n    console.log('Element copied:', copiedElement);\n    // Perform actions after element is copied\n    // e.g., initialize other plugins on the new element, update counters\n  });\n\n  // Listen to delete event\n  element.on('delete', (deletedElement) => {\n    console.log('Element deleted:', deletedElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Limited Copies\n\nLimit the number of copies that can be created.\n\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field (Max 5)\n</button>\n```\n\n### Pattern 2: Dynamic Form Fields\n\nCreate dynamic form fields with copy functionality.\n\n```html\n<div id=\"hs-fields-wrapper\">\n  <input id=\"hs-field-template\" type=\"text\" placeholder=\"Field name\">\n</div>\n\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field-template\",\n  \"wrapperSelector\": \"#hs-fields-wrapper\"\n}'>\n  Add Another Field\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2394,"sizeBytes":10067},{"name":"README.md","path":"src/plugins/datatable/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/datatable/README.md","title":"datatable Documentation","category":"plugin-manifest","format":"markdown","content":"# Datatables\n\nDatatables solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datatable) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/datatables.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [CSS](#css)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Datatables component provides a powerful data table interface with sorting, searching, pagination, and row selection capabilities. It integrates with DataTables.net for advanced table functionality.\n\n**Key Features:**\n- Column sorting\n- Search functionality\n- Pagination\n- Row selection (single and multiple)\n- Customizable paging controls\n- Integration with DataTables.net\n- Programmatic control via JavaScript API\n- Event system for table interactions\n\n## Installation\n\nTo get started, install DataTable plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i jquery datatables.net-dt @preline/datatable\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/datatable */\n@source \"../node_modules/@preline/datatable/*.js\";\n@import \"./node_modules/@preline/datatable/variants.css\";\n@import \"./node_modules/@preline/datatable/theme.css\";\n\n.dt-layout-row:has(.dt-search),\n.dt-layout-row:has(.dt-length),\n.dt-layout-row:has(.dt-paging) {\n  display: none !important;\n}\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script src=\"./node_modules/@preline/datatable/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script type=\"module\">\n  import HSDataTable from \"@preline/datatable/non-auto.mjs\";\n\n  new HSDataTable(document.querySelector(\"#datatable\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `jQuery` and `DataTable` are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/datatable` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport \"@preline/datatable\";\n```\n\n`@preline/datatable/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport HSDataTable from \"@preline/datatable/non-auto\";\n\nHSDataTable.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datatable\");\nif (el) new HSDataTable(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a datatable component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The table supports sorting, searching, and pagination.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\">\n          Name\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\">\n          Age\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\" class=\"--exclude-from-ordering\">\n          Action\n        </th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>John Brown</td>\n        <td>45</td>\n        <td>\n          <button type=\"button\">Delete</button>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n\n  <div style=\"display: none;\" data-hs-datatable-paging>\n    <button type=\"button\" data-hs-datatable-paging-prev>\n      <span aria-hidden=\"true\">«</span>\n      <span class=\"sr-only\">Previous</span>\n    </button>\n    <div data-hs-datatable-paging-pages></div>\n    <button type=\"button\" data-hs-datatable-paging-next>\n      <span class=\"sr-only\">Next</span>\n      <span aria-hidden=\"true\">»</span>\n    </button>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-datatable`: Required on the container element\n- Standard HTML `<table>` structure with `<thead>` and `<tbody>`\n- Optional paging controls with `data-hs-datatable-paging`\n- Optional search input with `data-hs-datatable-search`\n- Optional row selection checkboxes\n\n**Initial State:**\n- Table displays all data\n- Sorting is enabled on columns (unless excluded)\n- Pagination controls are hidden if not needed\n\n## CSS\n\nPlease add the following CSS to the head. This is necessary because Datatables.net does not allow to add custom markup with options disabled, such as `paging: false` or `searching: false`.\n\n```html\n<style>\n  .dt-layout-row:has(.dt-search),\n  .dt-layout-row:has(.dt-length),\n  .dt-layout-row:has(.dt-paging) {\n    display: none !important;\n  }\n</style>\n```\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-datatable` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datatable` | Container | - | - | Activate a Datatable by specifying on an element. Should be added to the container. |\n| `:pageBtnClasses` | Inside `data-hs-datatable` | string | `\"-\"` | Sets the specified classes to each paging element inside the `data-hs-datatable-paging-pages`. |\n| `:rowSelectingOptions.selectAllSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-all]\"` | A valid CSS selector (the element must be a checkbox or radio button), when the element is changed, all elements specified by `rowSelectingOptions.individualSelector` (or, if not specified, then `data-hs-datatable-row-selecting-individual`) on the current page will be changed. |\n| `:rowSelectingOptions.individualSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-individual]\"` | A valid CSS selector (the element must be a checkbox or radio button), affects the change of the element specified in `rowSelectingOptions.selectAllSelector`, if not all elements are selected on the current page, then it switches to `unchecked` state, if vice versa, then in `checked`. |\n\n**Example:**\n```html\n<div data-hs-datatable='{\n  \"pageBtnClasses\": \"px-3 py-1 border rounded\",\n  \"rowSelectingOptions\": {\n    \"selectAllSelector\": \"#hs-select-all\",\n    \"individualSelector\": \".hs-row-checkbox\"\n  }\n}'>\n  <!-- Table structure -->\n</div>\n```\n\n### Class Options\n\nCSS class modifiers use `--` prefix to control datatable behavior.\n\n| Class Modifier | Target Element | Purpose |\n| --- | --- | --- |\n| `--exclude-from-ordering` | Table header (`<th>`) | A modifier that allows you to disable the ordering functionality in a column. Should be added to the thead column. |\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for specific features to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-datatable-search` | Input element | Defines which element will be responsible for searching in the datatable (must be an `input`). |\n| `data-hs-datatable-page-entities` | Select element | Determines which element will be responsible for switching the number of elements on the page (must be a `select`, depends on the change event). |\n| `data-hs-datatable-paging` | Container element | Defines a paging container, that contains pagination, next and previous buttons. |\n| `data-hs-datatable-paging-prev` | Button element | Defines a previous button element. |\n| `data-hs-datatable-paging-next` | Button element | Defines a next button element. |\n| `data-hs-datatable-row-selecting-all` | Checkbox/Radio element | Defines a \"Select all\" checkbox/radio, if it's not specified in the `rowSelectingOptions.selectAllSelector` parameter. |\n| `data-hs-datatable-row-selecting-individual` | Checkbox/Radio element | Defines a checkbox/radio that will be responsible for selecting a specific row if it is not specified in the `rowSelectingOptions.individualSelector` parameter. |\n| `data-hs-datatable-info` | Container element | Defines an info container. |\n| `data-hs-datatable-info-from` | Element | Defines an element that showing `start` number. |\n| `data-hs-datatable-info-to` | Element | Defines an element that showing `end` number. |\n| `data-hs-datatable-info-length` | Element | Defines an element that showing `recordsTotal` number. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-datatable-ordering-asc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `asc`. |\n| `hs-datatable-ordering-desc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `desc`. |\n\n## JavaScript API\n\nThe `HSDataTable` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a datatable instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the datatable instance, removes all generated markup, classes, and event listeners. Use when removing datatable from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDataTable` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDataTable.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDataTable } \\| null` | Returns the datatable instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDataTable` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datatable instance is not found. |\n\n### Usage Examples\n\n**Example 1: Accessing DataTables.net instance**\n```javascript\n// Get the datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying DataTables.net instance\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Use DataTables.net methods\n    dataTable.search('query').draw();\n  }\n}\n```\n\n**Example 2: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 3: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-datatable').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most events available in the plugin are available in our wrapper.\n\nDatatable instances emit events through the underlying DataTables.net instance. Access the DataTables.net instance via `element.dataTable` to listen to events.\n\n**Common DataTables.net Events:**\n- `draw` - When table is redrawn\n- `order` - When column ordering changes\n- `search` - When search is performed\n- `page` - When page changes\n- `select` - When row is selected\n- `deselect` - When row is deselected\n\n### Event Usage Example\n\n```javascript\n// Get datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Listen to draw event\n    dataTable.on('draw.dt', () => {\n      console.log('Table redrawn!');\n      // Perform actions after table redraws\n      // e.g., update UI, reinitialize plugins\n    });\n\n    // Listen to order event\n    dataTable.on('order.dt', () => {\n      console.log('Column ordering changed!');\n    });\n\n    // Listen to search event\n    dataTable.on('search.dt', () => {\n      console.log('Search performed!');\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Search\n\nAdd search functionality to the datatable.\n\n```html\n<input type=\"text\" data-hs-datatable-search placeholder=\"Search...\">\n\n<div data-hs-datatable>\n  <!-- Table structure -->\n</div>\n```\n\n### Pattern 2: With Row Selection\n\nEnable row selection with checkboxes.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-all>\n        </th>\n        <!-- Other columns -->\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-individual>\n        </td>\n        <!-- Other cells -->\n      </tr>\n    </tbody>\n  </table>\n</div>\n```\n\n### Pattern 3: Exclude Column from Ordering\n\nDisable sorting on specific columns.\n\n```html\n<th scope=\"col\" class=\"--exclude-from-ordering\">\n  Actions\n</th>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3846,"sizeBytes":15908},{"name":"README.md","path":"src/plugins/datepicker/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/datepicker/README.md","title":"datepicker Documentation","category":"plugin-manifest","format":"markdown","content":"# Advanced Datepicker\n\nCreate visually appealing and interactive datepickers in combination with the Datepicker JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datepicker) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-datepicker.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Datepicker component provides a comprehensive date selection interface with support for custom templates, multiple date formats, time selection, and integration with Vanilla Calendar Pro. It offers flexible configuration options for styling and behavior.\n\n**Key Features:**\n- Date and time selection\n- Custom select inputs for months, years, and time\n- Multiple date formats and locales\n- Custom templates for arrows and time display\n- Integration with Vanilla Calendar Pro\n- Programmatic control via JavaScript API\n- Utility classes support\n\n## Installation\n\nTo get started, install Datepicker plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select @preline/datepicker\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\nUse `styles.css` with `theme.css` for the default themed appearance:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles.css\";\n@import \"./node_modules/@preline/datepicker/theme.css\";\n```\n\nUse `styles-utility.css` instead when you plan to style the datepicker entirely with Tailwind utility classes. In this case `theme.css` is not needed:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles-utility.css\";\n```\n\n`@preline/select` is required for styling the embedded select dropdowns used in `custom-select` mode.\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/datepicker/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDatepicker from \"@preline/datepicker/non-auto.mjs\";\n\n  new HSDatepicker(document.querySelector(\"#datepicker\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `vanilla-calendar-pro` and the Advanced Select plugin are bundled inside `@preline/datepicker`, so no separate imports are needed.\n\n`@preline/datepicker` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/datepicker\";\n```\n\n`@preline/datepicker/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDatepicker from \"@preline/datepicker/non-auto\";\n\nHSDatepicker.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datepicker\");\nif (el) new HSDatepicker(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced datepicker component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The datepicker appears when the input is focused.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\" placeholder=\"\" \n  readonly\n  data-hs-datepicker='{\n    \"type\": \"default\",\n    \"dateMax\": \"2050-12-31\",\n    \"styles\": {\n      \"week\": \"vc-week\",\n      \"weekDay\": \"vc-week__day\",\n      \"dates\": \"vc-dates\",\n      \"date\": \"vc-date\",\n      \"arrowPrev\": \"vc-arrow vc-arrow_prev\",\n      \"arrowNext\": \"vc-arrow vc-arrow_next\"\n    },\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n**Structure Requirements:**\n- `hs-datepicker`: Required class on the input element\n- `data-hs-datepicker`: Required attribute containing configuration options as JSON\n- `readonly` attribute on the input to prevent manual editing\n- `type=\"text\"` on the input element\n\n**Initial State:**\n- Input field is empty by default\n- Datepicker appears when input is focused or clicked\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most options available in the plugin are available in our wrapper. Check [Vanilla Calendar Pro Options](https://vanilla-calendar.pro/docs/reference/settings/) for more details.\n\nData options are specified in the `data-hs-datepicker` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datepicker` | Input element | - | - | Activate a Datepicker by specifying on an element. Should be added to the element itself. |\n| `:mode` | Inside `data-hs-datepicker` | `\"default\"` \\| `\"custom-select\"` | `\"default\"` | This option allows you to specify the operational mode of the datepicker. `default` uses standard layout provided by Vanilla Calendar Pro. `custom-select` activates custom select inputs for months, years and time elements. |\n| `:applyUtilityClasses` | Inside `data-hs-datepicker` | boolean | `false` | Specifies whether to apply utility classes to the datepicker. If set to `true`, the datepicker receives utility classes based on the `primary` theme. |\n| `:inputModeOptions.dateSeparator` | Inside `data-hs-datepicker` | string | `\".\"` | Specifies the character used to separate the year, month, and day in the date format displayed in the input field. E.g., `/` produces a date format like `2024/01/22`. |\n| `:inputModeOptions.itemsSeparator` | Inside `data-hs-datepicker` | string | `\", \"` | Specifies the character or string used to separate multiple date entries in the input field when multiple dates are selected. E.g., `\\|` produces a format like `2024-01-22 \\| 2024-01-23`. |\n| `:styles.customSelect.shared` | Inside `data-hs-datepicker` | object | - | Defines shared styling configurations for Advanced Custom Select components. These styles are consistently applied to all instances of the Advanced Custom Select within the wrapper. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.months` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"months\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.years` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"years\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.hours` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"hours\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.minutes` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"minutes\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.meridiem` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"meridiem\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:templates.time` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for rendering the time selection interface within the datepicker. |\n| `:templates.arrowPrev` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"previous\" navigation arrow in the datepicker. |\n| `:templates.arrowNext` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"next\" navigation arrow in the datepicker. |\n| `:dateFormat` | Inside `data-hs-datepicker` | string | - | Allows to define a custom date format. |\n| `:dateLocale` | Inside `data-hs-datepicker` | string | - | Allows to define the locale for the datepicker. |\n| `:replaceTodayWithText` | Inside `data-hs-datepicker` | boolean | - | Replace the current day name with the text \"Today\". |\n\n**Example:**\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\",\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"dateLocale\": \"en\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-vc-date-today:*` | A modifier that applies Tailwind classes to the current date (marked as \"today\") using the attribute `data-vc-date-today`. |\n| `hs-vc-date-selected:*` | A modifier that applies Tailwind classes to the selected dates using the attribute `data-vc-date-selected`. |\n| `hs-vc-date-weekend:*` | A modifier that applies Tailwind classes to weekend dates using the attribute `data-vc-date-weekend`. |\n| `hs-vc-week-day-off:*` | A modifier that applies Tailwind classes to weekdays marked as \"day off\" using the attribute `data-vc-week-day-off`. |\n| `hs-vc-date-month-prev:*` | A modifier that applies Tailwind classes to dates belonging to the previous month using the class `data-vc-date-month=\"prev\"`. |\n| `hs-vc-date-month-next:*` | A modifier that applies Tailwind classes to dates belonging to the next month using the attribute `data-vc-date-month=\"next\"`. |\n| `hs-vc-calendar-selected-middle:*` | A modifier that applies Tailwind classes to dates that are in the middle of a selected range using the attribute `data-vc-date-selected=\"middle\"`. |\n| `hs-vc-calendar-selected-first:*` | A modifier that applies Tailwind classes to the first date in a selected range using the attribute `data-vc-date-selected=\"first\"`. |\n| `hs-vc-calendar-selected-last:*` | A modifier that applies Tailwind classes to the last date in a selected range using the attribute `data-vc-date-selected=\"last\"`. |\n| `hs-vc-months-month-selected:*` | A modifier that applies Tailwind classes to the currently selected month using the attribute `data-vc-months-month-selected`. |\n| `hs-vc-years-year-selected:*` | A modifier that applies Tailwind classes to the currently selected year using the attribute `data-vc-years-year-selected`. |\n\n## JavaScript API\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most methods available in the plugin are available through our wrapper. Check [Vanilla Calendar Pro Methods](https://vanilla-calendar.pro/docs/reference/methods/) for more details.\n\nThe `HSDatepicker` object is available in the global `window` object after the plugin is loaded. Most methods from Vanilla Calendar Pro are available through the wrapper.\n\n### Instance Methods\n\nThese methods are called on a datepicker instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `{ selectedDates: DatesArr, selectedTime: string }` | Returns the current state of the datepicker including selected dates and time. |\n| `formatDate(date, format)` | `date`: `string \\| number \\| Date`<br>`format`: `string` (optional) | `string` | Formats a date according to the specified format or the datepicker's default format. |\n| `destroy()` | None | `void` | Destroys the datepicker instance, removes all generated markup, classes, and event listeners. Use when removing datepicker from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDatepicker` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDatepicker.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDatepicker } \\| null` | Returns the datepicker instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDatepicker` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datepicker instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('Selected dates:', state.selectedDates);\n  console.log('Selected time:', state.selectedTime);\n}\n```\n\n**Example 2: Formatting dates**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Format a date\n  const formatted = element.formatDate(new Date(), 'YYYY-MM-DD');\n\n  console.log('Formatted date:', formatted);\n}\n```\n\n**Example 3: Destroying datepicker instance**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('.hs-datepicker').remove();\n  });\n}\n```\n\n## Events\n\nDatepicker instances emit events that can be listened to for date selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected date or time changes | `{ selectedDates: string[], selectedTime: string }` (current selection state) | Fires after the user picks a date or changes the time. The payload matches the shape returned by `getCurrentState()`. |\n\n### Event Usage Example\n\n```javascript\n// Get datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for date or time changes\n  element.on('change', ({ selectedDates, selectedTime }) => {\n    console.log('Selected dates:', selectedDates);\n    console.log('Selected time:', selectedTime);\n    // Update dependent fields, validate form, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Select Mode\n\nUse custom select inputs for months, years, and time.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n### Pattern 2: Custom Date Format\n\nSet a custom date format and separator.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3792,"sizeBytes":16447},{"name":"README.md","path":"src/plugins/dropdown/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/dropdown/README.md","title":"dropdown Documentation","category":"plugin-manifest","format":"markdown","content":"# Dropdown\n\nA dropdown menu displays a list of actions and more with JavaScript dropdown plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/dropdown) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/dropdown.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (click, hover, contextmenu)\n- Floating UI integration for smart positioning\n- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/dropdown\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/dropdown */\n@source \"../node_modules/@preline/dropdown/*.js\";\n@import \"./node_modules/@preline/dropdown/variants.css\";\n@import \"./node_modules/@preline/dropdown/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/dropdown/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDropdown from \"@preline/dropdown/non-auto.mjs\";\n\n  new HSDropdown(document.querySelector(\"#dropdown\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/dropdown` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/dropdown\";\n```\n\n`@preline/dropdown/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDropdown from \"@preline/dropdown/non-auto\";\n\nHSDropdown.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#dropdown\");\nif (el) new HSDropdown(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.\n\n```html\n<div class=\"hs-dropdown relative inline-flex\">\n  <button id=\"hs-dropdown-unstyled\" type=\"button\" class=\"hs-dropdown-toggle inline-flex justify-center items-center gap-x-2\" aria-expanded=\"false\" aria-label=\"Menu\">\n    Actions\n  </button>\n\n  <div class=\"hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white\" role=\"menu\" aria-labelledby=\"hs-dropdown-unstyled\">\n    <a class=\"block\" href=\"#\">Newsletter</a>\n    <a class=\"block\" href=\"#\">Purchases</a>\n    <a class=\"block\" href=\"#\">Downloads</a>\n    <a class=\"block\" href=\"#\">Team Account</a>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-dropdown`: Required container wrapper\n- `hs-dropdown-toggle`: Required button element that triggers the dropdown\n- `hs-dropdown-menu`: Required container for dropdown menu items\n- Proper ARIA attributes (`aria-expanded`, `aria-label`, `role=\"menu\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the dropdown menu initially\n- Menu visibility is controlled by `hs-dropdown-open:*` modifier classes\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| Enter | Activates the selected menu item. |\n| ArrowUp / ArrowDown | Focuses previous/next non-disabled item. |\n| Home / End | Focuses first/last non-disabled item. |\n| Esc | Closes any open dropdown menus. |\n| A-Z / a-z | Focuses first item that matches keyboard input. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure dropdown behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-dropdown-transition` | `hs-dropdown` (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control dropdown behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--placement:*]` | `hs-dropdown` (container) | `\"auto\"` \\| `\"auto-start\"` \\| `\"auto-end\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-start\"` \\| `\"right-end\"` \\| `\"left\"` \\| `\"left-start\"` \\| `\"left-end\"` | `\"bottom\"` | Specifies the position of the menu when opened relative to the toggle button. |\n| `[--scope:*]` | `hs-dropdown` (container) | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `[--auto-close:*]` | `hs-dropdown` (container) | `\"inside\"` \\| `\"outside\"` \\| `\"false\"` \\| `\"true\"` | `\"true\"` | Specifies the zone, when clicked, which will close the menu. `inside` closes only when clicking inside, `outside` closes only when clicking outside, `true` closes on any click, `false` prevents auto-close. |\n| `[--has-autofocus:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening a dropdown. Set to `\"false\"` to disable autofocus. |\n| `[--strategy:*]` | `hs-dropdown` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"fixed\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--adaptive:*]` | `hs-dropdown` (container) | `\"none\"` \\| `\"adaptive\"` | `\"adaptive\"` | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |\n| `[--gpu-acceleration:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Disable/enable position calculation using the transform property. Set to `\"true\"` to enable GPU acceleration. |\n| `[--offset:*]` | `hs-dropdown` (container) | number | `10` | Sets the dropdown's bottom or top offset in pixels. |\n| `[--flip:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Flips the menu's placement when it starts to overlap its reference element. Set to `\"false\"` to disable flipping. |\n| `[--trigger:*]` | `hs-dropdown` (container) | `\"hover\"` \\| `\"click\"` \\| `\"contextmenu\"` | `\"click\"` | Event to trigger a dropdown. `hover` shows on mouse enter, `click` shows on click, `contextmenu` shows on right-click. |\n\n**Example:**\n```html\n<div class=\"hs-dropdown --trigger:hover --placement:top --auto-close:false\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the dropdown to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown` | Container wrapper | Groups the toggle and menu elements together |\n| `hs-dropdown-toggle` | Button element | The clickable element that activates the dropdown |\n| `hs-dropdown-menu` | Menu container | The dropdown menu that appears on trigger |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown-toggle-wrapper` | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a \"+\" button inside an existing Dropdown toggle button that opens a modal. |\n| `hs-dropdown-close` | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dropdown-open:*` | The modifier that allows you to set Tailwind classes when the dropdown menu is open. |\n\n## JavaScript API\n\nThe `HSDropdown` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a dropdown instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(target, openedViaKeyboard)` | `target`: `VirtualElement \\| HTMLElement` (optional)<br>`openedViaKeyboard`: `boolean` (optional) | `boolean` | Forces the dropdown menu to open programmatically. Returns `true` if opened successfully, `false` otherwise. |\n| `close(isAnimated)` | `isAnimated`: `boolean` (optional) | `boolean` | Forces the dropdown menu to close programmatically. `isAnimated` indicates whether the dropdown menu is closed with animation. Returns `true` if closed successfully, `false` otherwise. |\n| `forceClearState()` | None | `void` | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |\n| `destroy()` | None | `void` | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDropdown` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDropdown.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSDropdown \\| { id: string \\| number, element: HSDropdown } \\| null` | Returns the dropdown instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDropdown` instance. If `isInstance` is `false` or omitted, returns the `HSDropdown` instance directly. Returns `null` if dropdown instance is not found. |\n| `HSDropdown.open(target, openedViaKeyboard)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector)<br>`openedViaKeyboard`: `boolean` (optional) | `void` | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSDropdown.close(target)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new dropdown instance\nconst dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown programmatically\nopenBtn.addEventListener('click', () => {\n  dropdown.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSDropdown.open('#hs-dropdown');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSDropdown instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Clean up when removing from DOM\n  function removeDropdown() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying dropdown instance**\n```javascript\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-dropdown').remove();\n  });\n}\n```\n\n## Events\n\nDropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After dropdown menu is opened | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |\n| `on:close` | After dropdown menu is closed | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (dropdownInstance) => {\n    console.log('Dropdown opened:', dropdownInstance);\n    // Perform actions after dropdown opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (dropdownInstance) => {\n    console.log('Dropdown closed:', dropdownInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Trigger\n\nShow dropdown on hover instead of click.\n\n```html\n<div class=\"hs-dropdown --trigger:hover\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl dropdown from external buttons.\n\n```html\n<div class=\"hs-dropdown\" id=\"hs-dropdown-first\">\n  <button class=\"hs-dropdown-toggle\">Actions</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n\n<button id=\"hs-open-dropdown\">Open Dropdown</button>\n<button id=\"hs-close-dropdown\">Close Dropdown</button>\n\n<script>\n  const instance = HSDropdown.getInstance('#hs-dropdown-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-dropdown').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-dropdown').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3656,"sizeBytes":15736},{"name":"README.md","path":"src/plugins/file-upload/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/file-upload/README.md","title":"file-upload Documentation","category":"plugin-manifest","format":"markdown","content":"# File Upload\n\nTailwind CSS File Upload plugin using Dropzone library to enable drag-and-drop functionality, previews, and progress bars.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/file-upload) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/file-upload.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe File Upload component provides a drag-and-drop file upload interface with preview functionality, progress tracking, and integration with Dropzone.js. It supports multiple file uploads, file type validation, and customizable preview templates.\n\n**Key Features:**\n- Drag-and-drop file upload\n- File previews with thumbnails\n- Upload progress tracking\n- Multiple file support\n- File type and extension validation\n- Integration with Dropzone.js\n- Programmatic control via JavaScript API\n- Event system for upload tracking\n\n## Installation\n\nTo get started, install File Upload plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i lodash dropzone @preline/file-upload\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/file-upload */\n@source \"../node_modules/@preline/file-upload/*.js\";\n@import \"./node_modules/@preline/file-upload/variants.css\";\n@import \"./node_modules/@preline/file-upload/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script src=\"./node_modules/@preline/file-upload/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script type=\"module\">\n  import HSFileUpload from \"@preline/file-upload/non-auto.mjs\";\n\n  new HSFileUpload(document.querySelector(\"#file-upload\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `Dropzone` and `_` (Lodash) are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/file-upload` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport \"@preline/file-upload\";\n```\n\n`@preline/file-upload/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport HSFileUpload from \"@preline/file-upload/non-auto\";\n\nHSFileUpload.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#file-upload\");\nif (el) new HSFileUpload(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a file upload component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component supports drag-and-drop and file previews.\n\n```html\n<div id=\"hs-file-upload-first\" data-hs-file-upload='{\n  \"url\": \"/upload\"\n}'>\n  <template data-hs-file-upload-preview>\n    <button type=\"button\" data-hs-file-upload-remove>\n      Delete!\n    </button>\n    <span data-hs-file-upload-file-icon>\n      <img class=\"hidden\" data-dz-thumbnail />\n    </span>\n    <div>\n      <span data-hs-file-upload-file-name></span>.<span data-hs-file-upload-file-ext></span>\n    </div>\n    <div data-hs-file-upload-file-size></div>\n    <div role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" data-hs-file-upload-progress-bar>\n      <div style=\"width: 0\" data-hs-file-upload-progress-bar-pane></div>\n    </div>\n    <span data-hs-file-upload-progress-bar-value>0</span>%\n  </template>\n\n  <div class=\"cursor-pointer\" data-hs-file-upload-trigger>\n    Drop your file here or browse\n  </div>\n\n  <div data-hs-file-upload-previews></div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-file-upload`: Required on the container element, contains configuration options\n- `data-hs-file-upload-preview`: Required template element for file previews (should be a `<template>` element)\n- `data-hs-file-upload-trigger`: Required clickable element for file selection\n- `data-hs-file-upload-previews`: Required container for file preview items\n- Various data attributes for preview elements (file name, extension, size, progress, etc.)\n\n**Initial State:**\n- Upload trigger is visible\n- Previews container is empty\n- Template is hidden (it's a template element)\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-file-upload` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-file-upload` | Container | - | - | Activate a File Upload by specifying on an element. Should be added to the container. |\n| `:url` | Inside `data-hs-file-upload` | string | - | The URL where files will be uploaded. Required for file upload functionality. |\n| `:extensions` | Inside `data-hs-file-upload` | object | Includes markup. See the code. | A list of extensions and associated icons that will be added inside the container with the `data-hs-file-upload-file-icon` selector if they match the extension name. |\n| `:autoHideTrigger` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then `data-hs-file-upload-trigger` will disappear if at least one item is added to the upload and will appear if more than one item is not in the upload. |\n| `:singleton` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then the plugin will remove any previously uploaded file. It needs for emulate simple input file behavior (only one file at a time). |\n\n**Example:**\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/api/upload\",\n  \"autoHideTrigger\": true,\n  \"singleton\": false\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the file upload to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-trigger` | Clickable element | Determines which element inside the initialized element will act as a clickable element for file selection. |\n| `data-hs-file-upload-previews` | Container element | Determines which element inside the initialized element will act as a wrapper for uploaded items. |\n| `data-hs-file-upload-preview` | Template element | Determines which element inside the initialized element will act as a template for the uploading item. Should be a `template` HTML element. |\n\n### Optional CSS Classes / Data Attributes\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-clear` | Button element | Determines which element inside the initialized element will act as a button that deletes all files. |\n| `data-hs-file-upload-file-icon` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file icon defined in the `extensions` property. |\n| `data-hs-file-upload-file-name` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file name. |\n| `data-hs-file-upload-file-ext` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file extension. |\n| `data-hs-file-upload-file-size` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file size. |\n| `data-hs-file-upload-remove` | Button element inside preview | Determines which element inside the uploading item will act as a remove button. |\n| `data-hs-file-upload-reload` | Button element inside preview | Determines which element inside the uploading item will act as a re-upload button. |\n| `data-hs-file-upload-progress-bar` | Element inside preview | Determines which element inside the uploading item will act as a progressbar. |\n| `data-hs-file-upload-progress-bar-pane` | Element inside preview | Determines which element inside the uploading item will act as a progressbar pane, which width will dynamically increase during the uploading process. |\n| `data-hs-file-upload-progress-bar-value` | Element inside preview | Determines which element inside the uploading item will act as a current progress value, which will dynamically increase during the uploading process. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-file-upload-complete:*` | A modifier that allows you to set Tailwind classes to items that successfully uploaded. |\n\n## JavaScript API\n\nThe `HSFileUpload` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a file upload instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the file upload instance, removes all generated markup, classes, and event listeners. Use when removing file upload from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSFileUpload` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSFileUpload.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSFileUpload } \\| null` | Returns the file upload instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSFileUpload` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if file upload instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Accessing Dropzone instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying Dropzone instance\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Use Dropzone methods\n    dropzone.removeAllFiles();\n  }\n}\n```\n\n**Example 3: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-file-upload').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most events available in the plugin are available in our wrapper.\n\nFile upload instances emit events through the underlying Dropzone instance. Access the Dropzone instance via `element.dropzone` to listen to events.\n\n**Common Dropzone Events:**\n- `addedfile` - When a file is added\n- `removedfile` - When a file is removed\n- `uploadprogress` - During file upload\n- `success` - When file upload succeeds\n- `error` - When file upload fails\n- `complete` - When upload completes (success or error)\n\n### Event Usage Example\n\n```javascript\n// Get file upload instance\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Listen to complete event\n    dropzone.on('complete', (file) => {\n      console.log('File uploaded:', file);\n      // Perform actions after file upload completes\n      // e.g., update UI, show success message\n    });\n\n    // Listen to error event\n    dropzone.on('error', (file, errorMessage) => {\n      console.log('Upload error:', errorMessage);\n      // Handle upload errors\n    });\n\n    // Listen to success event\n    dropzone.on('success', (file, response) => {\n      console.log('Upload successful:', response);\n      // Handle successful upload\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Single File Upload\n\nAllow only one file at a time.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"singleton\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Pattern 2: Auto-hide Trigger\n\nHide trigger after files are added.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"autoHideTrigger\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3282,"sizeBytes":14296},{"name":"README.md","path":"src/plugins/input-number/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/input-number/README.md","title":"input-number Documentation","category":"plugin-manifest","format":"markdown","content":"# Input Number\n\nInput Number - input idea for Shop systems or so.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/input-number) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/input-number.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Input Number component provides a numeric input field with increment and decrement buttons. It's ideal for quantity selectors, cart systems, or any interface requiring numeric input with step controls.\n\n**Key Features:**\n- Numeric input with step controls\n- Increment/decrement buttons\n- Min/max value constraints\n- Custom step values\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Input Number plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/input-number\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/input-number */\n@source \"../node_modules/@preline/input-number/*.js\";\n@import \"./node_modules/@preline/input-number/variants.css\";\n@import \"./node_modules/@preline/input-number/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/input-number/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSInputNumber from \"@preline/input-number/non-auto.mjs\";\n\n  new HSInputNumber(document.querySelector(\"#input-number\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/input-number` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/input-number\";\n```\n\n`@preline/input-number/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSInputNumber from \"@preline/input-number/non-auto\";\n\nHSInputNumber.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#input-number\");\nif (el) new HSInputNumber(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an input number component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component includes an input field and increment/decrement buttons.\n\n```html\n<div data-hs-input-number>\n  <input type=\"text\" aria-roledescription=\"Number field\" value=\"1\" data-hs-input-number-input>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Decrease\" data-hs-input-number-decrement>\n    Minus\n  </button>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Increase\" data-hs-input-number-increment>\n    Add\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-input-number`: Required on the container element\n- `data-hs-input-number-input`: Required on the input element\n- `data-hs-input-number-increment`: Required on the increment button\n- `data-hs-input-number-decrement`: Required on the decrement button\n- Proper ARIA attributes (`aria-roledescription`, `aria-label`)\n\n**Initial State:**\n- Input field contains the initial value (e.g., `value=\"1\"`)\n- Buttons are enabled/disabled based on min/max constraints\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-input-number` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-input-number` | Container | - | - | Activate an Input Number by specifying on an element. Should be added to the container. |\n| `:min` | Inside `data-hs-input-number` | number \\| `\"-Infinity\"` | `0` | Defines the minimum possible value. `-Infinity` allows you to enter negative values without restrictions. |\n| `:max` | Inside `data-hs-input-number` | number \\| null | `null` | Defines the maximum possible value. `null` means no maximum limit. |\n| `:step` | Inside `data-hs-input-number` | number | `1` | Determines the step by which the value will increase or decrease when buttons are clicked. |\n| `:forceBlankValue` | Inside `data-hs-input-number` | boolean | `false` | Whether the input value should be blank when the value isn't set. When `true`, empty input shows blank instead of default value. |\n\n**Example:**\n```html\n<div data-hs-input-number='{\n  \"min\": 0,\n  \"max\": 100,\n  \"step\": 5,\n  \"forceBlankValue\": false\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the input number to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-input-number-input` | Input element | Identifies the numeric input field |\n| `data-hs-input-number-increment` | Button element | Identifies the increment button |\n| `data-hs-input-number-decrement` | Button element | Identifies the decrement button |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-input-number-disabled:*` | A modifier that allows you to set Tailwind classes when input's value is set to zero (or minimum value). |\n\n## JavaScript API\n\nThe `HSInputNumber` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an input number instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the input number instance, removes all generated markup, classes, and event listeners. Use when removing input number from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSInputNumber` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSInputNumber.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSInputNumber \\| { id: number, element: HSInputNumber } \\| null` | Returns the input number instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSInputNumber` instance. If `isInstance` is `false` or omitted, returns the `HSInputNumber` instance directly. Returns `null` if input number instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.inputValue);\n  console.log('Min value:', element.minInputValue);\n  console.log('Max value:', element.maxInputValue);\n  console.log('Step:', element.step);\n\n  // Clean up when removing from DOM\n  function removeInputNumber() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-input-number').remove();\n  });\n}\n```\n\n## Events\n\nInput number instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When input value was changed | `{ inputValue: number }` | Fires when the input value changes (via buttons or direct input). Returns an object with the `inputValue` containing the current numeric value. |\n\n### Event Usage Example\n\n```javascript\n// Get input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ inputValue }) => {\n    console.log('Input value changed:', inputValue);\n    // Perform actions after value changes\n    // e.g., update cart, calculate totals, validate\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Quantity Selector\n\nCreate a quantity selector for shopping carts.\n\n```html\n<div data-hs-input-number='{\n  \"min\": 1,\n  \"max\": 99,\n  \"step\": 1\n}'>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n```\n\n### Pattern 2: Negative Values Allowed\n\nAllow negative values by setting min to -Infinity.\n\n```html\n<div data-hs-input-number='{\n  \"min\": \"-Infinity\",\n  \"max\": null,\n  \"step\": 1\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Pattern 3: Handling Value Changes\n\nTrack value changes with event listener.\n\n```html\n<div id=\"hs-input-number-first\" data-hs-input-number>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n\n<script>\n  const instance = HSInputNumber.getInstance('#hs-input-number-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('change', ({ inputValue }) => {\n      console.log('Quantity:', inputValue);\n      // Update cart or perform other actions\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2567,"sizeBytes":10928},{"name":"README.md","path":"src/plugins/layout-splitter/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/layout-splitter/README.md","title":"layout-splitter Documentation","category":"plugin-manifest","format":"markdown","content":"# Layout Splitter\n\nCustomize your layout effortlessly by adjusting block sizes to your preference with Tailwind CSS Layout Splitters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/layout-splitter) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/layout-splitter.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Layout Splitter component provides resizable layout panels with drag handles. It allows users to adjust the size of layout sections horizontally or vertically, creating flexible and customizable interfaces.\n\n**Key Features:**\n- Horizontal and vertical resizing\n- Drag handles for intuitive resizing\n- Minimum size constraints\n- Pre-limit size detection\n- Responsive breakpoint support\n- Programmatic control via JavaScript API\n- Event system for drag tracking\n\n## Installation\n\nTo get started, install Layout Splitter plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/layout-splitter\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/layout-splitter */\n@source \"../node_modules/@preline/layout-splitter/*.js\";\n@import \"./node_modules/@preline/layout-splitter/variants.css\";\n@import \"./node_modules/@preline/layout-splitter/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/layout-splitter/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSLayoutSplitter from \"@preline/layout-splitter/non-auto.mjs\";\n\n  new HSLayoutSplitter(document.querySelector(\"#layout-splitter\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/layout-splitter` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/layout-splitter\";\n```\n\n`@preline/layout-splitter/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSLayoutSplitter from \"@preline/layout-splitter/non-auto\";\n\nHSLayoutSplitter.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#layout-splitter\");\nif (el) new HSLayoutSplitter(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a layout splitter component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The layout allows resizing blocks by dragging the splitter handle.\n\n```html\n<div data-hs-layout-splitter='{\n  \"horizontalSplitterTemplate\": \"<div><span class=\\\"absolute top-1/2 start-1/2 -translate-x-2/4 -translate-y-2/4 block w-[11px] h-5 bg-gray-400\\\"></span></div>\",\n  \"horizontalSplitterClasses\": \"relative flex border-s border-gray-200\"\n}'>\n  <div class=\"flex h-50\" data-hs-layout-splitter-horizontal-group>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"48.2\">Horizontal Block 1</div>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"30.0\">Horizontal Block 2</div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-layout-splitter`: Required on the container element, contains configuration options as JSON\n- `data-hs-layout-splitter-horizontal-group`: Required for horizontal splitting\n- `data-hs-layout-splitter-vertical-group`: Required for vertical splitting\n- `data-hs-layout-splitter-item`: Required on resizable blocks, accepts float number or object for initial size\n\n**Initial State:**\n- Blocks are sized according to `data-hs-layout-splitter-item` values\n- Splitter handles are visible and draggable\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-layout-splitter` attribute and on splitter items.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-layout-splitter` | Container | - | - | Activate a Layout Splitter by specifying on an element. Should be added to the container. |\n| `:horizontalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the horizontal splitter template. HTML markup for the drag handle. |\n| `:horizontalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the horizontal splitter CSS classes. CSS classes must be separated by a space. |\n| `:verticalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the vertical splitter template. HTML markup for the drag handle. |\n| `:verticalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the vertical splitter CSS classes. CSS classes must be separated by a space. |\n| `data-hs-layout-splitter-horizontal-group` | Container element | - | - | Defines a group of elements with horizontal resizable blocks. |\n| `data-hs-layout-splitter-vertical-group` | Container element | - | - | Defines a group of elements with vertical resizable blocks. |\n| `data-hs-layout-splitter-item` | Resizable block | float \\| object | - | Defines a block that can be resized depending on what group it is in. Also accepts a float number as a value to specify the initial width or height of the element. Can be an object with `dynamicSize`, `minSize`, `preLimitSize` properties. |\n| `:dynamicSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the initialized block size. |\n| `:minSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the size at which further narrowing of the block will be prevented. |\n| `:preLimitSize` | Inside `data-hs-layout-splitter-item` | float | - | Determines the size at which the `onPrevPreLimit` event will be called. For example, on this event you can add specific classes to the block to the right or left of the divider. |\n\n**Example:**\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0,\n  \"preLimitSize\": 20.0\n}'>\n  Resizable Block\n</div>\n```\n\n## JavaScript API\n\nThe `HSLayoutSplitter` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a layout splitter instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getSplitterItemSingleParam(item, name)` | `item`: `HTMLElement` (splitter item element)<br>`name`: `string` (parameter name) | `any` | Retrieves the value of a specific parameter (name) from the data attribute of a splitter item (item). The parameter is extracted from the parsed JSON object stored in the `data-hs-layout-splitter-item` attribute. |\n| `getData(el)` | `el`: `HTMLElement` (splitter item element) | `object` | Retrieves detailed information about a splitter item, including its current size, minimum size, and pre-limit size, both in pixels and flex values. Returns a structured object. |\n| `setSplitterItemSize(el, size)` | `el`: `HTMLElement` (splitter item element)<br>`size`: `number` (percentage) | `void` | Updates the size of a specified splitter item (`el`) to the given value (size). The new size is converted into a flex value and applied to the element. The method also updates the corresponding data attribute (`data-hs-layout-splitter-item`). |\n| `updateFlexValues(data)` | `data`: `Array<{ id: string, breakpoints: Record<number, number> }>` | `void` | Updates the flex values of splitter items based on the provided configuration. Each configuration object specifies an element's ID and its size values for different screen widths (breakpoints). The method dynamically adjusts the flex values of the specified elements to match the current screen width. |\n| `destroy()` | None | `void` | Cleans up and removes all event listeners, styles, and data attributes associated with the layout splitter. This method restores the affected elements to their original state and releases resources. |\n\n### Static Methods\n\nThese methods are called directly on the `HSLayoutSplitter` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSLayoutSplitter.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSLayoutSplitter } \\| null` | Returns the layout splitter instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSLayoutSplitter` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if layout splitter instance is not found. |\n| `HSLayoutSplitter.autoInit()` | None | `void` | Reinitializes all Layout Splitters on the page. Useful after dynamically adding layout splitters to DOM. |\n\n### Usage Examples\n\n**Example 1: Getting splitter item data**\n```javascript\n// Get the layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n  // Get item data\n  const data = element.getData(item);\n\n  console.log('Item data:', data);\n}\n```\n\n**Example 2: Setting splitter item size**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n\n  // Set item size to 50%\n  element.setSplitterItemSize(item, 50);\n}\n```\n\n**Example 3: Responsive breakpoints**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  // Update flex values based on window size\n  const updateLayoutSplitter = (splitter) => {\n    splitter.updateFlexValues([\n      {\n        id: \"hs-section-first\",\n        breakpoints: {\n          \"0\": 0,\n          \"1280\": 15.0\n        }\n      },\n      {\n        id: \"hs-section-second\",\n        breakpoints: {\n          \"0\": 0,\n          \"1024\": 25.4,\n          \"1280\": 21.0\n        }\n      },\n      {\n        id: \"hs-section-third\",\n        breakpoints: {\n          \"0\": 100,\n          \"1024\": 74.6,\n          \"1280\": 64.0\n        }\n      }\n    ]);\n  };\n\n  window.addEventListener('resize', () => {\n    if (instance) updateLayoutSplitter(instance.element);\n  });\n}\n```\n\n**Example 4: Destroying layout splitter instance**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-layout-splitter').remove();\n  });\n}\n```\n\n## Events\n\nLayout splitter instances emit events that can be listened to for drag tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:drag` | When the splitter is dragged | `{ prev: HTMLElement, next: HTMLElement, previousSize: number, previousFlexSize: number, nextSize: number, nextFlexSize: number }` | Fires when the splitter is being dragged. Returns an object with previous and next elements and their sizes. |\n| `on:onPrevLimit` | When the previous section size limit is reached | `HTMLElement` (previous element) | Fires when the previous section reaches its minimum size limit. |\n| `on:onPrevPreLimit` | When the previous section size is less than `preLimitSize` | `HTMLElement` (previous element) | Fires when the previous section size is less than the `preLimitSize` parameter value. |\n| `on:onNextLimit` | When the next section size limit is reached | `HTMLElement` (next element) | Fires when the next section reaches its minimum size limit. |\n| `on:onNextPreLimit` | When the next section size is less than `preLimitSize` | `HTMLElement` (next element) | Fires when the next section size is less than the `preLimitSize` parameter value. |\n\n### Event Usage Example\n\n```javascript\n// Get layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to limit events\n  element.on('onPrevLimit', (prev) => {\n    prev.classList.add('hs-prev-limit-reached');\n  });\n\n  element.on('onPrevPreLimit', (prev) => {\n    prev.classList.add('hs-prev-pre-limit-reached');\n  });\n\n  // Listen to drag event\n  element.on('drag', ({ prev, previousFlexSize }) => {\n    if (previousFlexSize > 10) {\n      prev.classList.remove('hs-prev-limit-reached');\n    }\n\n    if (previousFlexSize > 20) {\n      prev.classList.remove('hs-prev-pre-limit-reached');\n    }\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Horizontal Splitter\n\nCreate horizontal resizable panels.\n\n```html\n<div data-hs-layout-splitter>\n  <div data-hs-layout-splitter-horizontal-group>\n    <div data-hs-layout-splitter-item=\"50\">Left Panel</div>\n    <div data-hs-layout-splitter-item=\"50\">Right Panel</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Minimum Size\n\nSet minimum size constraints.\n\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0\n}'>\n  Resizable Panel\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3432,"sizeBytes":14434},{"name":"README.md","path":"src/plugins/overlay/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/overlay/README.md","title":"overlay Documentation","category":"plugin-manifest","format":"markdown","content":"# Overlay\n\nThe Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/overlay) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/overlay.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Overlay component provides a flexible foundation for creating modals, drawers, sidebars, and other overlay-based UI elements. It handles backdrop management, focus trapping, keyboard navigation, and z-index stacking automatically.\n\n**Key Features:**\n- Modal and drawer support\n- Automatic backdrop management\n- Focus trapping and keyboard navigation (Tab, Esc)\n- Z-index stacking for multiple overlays\n- Responsive breakpoint support\n- Auto-close on screen size changes\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/overlay\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/overlay */\n@source \"../node_modules/@preline/overlay/*.js\";\n@import \"./node_modules/@preline/overlay/variants.css\";\n@import \"./node_modules/@preline/overlay/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/overlay/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSOverlay from \"@preline/overlay/non-auto.mjs\";\n\n  new HSOverlay(document.querySelector(\"#overlay\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/overlay` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/overlay\";\n```\n\n`@preline/overlay/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSOverlay from \"@preline/overlay/non-auto\";\n\nHSOverlay.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#overlay\");\nif (el) new HSOverlay(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an overlay component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The overlay appears when the button is clicked.\n\n```html\n<button type=\"button\" aria-haspopup=\"dialog\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-modal\" data-hs-overlay=\"#hs-unstyled-modal\">\n  Open modal\n</button>\n\n<div id=\"hs-unstyled-modal\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-60 overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"hs-unstyled-modal-label\">\n  <div class=\"hs-overlay-open:opacity-100 hs-overlay-open:duration-500 opacity-0 transition-all sm:max-w-lg sm:w-full m-3 sm:mx-auto\">\n    <div class=\"pointer-events-auto\">\n      <h3 id=\"hs-unstyled-modal-label\">Title</h3>\n      Modal content\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-overlay`: Required on the trigger button, must be a valid CSS selector pointing to the overlay element\n- `hs-overlay`: Required class on the overlay content container\n- `hidden` class on the overlay initially\n- Proper ARIA attributes (`aria-haspopup`, `aria-expanded`, `aria-controls`, `role=\"dialog\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the trigger button initially\n- Add `hidden` class to the overlay initially\n- Overlay visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure overlay behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-overlay-options` | Trigger button | object (JSON) | - | Defines the modal configuration. Should be added to the button (trigger). |\n| `:hiddenClass` | Inside `data-hs-overlay-options` | string | `\"hidden\"` | Defines which classes will be added/removed when modal toggles. |\n| `:emulateScrollbarSpace` | Inside `data-hs-overlay-options` | boolean | `false` | If `true`, then adds right padding to the body equal to the size of the scrollbar. Useful to prevent layout shift when overlay opens. |\n| `:isClosePrev` | Inside `data-hs-overlay-options` | boolean | `true` | Determines whether the previous open modal will be closed when the next one is opened. |\n| `:backdropClasses` | Inside `data-hs-overlay-options` | string | `\"hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80\"` | Defines default backdrop classes. |\n| `:backdropExtraClasses` | Inside `data-hs-overlay-options` | string | - | Allows to add additional classes besides those specified in `backdropClasses`. |\n| `:backdropParent` | Inside `data-hs-overlay-options` | string | - | Allows to specify the selector of the element where the backdrop will be generated. |\n| `:moveOverlayToBody` | Inside `data-hs-overlay-options` | number \\| null | `null` | Moves the overlay to the body tag if the screen width is less than the value specified. |\n| `:isToggleClassesImmediately` | Inside `data-hs-overlay-options` | boolean | `false` | When set to `true`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n| `data-hs-overlay-backdrop-container` | Overlay content | string (CSS selector) | `null` | Backdrop element selector. Should be added to the overlay (content). |\n| `data-hs-overlay-keyboard` | Overlay content | boolean | `true` | When set to `false`, the modal will not close when pressing the ESC button on the keyboard. Should be added to the overlay (content). |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control overlay behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--overlay-backdrop:*]` | Overlay content | `\"static\"` \\| null | `null` | When backdrop is set to `\"static\"`, the modal will not close when clicking outside it. Should be added to the overlay (content). |\n| `[--auto-hide:*]` | Overlay content | number | `0` | Milliseconds for auto-closing a modal. When set to `0`, the modal will not close automatically. Should be added to the overlay (content). |\n| `[--auto-close:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the overlay will close automatically. Should be added to the overlay (content). |\n| `[--auto-close-equality-type:*]` | Overlay content | `'less-than'` \\| null | `null` | This option determines the condition under which an overlay should automatically close based on the window's width. When set to `less-than`, the overlay will close if the window's width is less than or equal to the value defined in `[--auto-close:*]`. Otherwise the overlay will close if the window's width is greater than or equal to the `[--auto-close:*]` threshold. Should be added to the overlay (content). |\n| `[--opened:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the plugin will apply functionality for the initially opened overlay. Should be added to the overlay (content). |\n| `[--body-scroll:*]` | Overlay content | boolean | `true` | When set to `false`, the body scroll will be hidden as soon as the modal opens. Should be added to the overlay (content). |\n| `[--tab-accessibility-limited:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Restricts focus to elements within an overlay (or modal). Should be added to the overlay (content). |\n| `[--has-autofocus:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening an overlay. Should be added to the overlay (content). |\n| `[--has-dynamic-z-index:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, each newly opened element in the group will receive an incremented `z-index` inline, ensuring it appears above previously opened elements with the same base `z-index`. Should be added to the overlay (content). |\n| `[--is-layout-affect:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | Informs the plugin that the instance affects the website layout. Should be added to the overlay (content). |\n| `[--toggle-classes-immediately:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n\n**Example:**\n```html\n<div class=\"hs-overlay --overlay-backdrop:static --auto-hide:5000\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the overlay to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay` | Overlay content container | Identifies the overlay content element |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay-animation-target` | Element inside overlay | Defines an element inside the popup, after the end of whose animation the popup will be completely hidden. Has no parameters. |\n| `autofocus` | Input element inside overlay | Focus the first input in a modal with the `autofocus` attribute on opening. Must be added to an input element inside a modal window. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-overlay-open:*` | Defines classes that will be applied to any elements inside the `hs-overlay` when the overlay is open. |\n| `hs-overlay-backdrop-open:*` | Defines classes that will be applied to backdrop when the overlay is open. |\n| `hs-overlay-layout-open:*` | Defines classes that will be applied to any elements inside the body tag when the overlay is open. |\n\n## JavaScript API\n\nThe `HSOverlay` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an overlay instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(cb)` | `cb`: `Function \\| null` (optional) | `Promise<void>` | Forces the overlay to open programmatically. Optionally accepts a callback function that will be called after opening. Returns a Promise. |\n| `close(forceClose, cb)` | `forceClose`: `boolean` (optional)<br>`cb`: `Function \\| null` (optional) | `Promise<unknown>` | Forces the overlay to close programmatically. `forceClose` bypasses animations and closes immediately. Optionally accepts a callback function. Returns a Promise. |\n| `minify(isMinified, cb)` | `isMinified`: `boolean`<br>`cb`: `Function \\| null` (optional) | `void` | Minimizes or restores the overlay. Useful for drawer/sidebar components. |\n| `updateToggles()` | None | `void` | Updates the toggle buttons. Useful after dynamically adding or removing toggle buttons. |\n| `destroy()` | None | `void` | Destroys the overlay instance, removes all generated markup, classes, and event listeners. Use when removing overlay from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSOverlay` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSOverlay.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSOverlay } \\| null` | Returns the overlay instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSOverlay` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if overlay instance is not found. |\n| `HSOverlay.open(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSOverlay.close(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new overlay instance\nconst modal = new HSOverlay(document.querySelector('#hs-modal'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay programmatically\nopenBtn.addEventListener('click', () => {\n  modal.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSOverlay.open('#hs-modal');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSOverlay instance\n  const openBtn = document.querySelector('#hs-open-btn');\n  const closeBtn = document.querySelector('#hs-close-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  closeBtn.addEventListener('click', () => {\n    element.close();\n  });\n\n  // Clean up when removing from DOM\n  function removeOverlay() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying overlay instance**\n```javascript\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-modal').remove();\n  });\n}\n```\n\n## Events\n\nOverlay instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After overlay is opened | `HSOverlay` (overlay instance) | Fires after the overlay has been displayed. Use for post-open actions like loading content or focusing elements. |\n| `on:close` | After overlay is closed | `HSOverlay` (overlay instance) | Fires after the overlay has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (overlayInstance) => {\n    console.log('Overlay opened:', overlayInstance);\n    // Perform actions after overlay opens\n    // e.g., load content, focus input, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (overlayInstance) => {\n    console.log('Overlay closed:', overlayInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Static Backdrop\n\nPrevent closing when clicking outside the overlay.\n\n```html\n<div class=\"hs-overlay --overlay-backdrop:static\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 2: Auto-close on Screen Size\n\nClose overlay automatically at specific breakpoints.\n\n```html\n<div class=\"hs-overlay --auto-close:md --auto-close-equality-type:less-than\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl overlay from external buttons.\n\n```html\n<button id=\"hs-open-modal\">Open Modal</button>\n<button id=\"hs-close-modal\">Close Modal</button>\n\n<div id=\"hs-modal-first\" class=\"hs-overlay hidden\">\n  <!-- Overlay content -->\n</div>\n\n<script>\n  const instance = HSOverlay.getInstance('#hs-modal-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-modal').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-modal').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":4110,"sizeBytes":17643},{"name":"README.md","path":"src/plugins/pin-input/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/pin-input/README.md","title":"pin-input Documentation","category":"plugin-manifest","format":"markdown","content":"# PIN Input\n\nA PIN Input plugin that allows to fill in data for inputs.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/pin-input) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/pin-input.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe PIN Input component provides a multi-field input interface for entering PIN codes, verification codes, or similar numeric/alphanumeric sequences. It automatically moves focus between fields and supports paste operations.\n\n**Key Features:**\n- Multiple input fields for PIN entry\n- Automatic focus management\n- Paste support for bulk entry\n- Character validation via regex\n- Programmatic control via JavaScript API\n- Event system for completion tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install PIN Input plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/pin-input\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/pin-input */\n@source \"../node_modules/@preline/pin-input/*.js\";\n@import \"./node_modules/@preline/pin-input/variants.css\";\n@import \"./node_modules/@preline/pin-input/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/pin-input/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSPinInput from \"@preline/pin-input/non-auto.mjs\";\n\n  new HSPinInput(document.querySelector(\"#pin-input\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/pin-input` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/pin-input\";\n```\n\n`@preline/pin-input/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSPinInput from \"@preline/pin-input/non-auto\";\n\nHSPinInput.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#pin-input\");\nif (el) new HSPinInput(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a PIN input component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component consists of multiple input fields that automatically advance focus.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-pin-input`: Required on the container element\n- `data-hs-pin-input-item`: Required on each input field\n- All input fields should be direct children of the container\n\n**Initial State:**\n- Input fields are empty by default\n- Focus starts on the first input field (if `autofocus` class is used)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-pin-input` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-pin-input` | Container | - | - | Activate a Pin Input by specifying on an element. Should be added to the container. |\n| `:availableCharsRE` | Inside `data-hs-pin-input` | RegExp (string) | `\"^[a-zA-Z0-9]+$\"` | Regular expression string for available characters. Only characters matching this regex will be accepted. |\n| `data-hs-pin-input-item` | Input element | - | - | Determines which element inside the initialized container will be responsible for entering data. Should be added to the input. |\n\n**Example:**\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <!-- Input fields -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control PIN input behavior.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `autofocus` | One of the input fields | If one of the fields has this class, it will be focused when the page loads. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-pin-input-active:*` | The class is added to the input when the PIN has been set up (all fields are filled). |\n\n## JavaScript API\n\nThe `HSPinInput` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a PIN input instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the PIN input instance, removes all generated markup, classes, and event listeners. Use when removing PIN input from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSPinInput` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSPinInput.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSPinInput \\| { id: string \\| number, element: HSPinInput } \\| null` | Returns the PIN input instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSPinInput` instance. If `isInstance` is `false` or omitted, returns the `HSPinInput` instance directly. Returns `null` if PIN input instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.currentValue);\n  console.log('Items:', element.items);\n\n  // Clean up when removing from DOM\n  function removePinInput() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-pin-input').remove();\n  });\n}\n```\n\n## Events\n\nPIN input instances emit events that can be listened to for completion tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:completed` | When the PIN has been set up (all fields filled) | `{ currentValue: string }` | Fires when all input fields have been filled. Returns an object with the `currentValue` containing the complete PIN string. |\n\n### Event Usage Example\n\n```javascript\n// Get PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to completed event\n  element.on('completed', ({ currentValue }) => {\n    console.log('PIN completed:', currentValue);\n    // Perform actions after PIN is completed\n    // e.g., validate PIN, submit form, proceed to next step\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Numeric PIN Only\n\nRestrict input to numbers only.\n\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 2: Auto-focus First Field\n\nAutomatically focus the first field on page load.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item class=\"autofocus\">\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 3: Handling Completion\n\nHandle PIN completion with event listener.\n\n```html\n<div id=\"hs-pin-input-first\" data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n\n<script>\n  const instance = HSPinInput.getInstance('#hs-pin-input-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('completed', ({ currentValue }) => {\n      console.log('PIN entered:', currentValue);\n      // Validate or submit the PIN\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2421,"sizeBytes":10014},{"name":"README.md","path":"src/plugins/range-slider/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/range-slider/README.md","title":"range-slider Documentation","category":"plugin-manifest","format":"markdown","content":"# Advanced Range Slider\n\nAdvanced Range Slider solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/range-slider) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-range-slider.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Range Slider component provides a flexible range input interface for selecting numeric values within a specified range. It integrates with noUiSlider for advanced functionality including dual handles, custom formatting, and smooth animations.\n\n**Key Features:**\n- Single and dual handle support\n- Custom value formatting\n- Smooth animations and transitions\n- Integration with noUiSlider\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Advanced Range Slider plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i nouislider @preline/range-slider\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/range-slider */\n@source \"../node_modules/@preline/range-slider/*.js\";\n@import \"./node_modules/@preline/range-slider/variants.css\";\n@import \"./node_modules/@preline/range-slider/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script src=\"./node_modules/@preline/range-slider/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script type=\"module\">\n  import HSRangeSlider from \"@preline/range-slider/non-auto.mjs\";\n\n  new HSRangeSlider(document.querySelector(\"#range-slider\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `noUiSlider` is referenced as a global, so expose it on `window` before importing the plugin.\n\n`@preline/range-slider` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport \"@preline/range-slider\";\n```\n\n`@preline/range-slider/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport HSRangeSlider from \"@preline/range-slider/non-auto\";\n\nHSRangeSlider.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#range-slider\");\nif (el) new HSRangeSlider(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced range slider component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The slider allows selecting a value between 0 and 100.\n\n```html\n<div data-hs-range-slider='{\n  \"cssClasses\": {},\n  \"start\": 50,\n  \"connect\": \"lower\",\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n**Structure Requirements:**\n- `data-hs-range-slider`: Required on the container element, contains configuration options as JSON\n- Container element will be automatically populated with slider markup by the plugin\n\n**Initial State:**\n- Slider starts at the value specified in `start` option\n- Range is defined by `min` and `max` in the `range` option\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin.\n\nData options are specified in the `data-hs-range-slider` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-range-slider` | Container element | - | - | Activate a Range Slider by specifying on an element. Should be added to the initialized element. Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin. |\n| `:disabled` | Inside `data-hs-range-slider` | boolean | `false` | Makes the element unavailable for interaction. When `true`, the slider cannot be moved. |\n| `:formatter` | Inside `data-hs-range-slider` | string \\| object | - | Output value formatting options. Can be a string or an object with formatting configuration. |\n| `:formatter:type` | Inside `data-hs-range-slider` | `\"integer\"` \\| `\"thousandsSeparatorAndDecimalPoints\"` \\| null | `null` | Defines an output formatter type. `integer` formats as integer, `thousandsSeparatorAndDecimalPoints` adds thousands separators and decimal points. |\n| `:formatter:prefix` | Inside `data-hs-range-slider` | string | - | Adds a prefix to the output value (e.g., \"$\" for currency). |\n| `:formatter:postfix` | Inside `data-hs-range-slider` | string | - | Adds a postfix to the output value (e.g., \"px\" for pixels). |\n\n**Example:**\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  },\n  \"disabled\": false\n}'></div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-range-slider-disabled:*` | A modifier that allows you to set Tailwind classes when slider has \"disabled\" attribute set to \"true\". |\n\n## JavaScript API\n\nThe `HSRangeSlider` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a range slider instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `formattedValue()` | None | `any` | Returns the current values formatted using the `formatter` option. Returns formatted values as specified in the formatter configuration. |\n| `destroy()` | None | `void` | Destroys the range slider instance, removes all generated markup, classes, and event listeners. Use when removing slider from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRangeSlider` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRangeSlider.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRangeSlider } \\| null` | Returns the range slider instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRangeSlider` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if range slider instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting formatted value**\n```javascript\n// Get the range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get formatted value\n  const formatted = element.formattedValue();\n\n  console.log('Formatted value:', formatted);\n}\n```\n\n**Example 2: Accessing noUiSlider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying noUiSlider instance\n  const noUiSlider = element.el.noUiSlider;\n\n  if (noUiSlider) {\n    // Use noUiSlider methods\n    const currentValue = noUiSlider.get();\n\n    console.log('Current value:', currentValue);\n  }\n}\n```\n\n**Example 3: Destroying range slider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-range-slider').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Note that all [events](https://refreshless.com/nouislider/events-callbacks/) available in the [noUiSlider](https://refreshless.com/nouislider/) plugin are also available in our plugin, just use `el.noUiSlider` to handle events.\n\nRange slider instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | When slider value is updated | `[integer, integer?]` | Fires when the slider value changes. Returns an array with current values. For single handle, array contains one value. For dual handles, array contains two values. |\n\n### Event Usage Example\n\n```javascript\n// Get range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to update event via noUiSlider\n  if (element.el.noUiSlider) {\n    element.el.noUiSlider.on('update', (values) => {\n      console.log('Slider updated:', values);\n      console.log('Formatted value:', element.formattedValue());\n      // Perform actions after slider value changes\n      // e.g., update UI, send to server, filter data\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dual Handle Range\n\nCreate a range slider with two handles for selecting a range.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n### Pattern 2: Formatted Values\n\nAdd prefix and postfix to slider values.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": 50,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  }\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2641,"sizeBytes":11075},{"name":"README.md","path":"src/plugins/remove-element/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/remove-element/README.md","title":"remove-element Documentation","category":"plugin-manifest","format":"markdown","content":"# Remove Element\n\nDismiss any content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/remove-element) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/remove-element.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Remove Element component provides a simple way to remove or dismiss elements from the DOM with optional animation. It's commonly used for dismissible alerts, notifications, modals, or any content that should be removable.\n\n**Key Features:**\n- One-click element removal\n- Optional removal animation\n- Programmatic control via JavaScript API\n- Simple configuration\n- Event system support\n\n## Installation\n\nTo get started, install Remove Element plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/remove-element\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/remove-element */\n@source \"../node_modules/@preline/remove-element/*.js\";\n@import \"./node_modules/@preline/remove-element/variants.css\";\n@import \"./node_modules/@preline/remove-element/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/remove-element/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSRemoveElement from \"@preline/remove-element/non-auto.mjs\";\n\n  new HSRemoveElement(document.querySelector(\"#remove-element\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/remove-element` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/remove-element\";\n```\n\n`@preline/remove-element/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSRemoveElement from \"@preline/remove-element/non-auto\";\n\nHSRemoveElement.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#remove-element\");\nif (el) new HSRemoveElement(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a remove element component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will remove the target element.\n\n```html\n<div id=\"hs-dismiss-unstyled-first\" class=\"inline-flex justify-between items-center w-full\">\n  <h3>\n    Remove element card\n  </h3>\n  <button type=\"button\" class=\"inline-flex justify-center items-center size-4\" data-hs-remove-element=\"#hs-dismiss-unstyled-first\">\n    <span class=\"sr-only\">Close</span>\n    <svg class=\"shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-remove-element`: Required on the trigger button, must be a valid CSS selector pointing to the element to remove\n- Target element must exist in the DOM\n- Button can be inside or outside the target element\n\n**Initial State:**\n- Target element is visible\n- Clicking the button removes the element\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-remove-element` | Trigger button | string (CSS selector) | - | The element to be removed. This must be a valid CSS selector. Should be added to the button (trigger). Required. |\n| `data-hs-remove-element-options` | Trigger button | object (JSON) | - | Options that can be added via the data attribute. Should be added to the button (trigger). |\n| `:removeTargetAnimationClass` | Inside `data-hs-remove-element-options` | string | `\"hs-removing\"` | Some valid CSS class that will be applied to the target element before removal. Useful for animations. |\n\n**Example:**\n```html\n<button data-hs-remove-element=\"#hs-alert\" data-hs-remove-element-options='{\n  \"removeTargetAnimationClass\": \"fade-out\"\n}'>\n  Close\n</button>\n```\n\n## JavaScript API\n\nThe `HSRemoveElement` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a remove element instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the remove element instance, removes all generated markup, classes, and event listeners. Use when removing remove element from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRemoveElement` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRemoveElement.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRemoveElement } \\| null` | Returns the remove element instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRemoveElement` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if remove element instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the remove element instance\nconst instance = HSRemoveElement.getInstance('[data-hs-remove-element]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Target element:', element.removeTarget);\n  console.log('Animation class:', element.removeTargetAnimationClass);\n\n  // Clean up when removing from DOM\n  function removeRemoveElement() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-remove-element').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dismissible Alert\n\nCreate a dismissible alert notification.\n\n```html\n<div id=\"hs-alert-first\" class=\"alert\">\n  <p>This is an alert message.</p>\n  <button data-hs-remove-element=\"#hs-alert-first\">×</button>\n</div>\n```\n\n### Pattern 2: With Animation\n\nAdd removal animation using CSS classes.\n\n```html\n<div id=\"hs-card-first\" class=\"card\">\n  <h3>Card Title</h3>\n  <button data-hs-remove-element=\"#hs-card-first\" data-hs-remove-element-options='{\n    \"removeTargetAnimationClass\": \"fade-out\"\n  }'>\n    Remove\n  </button>\n</div>\n\n<style>\n  .fade-out {\n    opacity: 0;\n    transition: opacity 0.3s;\n  }\n</style>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":1989,"sizeBytes":8565},{"name":"README.md","path":"src/plugins/scroll-nav/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/scroll-nav/README.md","title":"scroll-nav Documentation","category":"plugin-manifest","format":"markdown","content":"# Scroll Nav\n\nScroll Nav is a versatile and lightweight JavaScript plugin designed to enhance navigation menus with smooth scrolling capabilities.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scroll-nav) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scroll-nav.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scroll Nav component provides horizontal scrolling navigation with previous/next buttons and automatic centering. It's ideal for navigation menus with many items that need to be scrolled horizontally.\n\n**Key Features:**\n- Horizontal scrolling navigation\n- Previous/next navigation buttons\n- Automatic centering of active items\n- Programmatic control via JavaScript API\n- Smooth scrolling animations\n- Current state tracking\n\n## Installation\n\nTo get started, install Scroll Nav plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scroll-nav\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scroll-nav */\n@source \"../node_modules/@preline/scroll-nav/*.js\";\n@import \"./node_modules/@preline/scroll-nav/variants.css\";\n@import \"./node_modules/@preline/scroll-nav/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scroll-nav/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollNav from \"@preline/scroll-nav/non-auto.mjs\";\n\n  new HSScrollNav(document.querySelector(\"#scroll-nav\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scroll-nav` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scroll-nav\";\n```\n\n`@preline/scroll-nav/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollNav from \"@preline/scroll-nav/non-auto\";\n\nHSScrollNav.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scroll-nav\");\nif (el) new HSScrollNav(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scroll nav component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation items scroll horizontally with previous/next buttons.\n\n```html\n<div data-hs-scroll-nav class=\"relative px-6\">\n  <button type=\"button\" class=\"hs-scroll-nav-prev\">Previous</button>\n\n  <nav class=\"hs-scroll-nav-body flex flex-nowrap overflow-x-auto snap-x snap-mandatory\">\n    <a class=\"snap-start\" href=\"#\">Electronics</a>\n    <a class=\"snap-start\" href=\"#\">Clothing</a>\n    <a class=\"snap-start\" href=\"#\">House & Garden</a>\n    <a class=\"snap-start\" href=\"#\">Baby Care</a>\n    <a class=\"snap-start\" href=\"#\">Home appliances</a>\n    <a class=\"snap-start\" href=\"#\">Sports & Fitness</a>\n    <a class=\"snap-start\" href=\"#\">Constraction & Renovation</a>\n    <a class=\"snap-start\" href=\"#\">Food</a>\n    <a class=\"snap-start\" href=\"#\">Pharmacy</a>\n    <a class=\"snap-start\" href=\"#\">Pet Products</a>\n    <a class=\"snap-start\" href=\"#\">Books</a>\n    <a class=\"snap-start\" href=\"#\">Tourism, fishing, hunting</a>\n    <a class=\"snap-start\" href=\"#\">Furniture</a>\n    <a class=\"snap-start\" href=\"#\">Jewelery</a>\n    <a class=\"snap-start\" href=\"#\">Accessories</a>\n    <a class=\"snap-start\" href=\"#\">Games & Consoles</a>\n    <a class=\"snap-start\" href=\"#\">Stationery</a>\n    <a class=\"snap-start\" href=\"#\">Digital Goods</a>\n    <a class=\"snap-start\" href=\"#\">Music</a>\n  </nav>\n\n  <button type=\"button\" class=\"hs-scroll-nav-next\">Next</button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scroll-nav`: Required on the container element\n- `hs-scroll-nav-prev`: Required class on the previous button\n- `hs-scroll-nav-next`: Required class on the next button\n- `hs-scroll-nav-body`: Required class on the scrollable navigation container\n- Navigation items inside the scrollable container\n\n**Initial State:**\n- Navigation items are visible\n- Previous/next buttons are enabled/disabled based on scroll position\n- Active item can be centered automatically if `autoCentering` is enabled\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-scroll-nav` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scroll-nav` | Container | - | - | Activate a Scroll Nav by specifying on an element. Should be added to the container. |\n| `:autoCentering` | Inside `data-hs-scroll-nav` | boolean | `false` | Defines whether the Scroll Nav should align with the active item when the window loads. When `true`, the active item is automatically centered on page load. |\n\n**Example:**\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the scroll nav to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-scroll-nav-prev` | Previous button | Identifies the previous navigation button |\n| `hs-scroll-nav-next` | Next button | Identifies the next navigation button |\n| `hs-scroll-nav-body` | Scrollable container | Identifies the scrollable navigation container |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scroll-nav-active:*` | The class is applied to the navigation item to style the currently active element. |\n| `hs-scroll-nav-disabled:*` | The class is added to the prev/next buttons for style the disabled state. |\n\n## JavaScript API\n\nThe `HSScrollNav` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scroll nav instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `IScrollNavCurrentState` | Retrieves the currently visible first, last, and center elements within the scrollable navigation container. Returns an object with `first`, `last`, and `center` properties. |\n| `goTo(el, cb)` | `el`: `Element` (target element)<br>`cb`: `Function` (optional callback) | `void` | Smoothly scrolls the navigation container to bring the specified element into view. The callback function is executed after scrolling completes. |\n| `centerElement(el, behavior)` | `el`: `HTMLElement` (target element)<br>`behavior`: `ScrollBehavior` (optional, `\"auto\"` \\| `\"smooth\"`) | `void` | Scrolls the navigation container to position the specified element at the center of the visible area. Default behavior is `\"smooth\"`. |\n| `destroy()` | None | `void` | Destroys the scroll nav instance, removes all generated markup, classes, and event listeners. Use when removing scroll nav from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollNav` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollNav.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollNav } \\| null` | Returns the scroll nav instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollNav` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scroll nav instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the scroll nav instance\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('First visible:', state.first);\n  console.log('Last visible:', state.last);\n  console.log('Center visible:', state.center);\n}\n```\n\n**Example 2: Scrolling to an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Scroll to element\n  element.goTo(targetLink, () => {\n    console.log('Scrolled to element!');\n    // Perform actions after scrolling\n  });\n}\n```\n\n**Example 3: Centering an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Center element\n  element.centerElement(targetLink, 'smooth');\n}\n```\n\n**Example 4: Destroying scroll nav instance**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-scroll-nav').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Auto-centering Active Item\n\nAutomatically center the active item on page load.\n\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Pattern 2: Programmatic Navigation\n\nControl navigation programmatically.\n\n```html\n<div id=\"hs-scroll-nav-first\" data-hs-scroll-nav>\n  <!-- Navigation structure -->\n</div>\n\n<script>\n  const instance = HSScrollNav.getInstance('#hs-scroll-nav-first', true);\n  if (instance) {\n    const { element } = instance;\n    // Scroll to specific item\n    const item = document.querySelector('#hs-item-electronics');\n    \n    element.goTo(item);\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2614,"sizeBytes":11022},{"name":"README.md","path":"src/plugins/scrollspy/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/scrollspy/README.md","title":"scrollspy Documentation","category":"plugin-manifest","format":"markdown","content":"# Scrollspy\n\nAutomatically update navigation or list group components based on scroll position to indicate which link is currently active in the viewport.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scrollspy) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scrollspy.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scrollspy component automatically highlights navigation links based on the scroll position of the page. It tracks which section is currently visible in the viewport and updates the corresponding navigation item accordingly.\n\n**Key Features:**\n- Automatic navigation highlighting based on scroll position\n- Customizable scroll offset\n- Support for custom scrollable containers\n- Programmatic control via JavaScript API\n- Event system for scroll tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install Scrollspy plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scrollspy\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scrollspy */\n@source \"../node_modules/@preline/scrollspy/*.js\";\n@import \"./node_modules/@preline/scrollspy/variants.css\";\n@import \"./node_modules/@preline/scrollspy/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scrollspy/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollspy from \"@preline/scrollspy/non-auto.mjs\";\n\n  new HSScrollspy(document.querySelector(\"#scrollspy\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scrollspy` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scrollspy\";\n```\n\n`@preline/scrollspy/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollspy from \"@preline/scrollspy/non-auto\";\n\nHSScrollspy.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scrollspy\");\nif (el) new HSScrollspy(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scrollspy component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation links are automatically highlighted as you scroll through the sections.\n\n```html\n<div id=\"hs-scrollspy-scrollable-parent-first\" class=\"max-h-96 overflow-y-auto\">\n  <div data-hs-scrollspy=\"#hs-scrollspy-first\" class=\"flex flex-wrap gap-3\">\n    <a class=\"active\" href=\"#hs-first\">First</a>\n    <a href=\"#hs-second\">Second</a>\n    <a href=\"#hs-third\">Third</a>\n    <a href=\"#hs-fourth\">Fourth</a>\n    <a href=\"#hs-fifth\">Fifth</a>\n  </div>\n\n  <div id=\"hs-scrollspy-first\">\n    <div id=\"hs-first\" class=\"h-48\">\n      First\n    </div>\n    <div id=\"hs-second\" class=\"h-48\">\n      Second\n    </div>\n    <div id=\"hs-third\" class=\"h-48\">\n      Third\n    </div>\n    <div id=\"hs-fourth\" class=\"h-48\">\n      Fourth\n    </div>\n    <div id=\"hs-fifth\" class=\"h-48\">\n      Fifth\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scrollspy`: Required on the navigation container, must be a valid CSS selector pointing to the sections container\n- Navigation links with `href` attributes pointing to section IDs\n- Sections with matching `id` attributes\n- `active` class on the initially active navigation link\n\n**Initial State:**\n- Add `active` class to the navigation link that corresponds to the initially visible section\n- Ensure sections have sufficient height to enable scrolling\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure scrollspy behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scrollspy` | Navigation container | string (CSS selector) | - | A container containing sections. This must be a valid CSS selector pointing to the sections container. Should be added to the nav that contains Scrollspy links. |\n| `data-hs-scrollspy-scrollable-parent` | Navigation container | string (CSS selector) | `\"window\"` | Specifies the element to be scrolled. This must be a valid CSS selector. Should be added to the nav that contains Scrollspy links. Defaults to `window` if not specified. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control scrollspy behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--scrollspy-offset:*]` | Navigation container | number | `0` | Adds offset when scrolling to the section and to determine if the section is active. Useful for fixed headers or navigation bars. |\n\n**Example:**\n```html\n<div data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scrollspy-active:*` | A modifier that allows you to set Tailwind classes when the section is active for the corresponding link. |\n\n## JavaScript API\n\nThe `HSScrollspy` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scrollspy instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the scrollspy instance, removes all generated markup, classes, and event listeners. Use when removing scrollspy from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollspy` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollspy.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollspy } \\| null` | Returns the scrollspy instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollspy` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scrollspy instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying scrollspy instance**\n```javascript\nconst instance = HSScrollspy.getInstance('#hs-scrollspy', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Scrollspy links:', element.links);\n  console.log('Scrollspy sections:', element.sections);\n\n  // Clean up when removing from DOM\n  function removeScrollspy() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nScrollspy instances emit events that can be listened to for scroll tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeScroll` | Before scrolling begins | `HSScrollspy` (scrollspy instance) | Fires before scrolling to a section begins. Can return a Promise to delay scrolling until async operations complete. Useful for animations or other preparations. |\n\n### Event Usage Example\n\n```javascript\n// Get scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n  const collapse = HSCollapse.getInstance('[data-hs-collapse=\"#hs-navbar-collapse\"]', true);\n\n  // Listen to beforeScroll event\n  element.on('beforeScroll', (scrollspyInstance) => {\n    return new Promise((resolve) => {\n      if (collapse && collapse.element.el.classList.contains('open')) {\n        collapse.element.hide();\n        HSStaticMethods.afterTransition(collapse.element.content, () => resolve(true));\n      } else {\n        resolve(true);\n      }\n    });\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Scrollable Container\n\nUse scrollspy with a custom scrollable container instead of the window.\n\n```html\n<div id=\"hs-scrollable-container\" class=\"max-h-96 overflow-y-auto\">\n  <nav data-hs-scrollspy=\"#hs-sections\" data-hs-scrollspy-scrollable-parent=\"#hs-scrollable-container\">\n    <a href=\"#hs-section-first\">Section 1</a>\n    <a href=\"#hs-section-second\">Section 2</a>\n  </nav>\n  \n  <div id=\"hs-sections\">\n    <div id=\"hs-section-first\">Content 1</div>\n    <div id=\"hs-section-second\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Offset\n\nAdd offset for fixed navigation bars.\n\n```html\n<nav data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</nav>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2427,"sizeBytes":10259},{"name":"README.md","path":"src/plugins/select/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/select/README.md","title":"select Documentation","category":"plugin-manifest","format":"markdown","content":"# Advanced Select\n\nAdvanced select solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/select) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-select.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Select component provides a customizable dropdown select interface with support for search, tags, API integration, and extensive styling options. It's designed for handling large datasets and complex selection scenarios.\n\n**Key Features:**\n- Search functionality\n- Tags mode for multiple selections\n- API integration for dynamic options\n- Custom templates and styling\n- Multiple selection support\n- Option grouping\n- Icons and descriptions for options\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n\n## Installation\n\nTo get started, install Select plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select\n```\n\nSome positioning features require [Floating UI](https://floating-ui.com). Install it if you plan to use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`:\n\n```bash\nnpm i @floating-ui/dom\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, also load the [Floating UI](https://floating-ui.com) UMD bundle before the plugin. It exposes the `FloatingUIDOM` global the plugin relies on.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script src=\"./node_modules/@preline/select/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script type=\"module\">\n  import HSSelect from \"@preline/select/non-auto.mjs\";\n\n  new HSSelect(document.querySelector(\"#select\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, expose `FloatingUIDOM` on `window` before importing the plugin.\n\n`@preline/select` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport \"@preline/select\";\n```\n\n`@preline/select/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport HSSelect from \"@preline/select/non-auto\";\n\nHSSelect.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#select\");\nif (el) new HSSelect(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced select component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component transforms a standard select element into an advanced dropdown.\n\n```html\n<select data-hs-select='{\n    \"placeholder\": \"Select option...\",\n    \"toggleTag\": \"<button type=\\\"button\\\"></button>\",\n    \"toggleClasses\": \"\",\n    \"dropdownClasses\": \"\",\n    \"optionClasses\": \"hs-selected:\"\n  }'>\n  <option>Select option</option>\n  <option>Name</option>\n  <option>Email address</option>\n  <option>Description</option>\n  <option>User ID</option>\n</select>\n```\n\n**Structure Requirements:**\n- `data-hs-select`: Required on the `<select>` element, contains configuration options as JSON\n- Standard `<option>` elements inside the select\n- Optional `<optgroup>` elements for grouping\n\n**Initial State:**\n- Select displays placeholder text when no option is selected\n- Dropdown is closed by default\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-select` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-select` | Select element | - | - | Activate a Custom Select by specifying on an element. Should be added to the select. |\n| `:isOpened` | Inside `data-hs-select` | boolean | `false` | Opens the select if the value is `true`. |\n| `:placeholder` | Inside `data-hs-select` | string | `\"Select...\"` | Define a default placeholder when nothing is selected. |\n| `:hasSearch` | Inside `data-hs-select` | boolean | `false` | Define a search field inside the dropdown if the value is `true`. |\n| `:minSearchLength` | Inside `data-hs-select` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:preventSearchFocus` | Inside `data-hs-select` | boolean | `false` | Sets autofocus for the search field inside a dropdown list if the value is `true`. |\n| `:preventSearchInsideDescription` | Inside `data-hs-select` | boolean | `false` | Prevents searching inside the description when set to `true`. |\n| `:mode` | Inside `data-hs-select` | `'default'` \\| `'tags'` | `'default'` | Define a select mode. `default` for single/multiple selection, `tags` for tag-based selection. |\n| `:scrollToSelected` | Inside `data-hs-select` | boolean | `false` | Scroll to the selected option when the dropdown is opened. |\n| `:value` | Inside `data-hs-select` | string \\| string[] | - | Defines the initial selected value. Use an array when the select is multiple. |\n| `:viewport` | Inside `data-hs-select` | string | - | Defines the viewport selector used when recalculating dropdown direction and available space. |\n| `:toggleTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the select toggle. Note: `data-title` is required if you are using a custom placeholder. |\n| `:toggleClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' toggle. CSS classes must be separated by a space. |\n| `:toggleSeparators` | Inside `data-hs-select` | object | - | Define separators for the selects' toggle. |\n| `:toggleSeparators:items` | Inside `data-hs-select` | string | `\", \"` | Define which separator will be used for separate selected items. |\n| `:toggleSeparators:betweenItemsAndCounter` | Inside `data-hs-select` | string | `\"and\"` | Define which separator will be used for separate selected items and counter text. |\n| `:toggleCountText` | Inside `data-hs-select` | string | - | This option is only available for multiple select. Determines what text will be after the counter. It also activates the counting mode. |\n| `:toggleCountTextPlacement` | Inside `data-hs-select` | `'postfix'` \\| `'prefix'` \\| `'postfix-no-space'` \\| `'prefix-no-space'` | `'postfix'` | Allows to specify where the text specified in the `toggleCountText` parameter will be located relative to the counter. |\n| `:toggleCountTextMinItems` | Inside `data-hs-select` | number | `1` | This option is only available for multiple select. Defines the minimum number of selected items at which the counting mode will be activated. |\n| `:toggleCountTextMode` | Inside `data-hs-select` | `'countAfterLimit'` \\| `'nItemsAndCount'` | `'countAfterLimit'` | This option is only available for multiple select. Controls the display of the contents of the button title. |\n| `:wrapperClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' wrapper. CSS classes must be separated by a space. |\n| `:tagsItemTemplate` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define template for the single tag. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-remove` the target tag will be deleted when click on tag with this data attribute. |\n| `:tagsItemClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the single tags. CSS classes must be separated by a space. |\n| `:tagsInputId` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. This option is useful if there is a need to associate a `label` outside the initialized element with the tags input. |\n| `:tagsInputClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the input. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownTag` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define a markup for the dropdown. |\n| `:dropdownClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"bottom\"` | Specifies the position of the menu when opened. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownAutoPlacement` | Inside `data-hs-select` | boolean | `false` | Automatically determine the placement of the menu based on the available space. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownVerticalFixedPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"bottom\"` \\| null | `null` | Specifies a fixed vertical position for the menu when opened. It will not change when scrolling. |\n| `:dropdownScope` | Inside `data-hs-select` | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `:dropdownDirectionClasses` | Inside `data-hs-select` | `{ top?: string; bottom?: string; }` | `null` | Defines classes that are applied when the dropdown is placed above or below the toggle. |\n| `:dropdownSpace` | Inside `data-hs-select` | number | `10` | Defines the spacing in pixels between the toggle and dropdown. |\n| `:searchTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<input type=\\\"text\\\">\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search input. |\n| `:searchWrapperTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<div></div>\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search wrapper. |\n| `:searchWrapperClasses` | Inside `data-hs-select` | string | `\"bg-white p-2 sticky top-0\"` | This option is only available when `hasSearch: true`. Define CSS classes for the search wrapper. |\n| `:searchId` | Inside `data-hs-select` | string | - | This option is only available when `hasSearch: true`. This option is useful if there is a need to associate a `label` outside the initialized element with the search input. |\n| `:searchLimit` | Inside `data-hs-select` | number | `Infinity` | This option is only available when `hasSearch: true`. If this option is enabled, the search will display only the first 'n' matching items. |\n| `:isSearchDirectMatch` | Inside `data-hs-select` | boolean | `true` | This option is only available when `hasSearch: true`. If the option is disabled, then in the search results you will be able to see non-direct matches by text. For example, if you entered `england` in the search field, then `Eng-land`, `Eng.land`, `Eng_land` will also be shown. |\n| `:searchClasses` | Inside `data-hs-select` | string | `\"block w-[calc(100%-32px)] text-sm border-gray-200 rounded-md focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 py-2 px-3 my-2 mx-4\"` | This option is only available when `:hasSearch` attribute is `true`. Define CSS classes for the search field inside the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:searchPlaceholder` | Inside `data-hs-select` | string | `\"Search...\"` | This option is only available when `:hasSearch` attribute is `true`. Determines placeholder for the search field inside the dropdown. |\n| `:searchNoResultTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<span></span>\"` | This option is only available when `:hasSearch` attribute is `true`. Define a markup for the \"no result\" text. |\n| `:searchNoResultText` | Inside `data-hs-select` | string | `\"No options found...\"` | This option is only available when `:hasSearch` attribute is `true`. Defines the text that will be displayed if no results are found. |\n| `:searchNoResultClasses` | Inside `data-hs-select` | string | `\"px-4 text-sm\"` | This option is only available when `:hasSearch` attribute is `true`. Defines CSS classes for the \"no results\" wrapper. CSS classes must be separated by a space. |\n| `:optionAllowEmptyOption` | Inside `data-hs-select` | boolean | `false` | This option determines whether an empty option should be included in the list. When set to `true`, an additional option with an empty string (`\"\"`) as its value will be displayed, allowing users to select a blank choice if needed. |\n| `:optionTemplate` | Inside `data-hs-select` | string (HTML markup) | - | Define template for the single option. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-description` if target option tag has `data-hs-select-option:description`. |\n| `:optionTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option. |\n| `:optionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option. CSS classes must be separated by a space. |\n| `:optgroupTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option group. |\n| `:optgroupClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option group. CSS classes must be separated by a space. |\n| `:extraMarkup` | Inside `data-hs-select` | string \\| array (HTML markup) | - | Define a markup that could be extra added to the wrapper of the select for the decoration reasons. If it contains the `--prevent-click` class, clicking on this element will not trigger the open function. |\n| `:descriptionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the description inside the single option. CSS classes must be separated by a space. |\n| `:iconClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the icon inside the single option. CSS classes must be separated by a space. |\n| `:isAddTagOnEnter` | Inside `data-hs-select` | boolean | `true` | Determines whether a tag will be added when the enter key is pressed. |\n| `:isSelectedOptionOnTop` | Inside `data-hs-select` | boolean | `false` | Controls whether selected options should appear at the top of the dropdown list. When set to `true`, selected options will be sorted to the top of the list. |\n| `:apiUrl` | Inside `data-hs-select` | string \\| null | `null` | Defines the address where the API is located. |\n| `:apiQuery` | Inside `data-hs-select` | string \\| null | `null` | Defines query parameters that are separated by `?`. |\n| `:apiOptions` | Inside `data-hs-select` | RequestInit \\| null | `null` | Defines options for the fetch function. |\n| `:apiDataPart` | Inside `data-hs-select` | string \\| null | `null` | If data is in some first level parameter, then it allows you to specify the name of this parameter to extract the data. |\n| `:apiSearchQueryKey` | Inside `data-hs-select` | string \\| null | `null` | Defines the key for the query search parameter. |\n| `:apiFieldsMap` | Inside `data-hs-select` | `{ id: string; title: string; val?: string; icon?: string \\| null; description?: string \\| null; page?: string; offset?: string; limit?: string; pageStart?: string; totalPath?: string; [key: string]: unknown; } \\| null` | `null` | Allows you to convert fields from the API to the fields required for the plugin to work. `page`, `offset`, and `limit` customize pagination parameter names for remote data. |\n| `:apiLoadMore` | Inside `data-hs-select` | `boolean \\| { perPage?: number; scrollThreshold?: number; }` | `false` | Defines the options for the load more feature. |\n| `:apiPageStart` | Inside `data-hs-select` | number | - | Defines the first page or offset value used for remote option loading. If omitted, page-based loading starts from `1` and offset-based loading starts from `0`. |\n| `:apiTotalPath` | Inside `data-hs-select` | string \\| null | `null` | Defines the response path used to read the total result count for remote option loading. |\n| `:apiSelectedValues` | Inside `data-hs-select` | `string \\| string[] \\| null` | `null` | Allows to preselect values when loading options from an API. Can be a single value or an array of values for multiple selection. |\n| `:apiIconTag` | Inside `data-hs-select` | string \\| null | `null` | Allows to define an `img` tag that will be used when rendering options and in the trigger button. |\n| `data-hs-select-option` | Option element | object | - | Allows to define the parameters for the single option. Should be added to the option. |\n| `:description` | Inside `data-hs-select-option` | string | - | Allows to define the description of the option inside the element that have `data-description` attribute. |\n| `:icon` | Inside `data-hs-select-option` | string | - | Allows to define the icon of the option inside the element that have `data-icon` attribute. |\n| `:additionalClasses` | Inside `data-hs-select-option` | `[string, string[]][]` | - | Allows to define additional classes. The first string is the selector of the element to apply the classes to, the second string is an array of classes to apply. If the selector is set to `null`, the classes will be applied to the option itself. |\n| `:apiFields` | Inside `data-hs-select-option` | `{ [key: string]: unknown; }` | - | Allows to define the fields of the option from the API. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-select-opened:*` | A modifier that allows you to set Tailwind classes when select has been opened. |\n| `hs-select-disabled:*` | A modifier that allows you to set Tailwind classes when select has \"disabled\" attribute. |\n\n## JavaScript API\n\nThe `HSSelect` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a select instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open()` | None | `void` | Opens the dropdown list programmatically. |\n| `close()` | None | `void` | Closes the dropdown list programmatically. |\n| `setValue(value)` | `value`: `string \\| string[]` | `void` | Set the select value. This should be a string for a simple select and an array if it's a multiple select. |\n| `addOption(items)` | `items`: `{ title: string; val: string; options?: { description: string; icon: string; }; }` or array of such objects | `void` | Adds an option (several possible) to the select. The single option should follow this interface. |\n| `removeOption(val)` | `val`: `string \\| string[]` | `void` | Removes an option by value (several possible). |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown list. Useful when dropdown position needs to be updated after layout changes. |\n| `destroy()` | None | `void` | Destroys the select instance, removes all generated markup, classes, and event listeners. Use when removing select from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSSelect` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSSelect.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSSelect } \\| null` | Returns the select instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSSelect` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if select instance is not found. |\n\n### Usage Examples\n\n**Example 1: Opening and closing dropdown**\n```javascript\n// Get the select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Open dropdown\n  element.open();\n\n  // Close dropdown\n  element.close();\n}\n```\n\n**Example 2: Setting value programmatically**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set value for single select\n  element.setValue('option-value');\n\n  // Set value for multiple select\n  element.setValue(['value-1', 'value-2']);\n}\n```\n\n**Example 3: Adding and removing options**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Add option\n  element.addOption({\n    title: 'New Option',\n    val: 'new-option',\n    options: {\n      description: 'Option description',\n      icon: '<svg>...</svg>'\n    }\n  });\n\n  // Remove option\n  element.removeOption('new-option');\n}\n```\n\n**Example 4: Destroying select instance**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-select').remove();\n  });\n}\n```\n\n## Events\n\nSelect instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected value changes | `string | string[]` (new selected value; array in multi-select / tags mode) | Fires after the user picks an option or removes a tag. |\n| `on:request` | Right before an `apiUrl` request is sent (search or `apiLoadMore` pagination) | `{ url: string }` | Useful for showing a loading indicator. |\n| `on:requestCompleted` | After an `apiUrl` request settles, success or failure | `{ url: string, data?: unknown, error?: unknown }` | `data` holds the parsed response on success, `error` is set on failure. Not fired when a request is superseded/aborted (e.g. a newer search query). |\n\nBoth are also dispatched as DOM `CustomEvent`s (`request.hs.select` / `requestCompleted.hs.select`) with the same payload under `event.detail.payload`, so they can be listened to via `addEventListener` as well as `element.on(...)`.\n\n### Event Usage Example\n\n```javascript\n// Get select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for selection changes\n  element.on('change', (value) => {\n    console.log('Selected value:', value);\n    // value is a string for single-select, string[] for multi-select / tags mode\n  });\n\n  // Listen for API request lifecycle (only relevant when `apiUrl` is set)\n  element.on('request', ({ url }) => {\n    console.log('Requesting:', url);\n    // e.g. show a loading spinner\n  });\n\n  element.on('requestCompleted', ({ data, error }) => {\n    if (error) console.error('Request failed:', error);\n    else console.log('Request completed with:', data);\n    // e.g. hide the loading spinner\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Search Enabled\n\nEnable search functionality in dropdown.\n\n```html\n<select data-hs-select='{\n  \"hasSearch\": true,\n  \"searchPlaceholder\": \"Search options...\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 2: Tags Mode\n\nUse tags mode for multiple selections.\n\n```html\n<select multiple data-hs-select='{\n  \"mode\": \"tags\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 3: API Integration\n\nLoad options from API.\n\n```html\n<select data-hs-select='{\n  \"apiUrl\": \"/api/options\",\n  \"apiFieldsMap\": {\n    \"id\": \"id\",\n    \"title\": \"name\",\n    \"val\": \"value\"\n  }\n}'>\n  <!-- Options -->\n</select>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":6435,"sizeBytes":26348},{"name":"README.md","path":"src/plugins/stepper/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/stepper/README.md","title":"stepper Documentation","category":"plugin-manifest","format":"markdown","content":"# Stepper\n\nDynamic stepper plugin that guides users through the steps of a task.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/stepper) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/stepper.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Stepper component provides a multi-step wizard interface that guides users through a sequential process. It supports both linear (sequential) and non-linear (jumpable) modes, with options for optional steps, error states, and completion tracking.\n\n**Key Features:**\n- Multi-step wizard interface\n- Linear and non-linear navigation modes\n- Optional steps support\n- Error and processing states\n- Completion tracking\n- Programmatic control via JavaScript API\n- Event system for step transitions\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Stepper plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/stepper\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/stepper */\n@source \"../node_modules/@preline/stepper/*.js\";\n@import \"./node_modules/@preline/stepper/variants.css\";\n@import \"./node_modules/@preline/stepper/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/stepper/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStepper from \"@preline/stepper/non-auto.mjs\";\n\n  new HSStepper(document.querySelector(\"#stepper\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/stepper` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/stepper\";\n```\n\n`@preline/stepper/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStepper from \"@preline/stepper/non-auto\";\n\nHSStepper.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#stepper\");\nif (el) new HSStepper(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a stepper component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three steps with navigation buttons.\n\n```html\n<div data-hs-stepper>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 1\n  }'>\n    1 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 2\n  }'>\n    2 Step\n  </div>\n  <div class=\"hs-stepper-active:text-blue-500 hs-stepper-success:text-blue-500\" data-hs-stepper-nav-item='{\n    \"index\": 3\n  }'>\n    3 Step\n  </div>\n  \n  <div data-hs-stepper-content-item='{\n    \"index\": 1\n  }' style=\"display: none;\">\n    First content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 2\n  }' style=\"display: none;\">\n    Second content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"index\": 3\n  }' style=\"display: none;\">\n    Third content.\n  </div>\n  <div data-hs-stepper-content-item='{\n    \"isFinal\": true\n  }' style=\"display: none;\">\n    Final content.\n  </div>\n  \n  <button class=\"hs-stepper-disabled:opacity-50\" type=\"button\" data-hs-stepper-back-btn>\n    Back\n  </button>\n  <button type=\"button\" data-hs-stepper-skip-btn style=\"display: none;\">\n    Skip\n  </button>\n  <button type=\"button\" data-hs-stepper-next-btn>\n    Next\n  </button>\n  <button type=\"button\" data-hs-stepper-finish-btn style=\"display: none;\">\n    Finish\n  </button>\n  <button type=\"reset\" data-hs-stepper-reset-btn style=\"display: none;\">\n    Reset\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-stepper`: Required on the container element\n- `data-hs-stepper-nav-item`: Required on each navigation item\n- `data-hs-stepper-content-item`: Required on each content panel\n- `data-hs-stepper-back-btn`: Optional back button\n- `data-hs-stepper-next-btn`: Optional next button\n- `data-hs-stepper-finish-btn`: Optional finish button\n- `data-hs-stepper-skip-btn`: Optional skip button\n- `data-hs-stepper-reset-btn`: Optional reset button\n\n**Initial State:**\n- First step is active by default (index 1)\n- Content items should have `style=\"display: none;\"` except the first one\n- Navigation buttons visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-stepper`, `data-hs-stepper-nav-item`, and `data-hs-stepper-content-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-stepper` | Container | - | - | Activate a Stepper by specifying on an element. Should be added to the container. |\n| `:currentIndex` | Inside `data-hs-stepper` | number | `1` | Index of the current step. This must be a number between 1 and the maximum number of steps. |\n| `:mode` | Inside `data-hs-stepper` | `\"linear\"` \\| `\"non-linear\"` | `\"linear\"` | The mode of the stepper. In `\"non-linear\"` mode, the user can navigate to any step and nav items are clickable. In `\"linear\"` mode, the user can only navigate to the next/back step. |\n| `:isCompleted` | Inside `data-hs-stepper` | boolean | `false` | Whether the stepper is completed. |\n| `data-hs-stepper-nav-item` | Navigation item | - | - | Activate a Stepper Nav Item by specifying on an element. Should be added to the nav item. |\n| `:index` | Inside `data-hs-stepper-nav-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isOptional` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is optional. |\n| `:isCompleted` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step is skipped. |\n| `:hasError` | Inside `data-hs-stepper-nav-item` | boolean | `false` | Whether the step has an error. |\n| `data-hs-stepper-content-item` | Content item | - | - | Activate a Stepper Content Item by specifying on an element. Should be added to the content item. |\n| `:index` | Inside `data-hs-stepper-content-item` | number | - | The index of the step to which the item belongs. This must be a number between 1 and the maximum number of steps. |\n| `:isCompleted` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is completed. |\n| `:isSkip` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is skipped. |\n| `:isFinal` | Inside `data-hs-stepper-content-item` | boolean | `false` | Whether the step is final. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-stepper-active:*` | Modifies the active step. |\n| `hs-stepper-success:*` | Modifies the completed step. |\n| `hs-stepper-disabled:*` | Modifies the \"back\" button when the very first step is active. |\n| `hs-stepper-skipped:*` | Modifies the skipped step. |\n| `hs-stepper-error:*` | Modifies the step that has error. Error class should be added manually by some event. E.g. after form validation. |\n| `hs-stepper-process:*` | Modifies the step that processing. Process class should be added manually by some event. E.g. after form submit. |\n| `hs-stepper-completed:*` | Modifies all steps are completed. |\n\n## JavaScript API\n\nThe `HSStepper` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a stepper instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setProcessedNavItem(n)` | `n`: `number` | `void` | Set the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `setErrorNavItem(n)` | `n`: `number` | `void` | Set the nav item as error. `n` is the index of the step to which the item belongs. |\n| `unsetProcessedNavItem(n)` | `n`: `number` | `void` | Unset the nav item as processed. `n` is the index of the step to which the item belongs. |\n| `goToNext()` | None | `number` | Go to the next step. Returns the index of the next step. If the current step is the last, returns the index of the current step. |\n| `goToFinish()` | None | `number` | Go to the finish step. Returns the index of the finish step. If the current step is the last, returns the index of the current step. |\n| `disableButtons()` | None | `void` | Disable the \"next\" and \"back\" buttons. |\n| `enableButtons()` | None | `void` | Enable the \"next\" and \"back\" buttons. |\n| `destroy()` | None | `void` | Destroys the stepper instance, removes all generated markup, classes, and event listeners. Use when removing stepper from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStepper` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStepper.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSStepper \\| { id: string \\| number, element: HSStepper } \\| null` | Returns the stepper instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStepper` instance. If `isInstance` is `false` or omitted, returns the `HSStepper` instance directly. Returns `null` if stepper instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new stepper instance\nconst stepper = new HSStepper(document.querySelector('#hs-stepper'));\nlet errorState = 1;\n\nstepper.on('beforeNext', (index) => {\n  if (index === 2) {\n    stepper.setProcessedNavItem(index);\n    \n    setTimeout(() => {\n      stepper.unsetProcessedNavItem(index);\n      stepper.enableButtons();\n      \n      if (errorState) {\n        stepper.goToNext();\n      } else {\n        stepper.setErrorNavItem(index);\n      }\n      \n      errorState = !errorState;\n    }, 2000);\n  }\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSStepper instance\n  let errorState = 1;\n\n  element.on('beforeNext', (index) => {\n    if (index === 2) {\n      element.setProcessedNavItem(index);\n      \n      setTimeout(() => {\n        element.unsetProcessedNavItem(index);\n        element.enableButtons();\n        \n        if (errorState) {\n          element.goToNext();\n        } else {\n          element.setErrorNavItem(index);\n        }\n        \n        errorState = !errorState;\n      }, 2000);\n    }\n  });\n\n  // Clean up when removing from DOM\n  function removeStepper() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Programmatic navigation**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\nif (instance) {\n  const { element } = instance;\n  // Navigate to next step\n  const nextIndex = element.goToNext();\n  console.log('Moved to step:', nextIndex);\n  // Navigate to finish\n  const finishIndex = element.goToFinish();\n\n  console.log('Finished at step:', finishIndex);\n}\n```\n\n**Example 4: Destroying stepper instance**\n```javascript\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-stepper').remove();\n  });\n}\n```\n\n## Events\n\nStepper instances emit events that can be listened to for step transitions and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:active` | When the \"active\" class is set up | `currentIndex` (number) | Fires when a step becomes active. |\n| `on:beforeNext` | Before the \"next\" button is clicked | `currentIndex` (number) | Fires before navigating to the next step. Can be used to validate or prevent navigation. |\n| `on:beforeFinish` | Before the \"finish\" button is clicked | `currentIndex` (number) | Fires before finishing the stepper. Can be used to validate or prevent completion. |\n| `on:next` | When the \"next\" button is clicked | `currentIndex` (number) | Fires when navigating to the next step. |\n| `on:back` | When the \"back\" button is clicked | `currentIndex` (number) | Fires when navigating to the previous step. |\n| `on:complete` | When the \"complete\" button is clicked | `currentIndex` (number) | Fires when completing a step. |\n| `on:finish` | When the \"finish\" button is clicked | `currentIndex` (number) | Fires when finishing the stepper. |\n\n### Event Usage Example\n\n```javascript\n// Get stepper instance\nconst instance = HSStepper.getInstance('#hs-stepper', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to step activation\n  element.on('active', (currentIndex) => {\n    console.log('Step activated:', currentIndex);\n    // Perform actions when step becomes active\n    // e.g., load content, initialize form fields\n  });\n\n  // Listen to before next (for validation)\n  element.on('beforeNext', (currentIndex) => {\n    console.log('Before next step:', currentIndex);\n    // Validate current step before proceeding\n    // Can prevent navigation if validation fails\n  });\n\n  // Listen to finish\n  element.on('finish', (currentIndex) => {\n    console.log('Stepper finished:', currentIndex);\n    // Perform final actions\n    // e.g., submit form, show success message\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Non-linear Mode\n\nAllow users to jump to any step.\n\n```html\n<div data-hs-stepper='{\n  \"mode\": \"non-linear\"\n}'>\n  <!-- Stepper content -->\n</div>\n```\n\n### Pattern 2: Error Handling\n\nSet error state on a step after validation fails.\n\n```html\n<div id=\"hs-stepper-first\" data-hs-stepper>\n  <!-- Stepper content -->\n</div>\n\n<script>\n  const instance = HSStepper.getInstance('#hs-stepper-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    // Validate and set error if needed\n    function validateStep(stepIndex) {\n      // Validation logic\n      if (validationFails) {\n        element.setErrorNavItem(stepIndex);\n      }\n    }\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3967,"sizeBytes":15666},{"name":"README.md","path":"src/plugins/strong-password/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/strong-password/README.md","title":"strong-password Documentation","category":"plugin-manifest","format":"markdown","content":"# Strong Password\n\nUse strong passwords based on required characters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/strong-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/strong-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Strong Password component provides real-time password strength validation with visual feedback. It checks password requirements such as minimum length, uppercase/lowercase letters, numbers, and special characters, displaying strength indicators and helpful hints.\n\n**Key Features:**\n- Real-time password strength checking\n- Visual strength indicators (strips)\n- Password requirement hints\n- Customizable strength rules\n- Popover mode for hints\n- Programmatic control via JavaScript API\n- Event system for strength tracking\n\n## Installation\n\nTo get started, install Strong Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/strong-password\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/strong-password */\n@source \"../node_modules/@preline/strong-password/*.js\";\n@import \"./node_modules/@preline/strong-password/variants.css\";\n@import \"./node_modules/@preline/strong-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/strong-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSStrongPassword from \"@preline/strong-password/non-auto.mjs\";\n\n  new HSStrongPassword(document.querySelector(\"#strong-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/strong-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/strong-password\";\n```\n\n`@preline/strong-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSStrongPassword from \"@preline/strong-password/non-auto\";\n\nHSStrongPassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#strong-password\");\nif (el) new HSStrongPassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a strong password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component shows password strength indicators as the user types.\n\n```html\n<div class=\"flex-1\">\n  <input type=\"password\" id=\"hs-strong-password-input-first\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm  focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400\">\n\n  <div data-hs-strong-password='{\n    \"target\": \"#hs-strong-password-input-first\",\n    \"stripClasses\": \"hs-strong-password:opacity-100 hs-strong-password-accepted:bg-green-500 h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n  }' id=\"hs-strong-password-first\" class=\"flex mt-2 -mx-1\"></div>\n</div>\n\n<div class=\"mb-3\">\n  <span id=\"hs-strong-password-weakness-first\"></span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-strong-password`: Required on the container element, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Optional hints container for displaying password requirements\n\n**Initial State:**\n- Password input is empty\n- Strength strips are hidden or inactive\n- Hints are not displayed (unless configured)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-strong-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-strong-password` | Container element | - | - | Activate a Strong Password by specifying on an element. The value should contain at least one lowercase number, one uppercase letter, one number, one special character (!%&@#$^*?_~), and minimum 6 characters. Should be added to the content block that contains the checks. |\n| `:target` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed. This must be a valid CSS selector. Required. |\n| `:hints` | Inside `data-hs-strong-password` | string (CSS selector) | - | Determines which element should be observed as hints container. This must be a valid CSS selector. There are some selectors that available for using inside the hints: `[data-hs-strong-password-hints-weakness-text]` determines which element should be observed as weakness text container. It should contain an array of strings, e.g. - [\"Empty\", \"Weak\", \"Medium\", \"Strong\", \"Very Strong\", \"Super Strong\"]. Each text is for the concrete strength value, e.g. \"Medium\" have index 2, and it will be set up if strength is also 2. \"Empty\" have index 0, and it will be shown if field is empty and have no any strength. `[data-hs-strong-password-hints-rule-text]` determines which element should be observed as rule text container. It could have one of the follow value: \"min-length\" \\| \"lowercase\" \\| \"uppercase\" \\| \"numbers\" \\| \"special-characters\". `[data-check]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule check icon container. `[data-uncheck]` determines which element should be observed inside the `[data-hs-strong-password-hints-rule-text]` as rule uncheck icon container. |\n| `:stripClasses` | Inside `data-hs-strong-password` | string | - | Defines CSS classes for each strip. These classes are applied to each strength indicator strip. |\n| `:minLength` | Inside `data-hs-strong-password` | number | `6` | Determines minimum password length. |\n| `:mode` | Inside `data-hs-strong-password` | `\"default\"` \\| `\"popover\"` | `\"default\"` | Determines which mode should be used. If `popover` is selected, the hints will be displayed as a popover. |\n| `:popoverSpace` | Inside `data-hs-strong-password` | number | `10` | Available if `mode` is set to `popover`. Determines the space between the popover and the target element. |\n| `:checksExclude` | Inside `data-hs-strong-password` | array | `[]` | Determines which checks should be excluded. It could contain an array of strings, e.g. - [\"min-length\", \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"]. |\n| `:specialCharactersSet` | Inside `data-hs-strong-password` | string | `!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_\\`{|}~` | Determines which special characters should be used for checking. It could contain a string of available characters, e.g. - \"!%&\". |\n\n**Example:**\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"minLength\": 8,\n  \"mode\": \"popover\",\n  \"stripClasses\": \"h-2 flex-auto rounded-full bg-blue-500 opacity-50 mx-1\"\n}'>\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-strong-password:*` | A modifier that allows you to set Tailwind classes for the passed stripes. |\n| `hs-strong-password-accepted:*` | A modifier that allows you to set Tailwind classes when password strength is accepted. |\n\n## JavaScript API\n\nThe `HSStrongPassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a strong password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown hint position. Useful when scrolling or layout changes occur. |\n| `destroy()` | None | `void` | Destroys the strong password instance, removes all generated markup, classes, and event listeners. Use when removing strong password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSStrongPassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSStrongPassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSStrongPassword } \\| null` | Returns the strong password instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSStrongPassword` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if strong password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Recalculating popover position**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = element;\n\n  // Recalculate popover position on scroll\n  document.addEventListener('scroll', () => {\n    element.recalculateDirection();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Strength:', element.strength);\n  console.log('Passed rules:', element.passedRules);\n  console.log('Is opened:', element.isOpened);\n\n  // Clean up when removing from DOM\n  function removeStrongPassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying strong password instance**\n```javascript\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-strong-password').remove();\n  });\n}\n```\n\n## Events\n\nStrong password instances emit events that can be listened to for strength tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When target element was changed | `{ strength: number, rules: string[] }` | Fires when the password input value changes. Returns an object with `strength` (0-4, current level of strength, default is 0) and `rules` (array of passed checks: \"lowercase\", \"uppercase\", \"numbers\", \"special-characters\"). |\n\n### Event Usage Example\n\n```javascript\n// Get strong password instance\nconst instance = HSStrongPassword.getInstance('#hs-strong-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ strength, rules }) => {\n    console.log('Password strength:', strength);\n    console.log('Passed rules:', rules);\n    // Perform actions based on strength\n    // e.g., enable/disable submit button, show messages\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Hints Container\n\nDisplay password requirements in a hints container.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"hints\": \"#hs-hints-container\"\n}'></div>\n\n<div id=\"hs-hints-container\">\n  <div data-hs-strong-password-hints-weakness-text></div>\n  <div data-hs-strong-password-hints-rule-text data-check=\"min-length\"></div>\n</div>\n```\n\n### Pattern 2: Popover Mode\n\nDisplay hints as a popover.\n\n```html\n<input type=\"password\" id=\"hs-password-input\">\n\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"mode\": \"popover\",\n  \"popoverSpace\": 10\n}'></div>\n```\n\n### Pattern 3: Custom Requirements\n\nExclude certain checks or customize special characters.\n\n```html\n<div data-hs-strong-password='{\n  \"target\": \"#hs-password-input\",\n  \"checksExclude\": [\"special-characters\"],\n  \"specialCharactersSet\": \"!@#$%\"\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3087,"sizeBytes":13139},{"name":"README.md","path":"src/plugins/tabs/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/tabs/README.md","title":"tabs Documentation","category":"plugin-manifest","format":"markdown","content":"# Tabs\n\nTabs make it easy to switch between different views.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tabs) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tabs.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tabs component allows users to switch between different content sections within the same context. It supports keyboard navigation, programmatic control, and customizable event types for activation.\n\n**Key Features:**\n- Multiple tab panels in a single group\n- Keyboard navigation support (Arrow keys, Home, End, Enter)\n- Customizable activation events (click, hover)\n- Programmatic control via JavaScript API\n- Event system for tab change tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tabs plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tabs\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tabs */\n@source \"../node_modules/@preline/tabs/*.js\";\n@import \"./node_modules/@preline/tabs/variants.css\";\n@import \"./node_modules/@preline/tabs/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tabs/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTabs from \"@preline/tabs/non-auto.mjs\";\n\n  new HSTabs(document.querySelector(\"#tabs\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tabs` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tabs\";\n```\n\n`@preline/tabs/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTabs from \"@preline/tabs/non-auto\";\n\nHSTabs.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tabs\");\nif (el) new HSTabs(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tabs component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three tabs with corresponding content panels, with the first tab active by default.\n\n```html\n<nav class=\"flex gap-x-2\" aria-label=\"Tabs\" role=\"tablist\">\n  <button type=\"button\" class=\"active\" id=\"hs-unstyled-tabs-item-first\" aria-selected=\"true\" data-hs-tab=\"#hs-unstyled-tabs-first\" aria-controls=\"hs-unstyled-tabs-first\" role=\"tab\">\n    Tab 1\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-second\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-second\" aria-controls=\"hs-unstyled-tabs-second\" role=\"tab\">\n    Tab 2\n  </button>\n  <button type=\"button\" id=\"hs-unstyled-tabs-item-third\" aria-selected=\"false\" data-hs-tab=\"#hs-unstyled-tabs-third\" aria-controls=\"hs-unstyled-tabs-third\" role=\"tab\">\n    Tab 3\n  </button>\n</nav>\n\n<div class=\"mt-3\">\n  <div id=\"hs-unstyled-tabs-first\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-first\">\n    This is the <em>first</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-second\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-second\">\n    This is the <em>second</em> item's tab body.\n  </div>\n  <div id=\"hs-unstyled-tabs-third\" class=\"hidden\" role=\"tabpanel\" aria-labelledby=\"hs-unstyled-tabs-item-third\">\n    This is the <em>third</em> item's tab body.\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `role=\"tablist\"`: Required on the container element (nav, div, etc.)\n- `role=\"tab\"`: Required on each tab button\n- `role=\"tabpanel\"`: Required on each content panel\n- `data-hs-tab`: Required on each tab button, must be a valid CSS selector pointing to the corresponding tabpanel\n- Unique `id` attributes for each tab button and its corresponding panel\n- Proper ARIA attributes (`aria-selected`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- To have a tab active by default, add the `active` class to the tab button and set `aria-selected=\"true\"`\n- For inactive tabs, add the `hidden` class to the tabpanel and set `aria-selected=\"false\"`\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| ArrowLeft / ArrowRight | Selects the previous/next non-disabled tab. |\n| ArrowUp / ArrowDown (in vertical mode) | Selects the previous/next non-disabled tab. |\n| Home / End | Selects the first/last non-disabled tab. |\n| Enter | Activates the selected tab. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure tab behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tabs` | Tablist container | object (JSON) | - | Contains configuration options for customizing tab behavior and appearance. |\n| `:eventType` | Inside `data-hs-tabs` | `\"click\"` \\| `\"hover\"` | `\"click\"` | Set `hover` to activate tabs on mouseover, or `click` for manual clicks (the default). |\n| `data-hs-tab` | Tab button (trigger) | string (CSS selector) | - | Activate a tab by specifying the selector of the target tabpanel. This must be a valid CSS selector pointing to the tabpanel element. |\n| `data-hs-tab-select` | Tablist container | string (ID) | - | You can pass a select ID there. Each option of this select must have a value equal to the tab ID (e.g., `<option value=\"#hs-tab-to-select-first\">Tab 1</option>`). When you select this value, the corresponding tab will be opened. |\n\n**Example:**\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n</nav>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tab-active:*` | A modifier that allows you to set Tailwind classes when the tab is active. Can be applied to both the toggle button and the content panel. |\n\n## JavaScript API\n\nThe `HSTabs` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tabs instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the tabs instance, removes all generated markup, classes, and event listeners. Use when removing tabs from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTabs` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTabs.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTabs \\| { id: string \\| number, element: HSTabs } \\| null` | Returns the tabs instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTabs` instance. If `isInstance` is `false` or omitted, returns the `HSTabs` instance directly. Returns `null` if tabs instance is not found. |\n| `HSTabs.open(target)` | `target`: `HTMLElement` | `void` | Opens the tab identified by target. The target should be a tab button element. Static method to programmatically switch tabs. |\n\n### Usage Examples\n\n**Example 1: Destroying tabs instance**\n```javascript\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Opening tab programmatically (static method)**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\nopenBtn.addEventListener('click', () => {\n  const tabButton = document.querySelector('#hs-tab-first');\n  \n  if (tabButton) {\n    HSTabs.open(tabButton);\n  }\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current tab:', element.current);\n  console.log('Current content:', element.currentContent);\n\n  // Clean up when removing from DOM\n  function removeTabs() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nTabs instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When any tab is changed | `{ el: HTMLElement, prev: string, current: string }` | Fires when a tab is switched. Returns an object with:<br>- `el`: The toggle button element that was clicked<br>- `prev`: Previous tab ID<br>- `current`: Current tab ID |\n\n### Event Usage Example\n\n```javascript\n// Get tabs instance\nconst instance = HSTabs.getInstance('#hs-tabs', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ el, prev, current }) => {\n    console.log('Tab changed:', {\n      clickedButton: el,\n      previousTab: prev,\n      currentTab: current\n    });\n    // Perform actions after tab changes\n    // e.g., load content, update UI, track analytics\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Activation\n\nActivate tabs on hover instead of click.\n\n```html\n<nav data-hs-tabs='{\"eventType\": \"hover\"}' aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-second\" role=\"tab\">Tab 2</button>\n</nav>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tabs from external buttons.\n\n```html\n<nav id=\"hs-tabs-first\" aria-label=\"Tabs\" role=\"tablist\">\n  <button data-hs-tab=\"#hs-tab-content-first\" role=\"tab\">Tab 1</button>\n  <button data-hs-tab=\"#hs-tab-content-second\" role=\"tab\">Tab 2</button>\n</nav>\n\n<button id=\"hs-open-tab-first\">Open Tab 1</button>\n<button id=\"hs-open-tab-second\">Open Tab 2</button>\n\n<script>\n  document.querySelector('#hs-open-tab-first').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-first\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n\n  document.querySelector('#hs-open-tab-second').addEventListener('click', () => {\n    const tabButton = document.querySelector('[data-hs-tab=\"#hs-tab-content-second\"]');\n\n    if (tabButton) {\n      HSTabs.open(tabButton);\n    }\n  });\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2881,"sizeBytes":11712},{"name":"README.md","path":"src/plugins/textarea-auto-height/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/textarea-auto-height/README.md","title":"textarea-auto-height Documentation","category":"plugin-manifest","format":"markdown","content":"# Textarea Autoheight\n\nAutomatically sets the height of the textarea.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/textarea-auto-height) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/textarea-autoheight.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Textarea Autoheight component automatically adjusts the height of a textarea element based on its content. As users type, the textarea expands or contracts to fit the text, eliminating the need for scrollbars.\n\n**Key Features:**\n- Automatic height adjustment based on content\n- Smooth resizing as user types\n- Configurable default height\n- Programmatic control via JavaScript API\n- No scrollbars needed\n\n## Installation\n\nTo get started, install Textarea Autoheight plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/textarea-auto-height\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/textarea-auto-height */\n@import \"./node_modules/@preline/textarea-auto-height/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/textarea-auto-height/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto.mjs\";\n\n  new HSTextareaAutoHeight(document.querySelector(\"#textarea\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/textarea-auto-height` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/textarea-auto-height\";\n```\n\n`@preline/textarea-auto-height/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTextareaAutoHeight from \"@preline/textarea-auto-height/non-auto\";\n\nHSTextareaAutoHeight.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#textarea\");\nif (el) new HSTextareaAutoHeight(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a textarea autoheight component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The textarea automatically adjusts its height as content is entered.\n\n```html\n<textarea data-hs-textarea-auto-height></textarea>\n```\n\n**Structure Requirements:**\n- `data-hs-textarea-auto-height`: Required on the textarea element\n- Standard `<textarea>` element\n\n**Initial State:**\n- Textarea starts at default height (or configured `defaultHeight`)\n- Height adjusts automatically as content is entered or removed\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-textarea-auto-height` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-textarea-auto-height` | Textarea element | - | - | If this attribute is added to the textarea, then automatic height calculation according to the content is enabled. Should be added to the textarea itself. |\n| `:defaultHeight` | Inside `data-hs-textarea-auto-height` | number | `0` | Sets the default height (when no text is entered). When set to `0`, the textarea uses its natural height. |\n\n**Example:**\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 50\n}'></textarea>\n```\n\n## JavaScript API\n\nThe `HSTextareaAutoHeight` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a textarea autoheight instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the textarea autoheight instance, removes all generated markup, classes, and event listeners. Use when removing textarea autoheight from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTextareaAutoHeight` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTextareaAutoHeight.getInstance(target, isInstance)` | `target`: `HTMLTextAreaElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTextareaAutoHeight \\| { id: string \\| number, element: HSTextareaAutoHeight } \\| null` | Returns the textarea autoheight instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTextareaAutoHeight` instance. If `isInstance` is `false` or omitted, returns the `HSTextareaAutoHeight` instance directly. Returns `null` if textarea autoheight instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the textarea autoheight instance\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Default height:', element.defaultHeight);\n\n  // Clean up when removing from DOM\n  function removeTextareaAutoheight() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying textarea autoheight instance**\n```javascript\nconst instance = HSTextareaAutoHeight.getInstance('#hs-textarea-autoheight', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-textarea-autoheight').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Default Height\n\nSet a custom default height for the textarea.\n\n```html\n<textarea data-hs-textarea-auto-height='{\n  \"defaultHeight\": 100\n}'></textarea>\n```\n\n### Pattern 2: Multiple Autoheight Textareas\n\nApply autoheight to multiple textareas.\n\n```html\n<textarea id=\"hs-comment-first\" data-hs-textarea-auto-height></textarea>\n<textarea id=\"hs-comment-second\" data-hs-textarea-auto-height></textarea>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":1718,"sizeBytes":7502},{"name":"README.md","path":"src/plugins/theme-switch/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/theme-switch/README.md","title":"theme-switch Documentation","category":"plugin-manifest","format":"markdown","content":"# Theme Switch\n\nStyle your site in dark mode with ready made Preline UI's dark mode toggle plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/theme-switch) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/theme-switch.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Toggling dark mode manually](#toggling-dark-mode-manually)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Theme Switch component provides functionality to toggle between light and dark themes. It manages theme state, saves preferences to localStorage, and integrates with Tailwind CSS dark mode functionality.\n\n**Key Features:**\n- Light/dark theme switching\n- System theme detection\n- LocalStorage persistence\n- Tailwind CSS integration\n- Programmatic control via JavaScript API\n- Event system for theme changes\n\n## Installation\n\nTo get started, install Theme Switch plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/theme-switch\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/theme-switch */\n@source \"../node_modules/@preline/theme-switch/*.js\";\n@import \"./node_modules/@preline/theme-switch/variants.css\";\n@import \"./node_modules/@preline/theme-switch/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/theme-switch/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSThemeSwitch from \"@preline/theme-switch/non-auto.mjs\";\n\n  new HSThemeSwitch(document.querySelector(\"#theme-switch\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/theme-switch` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/theme-switch\";\n```\n\n`@preline/theme-switch/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSThemeSwitch from \"@preline/theme-switch/non-auto\";\n\nHSThemeSwitch.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#theme-switch\");\nif (el) new HSThemeSwitch(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a theme switch component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles between light and dark themes.\n\n```html\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:hidden block\" data-hs-theme-click-value=\"dark\">\n  Dark\n</button>\n<button type=\"button\" class=\"hs-dark-mode hs-dark-mode-active:inline-flex hidden\" data-hs-theme-click-value=\"light\">\n  Light\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-theme-click-value`: Required on button elements, specifies the theme value (`'default'`, `'dark'`, or `'auto'`)\n- `hs-dark-mode`: Required class for theme-aware styling\n- Buttons can be separate elements or combined into one toggle\n\n**Initial State:**\n- Theme is determined by system preference or saved preference\n- Appropriate button is shown/hidden based on current theme\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-theme-click-value` | Button element | `'default'` \\| `'dark'` \\| `'auto'` | `'default'` | When you click on an element with this attribute, the theme changes to the one specified in the attribute. Should be added to the button (trigger). |\n| `data-hs-theme-switch` | Input element (checkbox/radio) | - | - | When you change an element with this attribute, the theme changes to the opposite one. Element should have change event, e.g. checkbox or radio button. Should be added to the input (checkbox). |\n\n**Example:**\n```html\n<!-- Click-based toggle -->\n<button data-hs-theme-click-value=\"dark\">Dark Mode</button>\n\n<!-- Change-based toggle -->\n<input type=\"checkbox\" data-hs-theme-switch>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dark-mode-active:*` | Defines CSS classes when dark mode is ON |\n| `hs-default-mode-active:*` | Defines CSS classes when default (light) mode is ON |\n| `hs-default-auto-active:*` | Defines CSS classes according to the System theme |\n\n## JavaScript API\n\nThe `HSThemeSwitch` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a theme switch instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `setAppearance(theme, isSaveToLocalStorage, isSetDispatchEvent)` | `theme`: `string` (optional, `'dark'` \\| `'light'` \\| `'default'`)<br>`isSaveToLocalStorage`: `boolean` (optional)<br>`isSetDispatchEvent`: `boolean` (optional) | `void` | Sets the appearance/theme programmatically. `theme` specifies the theme to set. `isSaveToLocalStorage` determines whether to save to localStorage (default: `true`). `isSetDispatchEvent` determines whether to dispatch the theme change event (default: `true`). |\n| `destroy()` | None | `void` | Destroys the theme switch instance, removes all generated markup, classes, and event listeners. Use when removing theme switch from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSThemeSwitch` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSThemeSwitch.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSThemeSwitch } \\| null` | Returns the theme switch instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSThemeSwitch` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if theme switch instance is not found. |\n\n### Usage Examples\n\n**Example 1: Setting theme programmatically**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set dark theme\n  element.setAppearance('dark');\n\n  // Set light theme\n  element.setAppearance('light');\n\n  // Set theme without saving to localStorage\n  element.setAppearance('dark', false);\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the theme switch instance\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current theme:', element.theme);\n  console.log('Type:', element.type);\n\n  // Clean up when removing from DOM\n  function removeThemeSwitch() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying theme switch instance**\n```javascript\nconst instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('[data-hs-theme-click-value]').remove();\n  });\n}\n```\n\n## Events\n\nTheme switch instances emit global events that can be listened to for theme change tracking.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on-hs-appearance-change` | Every time the mode changes | None | An event that fires every time the mode changes. This is a global window event, not an instance event. |\n\n### Event Usage Example\n\n```javascript\n// Listen to global theme change event\nwindow.addEventListener('on-hs-appearance-change', () => {\n  console.log('Theme changed!');\n  // Perform actions after theme changes\n  // e.g., update UI, reload resources, track analytics\n});\n```\n\n## Common Patterns\n\n### Pattern 1: Click-based Toggle\n\nUse buttons to toggle between themes.\n\n```html\n<button data-hs-theme-click-value=\"dark\">Dark</button>\n<button data-hs-theme-click-value=\"light\">Light</button>\n```\n\n### Pattern 2: Checkbox Toggle\n\nUse a checkbox to toggle theme.\n\n```html\n<input type=\"checkbox\" data-hs-theme-switch>\n<label>Dark Mode</label>\n```\n\n### Pattern 3: Programmatic Control\n\nControl theme programmatically.\n\n```html\n<button id=\"hs-dark-btn\">Dark</button>\n<button id=\"hs-light-btn\">Light</button>\n\n<script>\n  const instance = HSThemeSwitch.getInstance('[data-hs-theme-click-value]', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-dark-btn').addEventListener('click', () => {\n      element.setAppearance('dark');\n    });\n\n    document.querySelector('#hs-light-btn').addEventListener('click', () => {\n      element.setAppearance('light');\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2365,"sizeBytes":10203},{"name":"README.md","path":"src/plugins/toggle-count/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/toggle-count/README.md","title":"toggle-count Documentation","category":"plugin-manifest","format":"markdown","content":"# Toggle Count\n\nToggle count from one value to another.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-count) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-count.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Count component provides animated counting functionality that transitions between two numeric values. It's commonly used for pricing displays, statistics, or any scenario where values need to animate between states (e.g., monthly/annual pricing).\n\n**Key Features:**\n- Animated counting between values\n- Configurable animation duration\n- Programmatic control via JavaScript API\n- Smooth transitions\n- Support for multiple count elements\n\n## Installation\n\nTo get started, install Toggle Count plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-count\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-count */\n@import \"./node_modules/@preline/toggle-count/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-count/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSToggleCount from \"@preline/toggle-count/non-auto.mjs\";\n\n  new HSToggleCount(document.querySelector(\"#toggle-count\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-count` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-count\";\n```\n\n`@preline/toggle-count/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSToggleCount from \"@preline/toggle-count/non-auto\";\n\nHSToggleCount.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-count\");\nif (el) new HSToggleCount(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle count component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The count animates between min and max values when the toggle changes.\n\n```html\n<div>\n  <div id=\"hs-toggle-count-first\">\n    <label>\n      Monthly\n      <input id=\"hs-toggle-count-monthly-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\">\n    </label>\n    <label>\n      Annual\n      <input id=\"hs-toggle-count-annual-first\" name=\"hs-toggle-count-first\" type=\"radio\" class=\"hidden\" checked/>\n    </label>\n  </div>\n</div>\n\n<div class=\"space-x-5\">\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 19,\n    \"max\": 29\n  }'>\n    19\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 89,\n    \"max\": 99\n  }'>\n    89\n  </span>\n  <span data-hs-toggle-count='{\n    \"target\": \"#hs-toggle-count-first\",\n    \"min\": 129,\n    \"max\": 149\n  }'>\n    129\n  </span>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-count`: Required on the element that displays the count, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the toggle element (radio buttons or checkbox)\n- Toggle element (radio buttons or checkbox) that triggers the count change\n- Multiple count elements can share the same target\n\n**Initial State:**\n- Count displays the `min` value initially\n- When toggle changes, count animates to `max` value\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-count` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-count` | Count display element | - | - | Activate a Toggle Count by specifying on an element. Should be added to the container. |\n| `:target` | Inside `data-hs-toggle-count` | string (CSS selector) | - | Determines which element will be observed. This must be a valid selector pointing to the toggle element (radio buttons or checkbox). Required. |\n| `:min` | Inside `data-hs-toggle-count` | number | `0` | Specifies default number (starting value). |\n| `:max` | Inside `data-hs-toggle-count` | number | `0` | Specifies the number to which the count up will go (ending value). |\n| `:duration` | Inside `data-hs-toggle-count` | number | `700` | Counting speed (animation duration in milliseconds). |\n\n**Example:**\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 10,\n  \"max\": 50,\n  \"duration\": 1000\n}'>\n  10\n</span>\n```\n\n## JavaScript API\n\nThe `HSToggleCount` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle count instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `countUp()` | None | `void` | Force count up animation. Animates from `min` to `max` value. |\n| `countDown()` | None | `void` | Force count down animation. Animates from `max` to `min` value. |\n| `destroy()` | None | `void` | Destroys the toggle count instance, removes all generated markup, classes, and event listeners. Use when removing toggle count from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSToggleCount` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSToggleCount.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSToggleCount \\| { id: string \\| number, element: HSToggleCount } \\| null` | Returns the toggle count instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSToggleCount` instance. If `isInstance` is `false` or omitted, returns the `HSToggleCount` instance directly. Returns `null` if toggle count instance is not found. |\n\n### Usage Examples\n\n**Example 1: Force count up**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const countUpBtn = document.querySelector('#hs-count-up-btn');\n\n  countUpBtn.addEventListener('click', () => {\n    element.countUp();\n  });\n}\n```\n\n**Example 2: Force count down**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = element;\n  const countDownBtn = document.querySelector('#hs-count-down-btn');\n\n  countDownBtn.addEventListener('click', () => {\n    element.countDown();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle count instance\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Min value:', element.min);\n  console.log('Max value:', element.max);\n  console.log('Duration:', element.duration);\n  console.log('Is checked:', element.isChecked);\n\n  // Clean up when removing from DOM\n  function removeToggleCount() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle count instance**\n```javascript\nconst instance = HSToggleCount.getInstance('#hs-toggle-count', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-count').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Pricing Toggle\n\nToggle between monthly and annual pricing.\n\n```html\n<div id=\"hs-pricing-toggle\">\n  <input type=\"radio\" name=\"pricing\" id=\"monthly\" checked>\n  <input type=\"radio\" name=\"pricing\" id=\"annual\">\n</div>\n\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-pricing-toggle\",\n  \"min\": 19,\n  \"max\": 199\n}'>\n  $19\n</span>\n```\n\n### Pattern 2: Custom Duration\n\nSet custom animation duration.\n\n```html\n<span data-hs-toggle-count='{\n  \"target\": \"#hs-toggle\",\n  \"min\": 0,\n  \"max\": 100,\n  \"duration\": 2000\n}'>\n  0\n</span>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2285,"sizeBytes":9286},{"name":"README.md","path":"src/plugins/toggle-password/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/toggle-password/README.md","title":"toggle-password Documentation","category":"plugin-manifest","format":"markdown","content":"# Toggle Password\n\nShow or hide password field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/toggle-password) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/toggle-password.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Toggle Password component provides a button to toggle password visibility in input fields. It allows users to switch between showing and hiding password text, improving usability while maintaining security.\n\n**Key Features:**\n- Toggle password visibility\n- Show/hide password text\n- Support for multiple password fields\n- Grouped toggle support\n- Programmatic control via JavaScript API\n- Event system for visibility tracking\n\n## Installation\n\nTo get started, install Toggle Password plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/toggle-password\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/toggle-password */\n@import \"./node_modules/@preline/toggle-password/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/toggle-password/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTogglePassword from \"@preline/toggle-password/non-auto.mjs\";\n\n  new HSTogglePassword(document.querySelector(\"#toggle-password\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/toggle-password` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/toggle-password\";\n```\n\n`@preline/toggle-password/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTogglePassword from \"@preline/toggle-password/non-auto\";\n\nHSTogglePassword.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#toggle-password\");\nif (el) new HSTogglePassword(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a toggle password component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button toggles password visibility.\n\n```html\n<div class=\"relative\">\n  <input id=\"hs-toggle-password-first\" type=\"password\" name=\"password\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-400\" value=\"12345qwerty\" required>\n  <button type=\"button\" data-hs-toggle-password='{\n      \"target\": \"#hs-toggle-password-first\"\n    }' class=\"absolute inset-y-0 end-0 flex items-center pointer-events-none z-10 p-3.5\">\n    <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n      <path class=\"hs-password-active:hidden\" d=\"M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z\"/>\n      <path class=\"hs-password-active:hidden\" d=\"M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"/>\n      <path class=\"hidden hs-password-active:block\" d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"/>\n    </svg>\n    <span class=\"hs-password-active:hidden\">\n      <svg class=\"shrink-0 text-gray-400 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n        <path d=\"M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z\"></path>\n        <path d=\"M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z\"></path>\n      </svg>\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-toggle-password`: Required on the toggle button, contains configuration options as JSON\n- `:target`: Required, must be a valid CSS selector pointing to the password input field\n- Password input field must exist in the DOM\n- Toggle button can be positioned anywhere (commonly inside a relative container)\n\n**Initial State:**\n- Password input is hidden (type=\"password\")\n- Toggle button shows \"show\" icon\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-toggle-password` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-toggle-password` | Toggle button | - | - | Activate a Toggle Password by specifying on an element. Should be added to the button (trigger). |\n| `:target` | Inside `data-hs-toggle-password` | string (CSS selector) | - | Determines which element will change type on click. This must be a valid CSS selector pointing to the password input field. Required. |\n| `:isShown` | Inside `data-hs-toggle-password` | boolean | `false` | Determines password visibility initially. When `true`, password is shown by default. |\n| `:eventType` | Inside `data-hs-toggle-password` | `'change'` \\| `'click'` | `'click'` | Determines event type. `click` for button clicks, `change` for checkbox/radio changes. |\n| `data-hs-toggle-password-group` | Container element | - | - | This option allows you to group multiple toggle password elements together so that they can be toggled simultaneously. It should be added to the parent element of the toggle password elements. Should be added to the container. |\n\n**Example:**\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": false,\n  \"eventType\": \"click\"\n}'>\n  Toggle\n</button>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-password-active:*` | A modifier that allows you to set Tailwind classes when the password was shown. |\n\n## JavaScript API\n\nThe `HSTogglePassword` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a toggle password instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Toggle the field to type text (show password). Changes input type from `password` to `text`. |\n| `hide()` | None | `void` | Toggle the field to type password (hide password). Changes input type from `text` to `password`. |\n| `destroy()` | None | `void` | Destroys the toggle password instance, removes all generated markup, classes, and event listeners. Use when removing toggle password from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTogglePassword` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTogglePassword.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSTogglePassword \\| { id: string \\| number, element: HSTogglePassword } \\| null` | Returns the toggle password instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTogglePassword` instance. If `isInstance` is `false` or omitted, returns the `HSTogglePassword` instance directly. Returns `null` if toggle password instance is not found. |\n\n### Usage Examples\n\n**Example 1: Showing password programmatically**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n}\n```\n\n**Example 2: Hiding password programmatically**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const hideBtn = document.querySelector('#hs-hide-btn');\n\n  hideBtn.addEventListener('click', () => {\n    element.hide();\n  });\n}\n```\n\n**Example 3: Getting instance and accessing properties**\n```javascript\n// Get the toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Is shown:', element.isShown);\n  console.log('Target:', element.target);\n\n  // Clean up when removing from DOM\n  function removeTogglePassword() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying toggle password instance**\n```javascript\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-toggle-password').remove();\n  });\n}\n```\n\n## Events\n\nToggle password instances emit events that can be listened to for visibility tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:toggle` | When password was shown or hidden | `HTMLElement` (target input element) | Fires when password visibility changes. Returns the target password input element. |\n\n### Event Usage Example\n\n```javascript\n// Get toggle password instance\nconst instance = HSTogglePassword.getInstance('#hs-toggle-password', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to toggle event\n  element.on('toggle', (target) => {\n    console.log('Password visibility toggled:', target);\n    // Perform actions after password visibility changes\n    // e.g., track analytics, update UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Grouped Toggle\n\nGroup multiple password fields to toggle simultaneously.\n\n```html\n<div data-hs-toggle-password-group>\n  <input type=\"password\" id=\"password-1\">\n  <button data-hs-toggle-password='{\"target\": \"#password-1\"}'>Toggle</button>\n  \n  <input type=\"password\" id=\"password-2\">\n  <button data-hs-toggle-password='{\"target\": \"#password-2\"}'>Toggle</button>\n</div>\n```\n\n### Pattern 2: Initially Shown\n\nShow password by default.\n\n```html\n<button data-hs-toggle-password='{\n  \"target\": \"#hs-password-input\",\n  \"isShown\": true\n}'>\n  Toggle\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3725,"sizeBytes":12603},{"name":"README.md","path":"src/plugins/tooltip/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/tooltip/README.md","title":"tooltip Documentation","category":"plugin-manifest","format":"markdown","content":"# Tooltip & Popover\n\nA tooltip is a floating, non-actionable label used to explain a user interface element or feature.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tooltip) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tooltip.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tooltip component provides contextual information when users hover over or focus on an element. It supports multiple placement options, customizable triggers, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (hover, focus)\n- Floating UI integration for smart positioning\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility support (ARIA attributes)\n\n## Installation\n\nTo get started, install Tooltip & Popover plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tooltip\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/tooltip */\n@source \"../node_modules/@preline/tooltip/*.js\";\n@import \"./node_modules/@preline/tooltip/variants.css\";\n@import \"./node_modules/@preline/tooltip/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/tooltip/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSTooltip from \"@preline/tooltip/non-auto.mjs\";\n\n  new HSTooltip(document.querySelector(\"#tooltip\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/tooltip` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/tooltip\";\n```\n\n`@preline/tooltip/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSTooltip from \"@preline/tooltip/non-auto\";\n\nHSTooltip.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tooltip\");\nif (el) new HSTooltip(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tooltip component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The tooltip appears when hovering over the button.\n\n```html\n<div class=\"hs-tooltip [--placement:auto] inline-block\">\n  <button type=\"button\" class=\"hs-tooltip-toggle\">\n    Hover me\n    <span class=\"hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-gray-900 text-white\" role=\"tooltip\">\n      Tooltip on top\n    </span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-tooltip`: Required container wrapper\n- `hs-tooltip-toggle`: Required element that triggers the tooltip (button, link, etc.)\n- `hs-tooltip-content`: Required element containing the tooltip content\n- Proper ARIA attributes (`role=\"tooltip\"`)\n\n## Configuration Options\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control tooltip behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--trigger:*]` | `hs-tooltip` (container) | `\"focus\"` \\| `\"hover\"` | `\"hover\"` | Event to trigger a tooltip. `hover` shows on mouse enter, `focus` shows on focus. |\n| `[--placement:*]` | `hs-tooltip` (container) | `\"auto\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"auto\"` (top → bottom, left → right) | Tooltip placement relative to the trigger element. If `\"auto\"`, the tooltip will be positioned based on available space. |\n| `[--strategy:*]` | `hs-tooltip` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"absolute\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--prevent-popper:*]` | `hs-tooltip` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Prevents Floating UI positioning calculation issues. Set to `\"true\"` if experiencing positioning problems. |\n\n**Example:**\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the tooltip to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-tooltip` | Container wrapper | Groups the toggle and content elements together |\n| `hs-tooltip-toggle` | Trigger element | The element that activates the tooltip (button, link, etc.) |\n| `hs-tooltip-content` | Content element | The tooltip content that appears on trigger |\n\n## JavaScript API\n\nThe `HSTooltip` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tooltip instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `void` | Forces the tooltip to show programmatically. |\n| `hide()` | None | `void` | Forces the tooltip to hide programmatically. |\n| `destroy()` | None | `void` | Destroys the tooltip instance, removes all generated markup, classes, and event listeners. Use when removing tooltip from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTooltip` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTooltip.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSTooltip } \\| HTMLElement \\| null` | Returns the tooltip instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTooltip` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tooltip instance is not found. |\n| `HSTooltip.show(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSTooltip.hide(target)` | `target`: `HSTooltip \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the tooltip identified by target. Accepts a tooltip instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new tooltip instance\nconst tooltip = new HSTooltip(document.querySelector('#hs-tooltip'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip programmatically\nshowBtn.addEventListener('click', () => {\n  tooltip.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show tooltip using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSTooltip.show(document.querySelector('#hs-tooltip'));\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSTooltip instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeTooltip() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying tooltip instance**\n```javascript\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tooltip').remove();\n  });\n}\n```\n\n## Events\n\nTooltip instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:show` | After tooltip is shown | `HSTooltip` (tooltip instance) | Fires after the tooltip has been displayed. Use for post-show actions like tracking analytics. |\n| `on:hide` | After tooltip is hidden | `HSTooltip` (tooltip instance) | Fires after the tooltip has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get tooltip instance\nconst instance = HSTooltip.getInstance('#hs-tooltip', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to show event\n  element.on('show', (tooltipInstance) => {\n    console.log('Tooltip shown:', tooltipInstance);\n    // Perform actions after tooltip shows\n    // e.g., track analytics, update UI\n  });\n\n  // Listen to hide event\n  element.on('hide', (tooltipInstance) => {\n    console.log('Tooltip hidden:', tooltipInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Focus-based Tooltip\n\nShow tooltip on focus for keyboard accessibility.\n\n```html\n<div class=\"hs-tooltip --trigger:focus --placement:bottom\">\n  <button class=\"hs-tooltip-toggle\">Focus me</button>\n  <span class=\"hs-tooltip-content\">Helpful information</span>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl tooltip from external buttons.\n\n```html\n<div class=\"hs-tooltip\" id=\"hs-tooltip-first\">\n  <button class=\"hs-tooltip-toggle\">Hover me</button>\n  <span class=\"hs-tooltip-content\">Tooltip content</span>\n</div>\n\n<button id=\"hs-show-tooltip\">Show Tooltip</button>\n<button id=\"hs-hide-tooltip\">Hide Tooltip</button>\n\n<script>\n  const instance = HSTooltip.getInstance('#hs-tooltip-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-tooltip').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-tooltip').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":2728,"sizeBytes":11702},{"name":"README.md","path":"src/plugins/tree-view/README.md","rawUrl":"https://raw.githubusercontent.com/htmlstreamofficial/preline/HEAD/src/plugins/tree-view/README.md","title":"tree-view Documentation","category":"plugin-manifest","format":"markdown","content":"# Tree View\n\nTree View solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/tree-view) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/tree-view.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Tree View component provides a hierarchical structure for displaying nested data, similar to file explorers. It integrates with the Accordion component to create expandable/collapsible tree nodes and supports both checkbox and button selection modes.\n\n**Key Features:**\n- Hierarchical nested structure\n- Expandable/collapsible nodes (using Accordion)\n- Multiple selection modes (checkbox, button)\n- Auto-select children option for directories\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Tree View plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/tree-view @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n\n/* @preline/tree-view */\n@source \"../node_modules/@preline/tree-view/*.js\";\n@import \"./node_modules/@preline/tree-view/variants.css\";\n@import \"./node_modules/@preline/tree-view/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n<script src=\"./node_modules/@preline/tree-view/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. Tree View depends on Accordion for expand/collapse behavior, so call `HSAccordion.autoInit()` first and then initialize the specific Tree View element manually.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n  import HSTreeView from \"@preline/tree-view/non-auto.mjs\";\n\n  HSAccordion.autoInit();\n  new HSTreeView(document.querySelector(\"#tree-view\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. Initialize `HSAccordion` first, since it powers the expand/collapse nodes inside the tree.\n\nThe first example uses the auto-init entries. This means the plugins scan the DOM and initialize matching elements automatically.\n\nThe second example uses the manual `non-auto` entries. Use this when you want explicit control over initialization order and timing, or when you need to initialize a specific Tree View instance yourself.\n\n```js\nimport \"@preline/accordion\";\nimport \"@preline/tree-view\";\n```\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\nimport HSTreeView from \"@preline/tree-view/non-auto\";\n\n// Initialize all matching accordion nodes first\nHSAccordion.autoInit();\n\n// Then initialize all matching tree views\nHSTreeView.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#tree-view\");\nif (el) new HSTreeView(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a tree view component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows a directory structure with expandable folders and files.\n\n```html\n<div role=\"tree\" aria-orientation=\"vertical\" data-hs-tree-view>\n  <div class=\"hs-accordion active\" role=\"treeitem\" aria-expanded=\"true\" id=\"hs-tree-view-heading-first\" data-hs-tree-view-item='{\n    \"value\": \"assets\",\n    \"isDir\": true\n  }'>\n    <div class=\"hs-accordion-heading\">\n      <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-tree-view-collapse-first\">\n        <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path class=\"hs-accordion-active:hidden block\" d=\"M12 5v14\"/></svg>\n      </button>\n      assets\n    </div>\n\n    <div id=\"hs-tree-view-collapse-first\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"group\" aria-labelledby=\"hs-tree-view-heading-first\">\n      <div role=\"treeitem\" data-hs-tree-view-item='{\n        \"value\": \"image.jpg\"\n      }'>image.jpg</div>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-tree-view`: Required on the root container element\n- `role=\"tree\"`: Required on the root container\n- `aria-orientation=\"vertical\"`: Required on the root container\n- `hs-accordion`: Required for each directory/folder node\n- `data-hs-tree-view-item`: Required on each tree item (files and directories)\n- Proper ARIA attributes (`role=\"treeitem\"`, `aria-expanded`, `aria-controls`, `aria-labelledby`)\n\n**Initial State:**\n- Directories can be expanded/collapsed using accordion functionality\n- Items can be selected based on the `controlBy` mode\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-tree-view` and `data-hs-tree-view-item` attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-tree-view` | Root container | - | - | Activate a Tree View by specifying on an element. Should be added to the wrapper container. |\n| `:controlBy` | Inside `data-hs-tree-view` | `\"checkbox\"` \\| `\"button\"` | `\"button\"` | Tells the plugin which mode is active and processes events accordingly. `checkbox` mode allows multiple selections, `button` mode allows single selection. |\n| `:autoSelectChildren` | Inside `data-hs-tree-view` | boolean | `false` | This option is available if `controlBy` is set to `checkbox` and if the item is a directory (`isDir: true`). When `true`, if a parent directory is selected, all nested items receive the same selection state. |\n| `:isIndeterminate` | Inside `data-hs-tree-view` | boolean | `true` | Adds indeterminate visual style for checkboxes when `controlBy` is set to `checkbox`. Shows when some but not all children are selected. |\n| `data-hs-tree-view-item` | Each tree item | - | - | Determines which element inside the initialized component is the item. Should be added to the item itself. |\n| `:id` | Inside `data-hs-tree-view-item` | string | - | Optional. Desired identifier for the item. |\n| `:value` | Inside `data-hs-tree-view-item` | string | - | The value that will be passed to the resulting array when item is selected. |\n| `:isDir` | Inside `data-hs-tree-view-item` | boolean | `false` | Determines that the element is a directory and processes it accordingly. Directories can contain nested items. |\n\n**Example:**\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true,\n  \"isIndeterminate\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-tree-view-selected:*` | A modifier that allows you to set Tailwind classes when item has been selected. |\n| `hs-tree-view-disabled:*` | A modifier that allows you to set Tailwind classes when item has \"disabled\" class. |\n\n## JavaScript API\n\nThe `HSTreeView` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a tree view instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `update()` | None | `void` | Updates the element. This is suitable, for example, in case of changing the order of elements or dynamically adding/removing items. |\n| `getSelectedItems()` | None | `ITreeViewItem[]` | Returns a list of selected items as an array of objects with item properties. |\n| `changeItemProp(id, prop, val)` | `id`: `string`<br>`prop`: `string`<br>`val`: `any` | `void` | Changes a property of a specific item. Useful for updating item state programmatically. |\n| `destroy()` | None | `void` | Destroys the tree view instance, removes all generated markup, classes, and event listeners. Use when removing tree view from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSTreeView` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSTreeView.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSTreeView } \\| null` | Returns the tree view instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSTreeView` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if tree view instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting selected items**\n```javascript\n// Get the tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get all selected items\n  const selectedItems = element.getSelectedItems();\n\n  console.log('Selected items:', selectedItems);\n}\n```\n\n**Example 2: Updating tree view after dynamic changes**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // After adding or removing items dynamically\n  element.update();\n}\n```\n\n**Example 3: Changing item properties programmatically**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Change item property\n  element.changeItemProp('item-id', 'isSelected', true);\n}\n```\n\n**Example 4: Destroying tree view instance**\n```javascript\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-tree-view').remove();\n  });\n}\n```\n\n## Events\n\nTree view instances emit events that can be listened to for selection tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:click` | When any item is selected | `{ id: string, value: string, isDir: boolean, path: string, isSelected: boolean }` | Fires when an item is clicked/selected. Returns an object with:<br>- `id`: Item identifier<br>- `value`: Item value<br>- `isDir`: Whether item is a directory<br>- `path`: Path to the item<br>- `isSelected`: Whether item is currently selected |\n\n### Event Usage Example\n\n```javascript\n// Get tree view instance\nconst instance = HSTreeView.getInstance('#hs-tree-view', true);\nif (instance) {\n  const { element } = instance;\n\n  // Listen to click event\n  element.on('click', ({ id, value, isDir, path, isSelected }) => {\n    console.log('Item clicked:', {\n      id,\n      value,\n      isDirectory: isDir,\n      path,\n      isSelected\n    });\n    // Perform actions after item selection\n    // e.g., update UI, track analytics, load content\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Checkbox Mode with Auto-select Children\n\nEnable checkbox selection with automatic child selection.\n\n```html\n<div data-hs-tree-view='{\n  \"controlBy\": \"checkbox\",\n  \"autoSelectChildren\": true\n}'>\n  <!-- Tree items -->\n</div>\n```\n\n### Pattern 2: Getting Selected Items\n\nRetrieve all selected items programmatically.\n\n```html\n<div id=\"hs-tree-view-first\" data-hs-tree-view>\n  <!-- Tree items -->\n</div>\n\n<button id=\"hs-get-selected\">Get Selected</button>\n\n<script>\n  const instance = HSTreeView.getInstance('#hs-tree-view-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-get-selected').addEventListener('click', () => {\n      const selected = element.getSelectedItems();\n      \n      console.log('Selected items:', selected);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n","isInternal":false,"tokens":3117,"sizeBytes":13003}],"systemPromptSnippet":"<yakaai_skills repo=\"htmlstreamofficial/preline\">\n<!-- File: skills/preline-mcp/agents/openai.yaml (Tokens: ~96 | Category: anthropic-skill) -->\ninterface:\n  display_name: \"Preline MCP\"\n  short_description: \"Add Preline UI components and blocks to HTML files via the Preline MCP server\"\n  default_prompt: \"Use $preline-mcp to add Preline UI components or blocks to the target HTML file via the Preline MCP server. Call components_list first to discover valid section and component slugs, then single_component to get the HTML/CSS/JS, then integrate into the target file.\"\n\n<!-- File: skills/preline-mcp/references/catalog-map.md (Tokens: ~1014 | Category: anthropic-skill) -->\n# Catalog Map - where intents and elements live\n\n> Generated from the catalog (blocks.json, components.json, constants.ts). Regenerate with `node scripts/gen-catalog-map.mjs`.\n> This is a **routing map**, not authority - always confirm exact slugs against live `blocks_categories` / `components_list` output before inserting.\n\nUse during the **Discover** step (SKILL.md → Composite & Layout Requests): map an abstract intent to a blocks branch, and each named element to a component section.\n\n---\n\n## Blocks - intent ontology (for whole pages / regions / shells)\n\nAbstract request (\"a pricing page\", \"a login screen\", \"an admin shell\", \"a checkout flow\")? Route to a branch, then `blocks_in_category({ mainSection, subSection, category })`. Whole app shells live under **Navigation & Layout → Sidebars & Shells**.\n\n- **Marketing** - Hero, Banners & Landing Intros · Site Navigation & Headers · Trust & Social Proof · Content & Editorial · Pricing & Conversion · Website Foundations\n- **Data Display** - Analytics & KPIs · Operational & Activity Views · Tables & Records\n- **UI Components** - Menus & Selection · Actions, Status & Progress · Lists & Links\n- **Forms** - Core Inputs & Layouts · Creation & Management · Filters & Refinement · Billing, Feedback & Conversion\n- **Authentication** - Sign In & Account Access · Onboarding & Activation\n- **Ecommerce** - Product Discovery · Product Detail & Purchase · Orders & Commerce Utilities\n- **Communication** - Chat & Inbox · Calls & Media · Contacts & Presence\n- **Productivity** - Calendars & Scheduling · Planning Boards\n- **Blog & Articles** - Editorial Pages · Blog Discovery & Sections\n- **Application Pages** - Workspaces & Admin · Profiles & Accounts\n- **Billing & Payments** - Billing & Payments\n- **Feedback & States** - Alerts & Recovery · Actions & Engagement · Consent & Compliance\n- **Search & Command Palettes** - Search & Navigation · Actions & Shortcuts\n- **Navigation & Layout** - Application Headers & Navigation · Sidebars & Shells · Title Bars & Footers · Blank, Error & Utility Layouts\n\n---\n\n## Components - section index (for individual elements)\n\nPass the section to keep the list small: `components_list({ section: \"<slug>\" })`. 📖 = has a `component_documentation` entry (JS API). Any interactive component still needs the Preline core script wired once.\n\n**Form inputs & controls:** `input` · `input-group` · `input-number`📖 · `textarea` · `select` · `advanced-select`📖 · `combobox`📖 · `searchbox` · `checkbox` · `radio` · `switch` · `color-picker` · `range-slider` · `advanced-range-slider`📖 · `datepicker` · `advanced-datepicker`📖 · `time-picker` · `pin-input`📖 · `strong-password`📖 · `toggle-password`📖 · `file-input` · `file-upload`📖 · `file-uploading-progress-form`\n\n**Menus & overlays:** `dropdown`📖 · `mega-menu` · `context-menu` · `modal` · `offcanvas` · `popover` · `tooltip`📖\n\n**Navigation:** `navbar` · `navs` · `sidebar` · `breadcrumb` · `pagination` · `tabs`📖 · `stepper`📖 · `scrollspy`📖 · `tree-view`📖\n\n**Feedback & status:** `alerts` · `toasts` · `toast-notifications` · `badge` · `progress` · `ratings` · `spinners` · `skeleton` · `legend-indicator`\n\n**Data display:** `tables` · `datatables` · `charts` · `datamaps` · `maps` · `list-group` · `lists` · `timeline`\n\n**Content & media:** `card` · `avatar` · `avatar-group` · `blockquote` · `images` · `carousel`📖 · `marquee` · `devices` · `chat-bubbles` · `text-editor` · `typography` · `static-icons`\n\n**Actions & misc:** `buttons` · `button-group` · `links` · `dividers` · `kbd` · `clipboard` · `copy-markup`📖 · `toggle-count`📖 · `dark-mode` · `confetti` · `drag-and-drop` · `collapse`📖 · `accordion`📖\n\n**Layout:** `container` · `grid` · `columns` · `layout-splitter`📖 · `custom-scrollbar`\n\n<!-- File: skills/preline-mcp/references/composite-layouts.md (Tokens: ~1862 | Category: anthropic-skill) -->\n# Composite & Layout Requests - Detailed Guide\n\nHow to handle any request bigger than a single component - multiple pieces, a region, a full page, an app shell, a wizard. Companion to `SKILL.md` → \"Composite & Layout Requests\".\n\nThe point: **the same five steps apply regardless of domain.** A dashboard shell, an ecommerce product page, a settings screen, a multi-step form, a marketing section, or \"a card with X, Y and Z\" all decompose the same way. Apply the method and let the catalog be the source of truth.\n\nThe mistake to avoid: firing `single_component` / `single_block` ad-hoc and stitching markup as you go. Plan the whole layout first; fetch and integrate one node at a time against that plan.\n\n---\n\n## The method\n\n### 1. Lock cross-cutting constraints first\nResolve anything that applies to the **whole** result before any fetch, because it governs every fetch and edit:\n- **Class system** - design tokens vs utilities. Drives `isUtilityBased` / `theme` on every call; keep it identical across the whole build.\n- **Shared surfaces / color / spacing / density** - anything the request describes as uniform across regions.\n- **Repeated element states** - pinned, selected, active, disabled, collapsed, aligned, icon-only, etc., noted against the element they modify (they change which variant you pick).\n\nDeciding these once prevents inconsistent output halfway through.\n\n### 2. Decompose into a tree\nContainers → regions → leaf components. Write it down. Each leaf is one fetch; grouping leaves by region lets you integrate a region at a time. The shape of the method is identical whether the request is two nested elements or a fifty-element page.\n\n### 3. Discover every node - never guess\n- **Larger structures** (shells, navbars, headers, full sections, pages) → `blocks_categories()`. If a ready-made block already covers the structure, reuse it as the skeleton instead of hand-building.\n- **Individual pieces** → `components_list({ section })`, then `single_component`.\n- Resolve **each named element in the request** to a real slug from the tool output. The \"Where things tend to live\" table below is only a starting point for *which section to open* - the catalog is authoritative, so confirm against its output and never insert an unverified slug.\n\n### 4. Assemble in order - one fetch + integrate at a time\n1. **Outermost / skeleton first.** The shell or top container sets the shared surfaces and - importantly - the script/init placement anchors for everything inside it.\n2. **Fill inward, region by region, leaf by leaf.** Integrate each fully (HTML, CSS in `<head>`, external scripts at the structural anchor, init before `</body>` - see Integration Rules) before fetching the next.\n3. **Honor the locked constraints on every insert.** Keep the classes the MCP returns and change **text only** - except for the specific surfaces/colors/states the user explicitly asked to change.\n4. **Plugins once for the page.** Interactive components share the Preline core script - add it and the init per the placement rules a single time; never duplicate the core script. For a plugin's JS API, events, or options, call `component_documentation`.\n\n### 5. Verify\nRe-read the request and check every named element, state, and constraint is present. Composite requests are easy to under-deliver; the checklist is the guard. Verify **against the request, not by auditing the generated code** - do not grep the project's CSS to confirm token classes exist, and do not run HTML/AST validators over the result (see SKILL.md → Integration Rules). The markup the MCP returned is valid by construction; the only open question is whether you placed every element the user asked for.\n\n---\n\n## Where things live\n\nA request describes UI in plain language; you still discover the real slug. To route an abstract intent (a whole page/region/shell) to the right blocks branch, and each named element to the right component section, use the generated **[catalog-map.md](catalog-map.md)** - it lists the block intent ontology and all component sections grouped by purpose (JS-plugin marked). It's a starting-point map, not authority: always confirm the exact slug against live `blocks_categories` / `components_list` output before inserting.\n\n---\n\n## On surfaces and tokens\n\nWhen the user wants semantic / design-token classes (not raw utilities), keep `isUtilityBased: false` and **preserve the token classes the MCP output already uses** - Preline returns semantic surface tokens, not hardcoded colors. Translate plain-language surface words to the matching tokens you see in the returned markup; do not substitute `bg-white` / `bg-gray-*` / hex, and do not invent token names.\n\nSurface tokens you'll commonly see in the output (recognize and reuse them; exact names can vary by version - match what the catalog emits):\n\n| Plain wording | Token family to look for |\n|---|---|\n| page background | `bg-background` |\n| panel / \"one gray part\" shared by regions | a muted/secondary surface, e.g. `bg-muted` / `bg-secondary` |\n| card / \"layer\" - white with borders | `bg-layer` + `border border-layer-line` (text `text-layer-foreground`) |\n| inset / control surface | `bg-surface` |\n| separators between regions | the matching `*-line` border token |\n\n---\n\n## Worked illustration (one shape of many)\n\nThe five steps are domain-agnostic. The same decomposition covers, for example:\n- **App shell** - outer shell (shared surface) → navbar region(s) → sidebar region → content (distinct surface).\n- **Product page** - gallery → details (title, price, variant pickers, actions) → tabs / reviews.\n- **Settings page** - section nav → form sections → save bar.\n- **Multi-step form** - stepper → step panels → footer actions.\n\nA fuller pass for an app-shell request such as *\"a navbar with a logo, a sidebar toggle, project / team / account dropdowns and dividers, links and a theme switcher; a left sidebar with search and links; navbar + sidebar as one gray panel, content as a white bordered layer; use theme-token classes\":*\n\n**1. Constraints (locked first):**\n- \"theme-token classes\" → `isUtilityBased: false`, no theme passed - and the same setting on every later call.\n- \"navbar + sidebar one gray panel\" → both regions share one muted/secondary surface token; no off-color seams.\n- \"content as a white bordered layer\" → content region uses the layer surface tokens (`bg-layer` + border).\n\n**2. Tree:**\n```\nshell (navbar + left sidebar = shared surface) + content (layer surface)\n├─ navbar · left:  logo · sidebar-toggle · project-dropdown {★pinned A, ★pinned B, Add, Manage}\n│                  · divider · team-dropdown {Add team, ✓selected \"Marketing\"}\n├─ navbar · right: button(s) · text/icon links · divider\n│                  · account-dropdown (avatar trigger) {Upgrade CTA, theme switch, icon links}\n├─ sidebar (below navbar): search input · links · (bottom) icon links\n└─ content: layer surface\n```\n\n**3. Discover (resolve every slug from output):**\n1. `blocks_categories()` → look for an Application / Admin / Dashboard / Navigation shell that already pairs a navbar + sidebar + content. If one fits → `single_block(...)` → use as the skeleton.\n2. If none fits, build the skeleton from the `navbar` + `sidebar` sections.\n3. For each remaining leaf, `components_list({ section })` then `single_component` - sections per the orientation table (`dropdown`, `dividers`, `avatar`, `dark-mode`/`switch`, `input`/`searchbox`, `buttons`, `links`).\n\n**4. Assemble (one fetch + integrate at a time):**\n1. Skeleton → establishes the shared gray surface, the layer content container, and the script anchors.\n2. Navbar-left leaves → navbar-right leaves → sidebar leaves, integrating each fully before the next.\n3. Keep returned classes, edit **text only**; apply only the requested surface changes (shared gray shell, layer content). Set per-element states (pinned/selected) using the component's own selected/checkbox variant.\n4. Wire plugin scripts/init **once** (sidebar toggle, dropdowns, theme switch all need the Preline core script - don't duplicate it). Use `component_documentation` for any plugin's JS API.\n\n**5. Verify** against the request: every named element, every state (pinned, selected), and both surface rules (one gray shell, layer content) present.\n\n<!-- File: skills/preline-mcp/references/mcp-tools-reference.md (Tokens: ~1613 | Category: anthropic-skill) -->\n# Preline MCP - Tools Reference\n\nFull parameter specs and placement rules for all 6 MCP tools.\n\n---\n\n## Tool 1: `components_list`\n\nBrowse available component sections and their component IDs.\n\n**Input:**\n- `section` (optional string) - pass the inferred section slug to get only that section. Omit to get all sections.\n\n**When to pass section:** whenever the component type is known (e.g. `components_list({ section: \"buttons\" })`). Omitting section loads 300k+ characters - avoid unless you genuinely don't know the section.\n\n**Returns:** JSON with section IDs, component IDs, titles, descriptions, and optional `relative` metadata.\n\n---\n\n## Tool 2: `single_component`\n\nGet HTML/CSS/JS for one Preline component.\n\n**Input:**\n- `section` (required) - exact section slug from `components_list`. Never guess.\n- `component` (required) - exact component ID from `components_list`. Never guess.\n- `isUtilityBased` (optional, default: `false`) - convert design tokens to Tailwind utility classes\n- `theme` (optional, default: `\"default\"`) - theme for utility conversion\n- `parameters` (optional) - template-specific overrides\n\n**Available themes:**\n\n| Slug | Color |\n|---|---|\n| `default` | Blue |\n| `harvest` | Amber |\n| `retro` | Fuchsia |\n| `moon` | Grayscale |\n| `ocean` | Cyan |\n| `bubblegum` | Pink |\n| `cashmere` | Mauve |\n| `autumn` | Amber/Orange |\n| `olive` | Green |\n\n**Examples:**\n```\nsingle_component({ section: \"accordion\", component: \"accordion-basic-usage-example\" })\nsingle_component({ section: \"alerts\", component: \"alerts-solid-color-variants\", isUtilityBased: true })\nsingle_component({ section: \"datatables\", component: \"datatables-basic-usage-example\" })\n```\n\n**Response sections:**\n- `=== COMPONENT HTML ===` - main markup\n- `<!-- CSS -->` - `<link>` and `<style>` tags\n- `<!-- Scripts -->` - `<script src>` tags\n- `<!-- Init -->` - initialization code\n- Large responses are written to a temp file; read via the Read tool\n\n---\n\n## Tool 3: `component_documentation`\n\nGet JavaScript API docs for plugin components.\n\n**Input:**\n- `component` (required) - plugin component name from the documented list\n\n**Available documented components:**\naccordion, advanced-datepicker, advanced-range-slider, advanced-select, carousel, collapse, combobox, copy-markup, datatable, dropdown, file-upload, input-number, layout-splitter, overlay, pin-input, remove-element, scroll-nav, scrollspy, stepper, strong-password, tabs, textarea-autoheight, theme-switch, toggle-count, toggle-password, tooltip, tree-view\n\n**Returns:** API methods, events, data attributes, configuration options, integration examples.\n\n---\n\n## Tool 4: `blocks_categories`\n\nList all block categories in a hierarchy.\n\n**Input:** none\n\n**Returns:** `mainSection → subSection → category` tree with block counts per category.\n\n**Call first** when the user asks for blocks (ready-made page sections; users may also say \"examples\") or references a broad category.\n\n---\n\n## Tool 5: `blocks_in_category`\n\nList block IDs within one category.\n\n**Input:**\n- `mainSection` (required) - top-level slug from `blocks_categories`\n- `subSection` (required) - subsection slug from `blocks_categories`\n- `category` (required) - category slug from `blocks_categories`\n\n**Returns:** stable block IDs and slug/file mappings. Use IDs to call `single_block`.\n\n---\n\n## Tool 6: `single_block`\n\nGet complete integration-ready HTML/CSS/JS for one block.\n\n**Input:**\n- `mainSection` (required)\n- `subSection` (required)\n- `category` (required)\n- `block` (required) - exact ID from `blocks_in_category`. Accepts comma/newline-separated list (first is returned, rest marked as pending).\n- `context` (optional) - insertion hint\n- `isUtilityBased` (optional, default: `false`)\n- `theme` (optional, default: `\"default\"`)\n\n**Returns:** full block HTML with CSS/JS sections.\n\n---\n\n## Integration Placement Rules\n\n### CSS (`<!-- CSS -->`)\n\n```\n✅ CORRECT\n<head>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← immediately before </head>\n</head>\n\n❌ WRONG\n<body>\n  ...\n  <link rel=\"stylesheet\" href=\"...\">   ← NEVER in <body>\n</body>\n```\n\n### External Scripts (`<!-- Scripts -->`)\n\nThe `<!-- Scripts -->` label is a **response section marker only** - it does NOT indicate where to insert in the target file.\n\n**Placement algorithm:**\n\n1. Open the target file\n2. Starting from `</body>`, scan **upward** line by line\n3. Skip: blank lines, HTML comments (`<!-- ... -->`), non-structural tags (`</script>`, `</style>`, `</link>`, `</noscript>`), any non-HTML syntax\n4. The **first structural closing tag** reached (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`, `</header>`, `</aside>`) is the **anchor**\n5. Insert ALL `<script src>` tags on the line **immediately after** that anchor - nothing between the anchor and the scripts\n\n```html\n<!-- ✅ CORRECT - anchor is </main> -->\n</main>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ✅ CORRECT - anchor is </section> -->\n</section>\n<script src=\"lib.js\"></script>\n</body>\n\n<!-- ❌ WRONG - non-structural content between anchor and scripts -->\n</main>\n<!-- any comment -->\n<script src=\"lib.js\"></script>\n</body>\n```\n\n### Init (`<!-- Init -->`)\n\n```html\n<!-- Place immediately before </body> -->\n<!-- Wrap in window.addEventListener unless block already contains <script> tags -->\n\n<script>\nwindow.addEventListener('load', () => {\n  // init code here\n});\n</script>\n</body>\n```\n\nIf `<!-- Init -->` already contains `<script>` tags - insert as-is, do NOT wrap again.\n\n---\n\n## Smart Component Selection\n\n`components_list` may include a `relative` object per component:\n\n| Field | Meaning | When to prefer |\n|---|---|---|\n| `isSectionDefault: true` | Recommended showcase for the whole section | User request is vague, no specific variant mentioned |\n| `category: \"<slug>\"` | Logical group (e.g. `\"color-variants\"`, `\"states\"`) | Use to filter by style group |\n| `isCategoryDefault: true` | Recommended default within its category | Request implies a style group (e.g. \"icon button\", \"loading state\") |\n\n**Exception for `isSectionDefault`:** if the component description mentions \"multiple variants side by side\" and the user wants a single element - skip it and pick a single-variant component. If no single-variant exists, use the default but extract only the one matching variant from the HTML.\n\n---\n\n## General Rules\n\n- Customize **text content only** - never CSS classes or HTML structure\n- Remove HTML comments before inserting into the target file\n- For complex components (accordion, table, tree), insert the full structure\n- For simple elements (button, input), insert one instance unless multiple are explicitly requested\n\n<!-- File: skills/preline-mcp/SKILL.md (Tokens: ~2718 | Category: anthropic-skill) -->\n---\nname: preline-mcp\ndescription: Add, integrate, or build Preline UI components and blocks into HTML files via the Preline MCP server. Use when the user mentions \"preline\", says \"/preline\", asks to add a UI component, or requests a Preline block (a ready-made page section) using Preline UI.\n---\n\n# Preline MCP\n\n## Intent\n\nUse this skill to integrate Preline UI components and blocks into HTML files via the Preline MCP server. The server exposes 6 tools that return integration-ready HTML, CSS, and JS.\n\nActivate when:\n- User says \"preline\", \"add preline\", \"/preline\", \"use preline\"\n- User asks for a Tailwind CSS component from the Preline library\n- User asks for a block (ready-made page section) or a starter page\n\n## Defaults\n\n- **Never guess slugs.** All section, component, and category identifiers are exact kebab-case strings. Always discover them via `components_list` or `blocks_categories` first.\n- **One at a time.** Retrieve one component or block, integrate it fully, then move to the next.\n- **Always read the target file** before inserting anything.\n- Call `components_list({ section: \"<inferred-slug>\" })` when the component type is known - this keeps the response small. Omit section only when unsure.\n\n## Decision: Component vs Block\n\n| User says | Use |\n|---|---|\n| \"component\" / \"components\" | Component workflow |\n| \"block\" / \"blocks\" (or \"example\" / \"examples\") | Block workflow |\n| Ambiguous | Use judgment; blocks give more complete results |\n\n## Clarifying Vague Requests\n\nAsk the user to pick instead of guessing in either of these cases:\n\n- **Abstract request.** Too abstract to confidently map onto one category/section, let alone one block/component - e.g. \"give me something for a pricing page\", \"I need a nice form\", \"show me a dashboard example\". Start at step 1 below.\n- **Tied candidates.** The request is concrete enough to reach a single category/section, but `blocks_in_category` / `components_list({ section })` still leaves 2+ candidates whose titles + descriptions satisfy every criterion the request named about equally well (same layout/feature/style class), with nothing in the request to break the tie. Skip straight to step 3 below with just the tied candidates.\n\nIf exactly one candidate matches everything the request names (\"a basic accordion\", \"the SaaS hero with tabs\"), skip straight to the normal workflow - don't interrogate the user for things you can already resolve, and don't ask just to be safe when one option is clearly the best fit.\n\nAll clarifying questions and lists are in **English by default** - switch to the user's language only once they write to you in it.\n\n**Blocks:**\n1. **Ask which category fits.** Call `blocks_categories()` and present the relevant `mainSection / subSection / category` paths with their titles and descriptions - the catalog carries a one-line description for every category, so present it directly rather than inventing your own summary.\n2. **Try to resolve in one round.** If the user's reply both names a category *and* describes the item distinctively enough to identify a single match (a specific layout, feature, or style), call `blocks_in_category` to confirm the exact ID and go straight to `single_block` - skip step 3 entirely.\n3. **Otherwise, ask which block fits best.** Call `blocks_in_category({ mainSection, subSection, category })`, list the block titles + descriptions - or just the tied candidates, for the case above - and ask the user to pick the one closest to their needs.\n4. **Fetch and integrate** the chosen block with `single_block`.\n\n**Components - same shape, sections instead of categories:**\n1. Ask which **section** fits (`components_list()` for the full list, or `components_list({ section })` once you can infer one).\n2. If the reply names a section *and* describes the component distinctively enough → resolve directly via `single_component` (lean on `relative` metadata - see Smart Component Selection - to land on the right default/variant).\n3. Otherwise list the components in that section (titles + descriptions, with `relative.category` groupings where present - or just the tied candidates, for the case above) and ask which fits best.\n4. Fetch and integrate with `single_component`.\n\nKeep each round to one focused question with a short, scannable list - that beats an open \"what do you want?\" and beats guessing a slug just to avoid asking.\n\n## Composite & Layout Requests\n\nWhen a request describes **more than a single component** - several pieces, a region, a full page, or an app shell - don't fetch ad-hoc. Plan the whole result first, then fetch and integrate one node at a time. The same steps apply to any shape: a dashboard, a settings page, a product page, a multi-step form, \"a card/section/layout with X, Y and Z\", etc.\n\n1. **Lock cross-cutting constraints first.** Anything that applies to the whole result - class system (theme tokens vs utilities → `isUtilityBased` / `theme`), shared surfaces/colors, spacing/density, repeated element states - decide once, up front. Keep it identical across every later fetch and edit.\n2. **Decompose into a tree:** containers → regions → leaf components. Write it down; each leaf is one fetch.\n3. **Discover every node - route, then confirm.** Use [references/catalog-map.md](references/catalog-map.md) to route: an abstract intent (a whole page/region/shell) → the right `blocks_categories` branch (reuse a ready-made block as the skeleton when one fits); each named element → the right `components_list({ section })`. The map only tells you *where to look* - always confirm the exact slug against the tool output before inserting.\n4. **Assemble outermost-first, one at a time.** The skeleton/outer container sets the shared surfaces and the script/init anchors; then fill inward region by region, integrating each fully (Integration Rules) before the next. Keep the returned classes, change **text only** - except the specific surfaces/colors/states the user asked to change. Apply every structural adaptation to the markup **before** writing it (see *Adapt before you write* in Integration Rules).\n5. **Verify** the finished result against the request, element by element.\n\nSee [references/composite-layouts.md](references/composite-layouts.md) for the expanded method and worked illustrations.\n\n## Workflow: Components\n\n```\n1. components_list({ section: \"<slug>\" })   → get valid component IDs\n2. single_component({ section, component }) → get HTML/CSS/JS\n3. Read target file\n4. Integrate HTML, CSS, scripts, init (see Integration Rules)\n5. Repeat from step 1 only after integration is complete\n```\n\n## Workflow: Blocks\n\n```\n1. blocks_categories()                                    → get hierarchy\n2. blocks_in_category({ mainSection, subSection, category }) → get block IDs\n3. single_block({ mainSection, subSection, category, block }) → get HTML/CSS/JS\n4. Read target file\n5. Integrate HTML, CSS, scripts, init (see Integration Rules)\n6. Repeat from step 1 only after integration is complete\n```\n\n## Integration Rules\n\n**HTML** - insert where the user needs it.\n\n**CSS** (`<!-- CSS -->` section):\n- MUST go inside `<head>`, immediately before `</head>`\n- NEVER place in `<body>` or near `</body>`\n\n**External scripts** (`<!-- Scripts -->` section):\n- The `<!-- Scripts -->` label is a response section marker - NOT a location in the target file\n- Placement algorithm: open target file, scan **upward** from `</body>`, skipping blank lines, comments, and non-structural tags (`</script>`, `</style>`, etc.)\n- The **first structural closing tag** you reach (`</main>`, `</section>`, `</div>`, `</footer>`, `</article>`) is the anchor\n- Insert `<script src>` tags after that anchor, ordered around the existing Preline core script - the loaded `<script src>` whose `src` contains `preline` (e.g. `…/preline/dist/index.js`; exact path varies by install):\n  - scripts whose `src` does **not** contain `preline` (third-party libs: lodash, apexcharts, …) → **before** the Preline core script\n  - scripts whose `src` **does** contain `preline` (Preline helpers, e.g. `hs-*-helpers.js`) → **after** the Preline core script\n  - if no Preline core script exists yet, keep order: scripts without `preline`, then scripts with `preline`\n  - inline init `<script>` always comes last, immediately before `</body>`\n\n**Init** (`<!-- Init -->` section):\n- Place immediately before `</body>`\n- Wrap in `window.addEventListener('load', () => { ... })` unless the block already contains `<script>` tags\n\n**Large responses (artifacts):** when a response is written to a temp scratch file, read it once with the Read tool (use offset/limit for big files), copy the needed blocks into the target, then delete the scratch file. Don't pull the whole artifact into context if you only need to place it.\n\n**Adapt before you write.** When the request differs from the fetched markup - regions the user didn't ask for (breadcrumbs, demo menus, placeholder logos), different blocks or labels - produce the final markup in memory first, then write it into the target in ONE edit per region. Never insert fetched markup wholesale and refactor it with a chain of follow-up edits: every such edit re-transfers large markup, bloats context, and desyncs file state.\n\n**Same token = same color.** Theme tokens (`bg-navbar`, `bg-sidebar`, `bg-layer`, …) are consistent across a theme - to give two surfaces the same color, give them the same token class. Never resolve tokens to raw colors by reading the project's CSS.\n\n**Trust the returned markup - do not re-verify it.** The classes, structure, and `data-hs-*` attributes the MCP returns are valid Preline by construction. This is the single biggest time-sink to avoid. Do NOT:\n- grep, parse, or scan the user's compiled CSS (e.g. `main.css`) to \"confirm\" a class exists or to resolve what color a design token produces - Preline classes resolve at the consumer's build step, so absence from any one stylesheet means nothing;\n- write HTML/DOM/AST validators (Python `HTMLParser`, tag-balance checkers, etc.) - SVG and void elements trip naive parsers and produce false errors;\n- re-read a placed artifact or re-open the edited file just to \"double-check\" the generated code.\n\nPlace the markup, change text only, move on. **Verify against the request** (is every element the user named present?), never by auditing the generated code or the project's CSS.\n\n## Smart Component Selection\n\n`components_list` may return a `relative` object per component:\n- `isSectionDefault: true` - recommended default for the section; prefer when the request is vague\n  - Exception: if the description says \"multiple variants\" and the user wants one, pick a single-variant component instead\n- `category` - logical group (e.g. `\"color-variants\"`, `\"states\"`)\n- `isCategoryDefault: true` - recommended default for its category; prefer when the request implies a style group\n\n## Available Themes\n\n`default` (blue) · `harvest` (amber) · `retro` (fuchsia) · `moon` (grayscale) · `ocean` (cyan) · `bubblegum` (pink) · `cashmere` (mauve) · `autumn` (orange) · `olive` (green)\n\nPass via `isUtilityBased: true, theme: \"<name>\"` on `single_component` or `single_block`.\n\n## Key References\n\n- [references/catalog-map.md](references/catalog-map.md) - routing map: block intent ontology (whole pages/shells) + all component sections grouped by purpose, JS-plugin marked. Open this to pick the right `section` / blocks branch before any discovery call.\n- [references/composite-layouts.md](references/composite-layouts.md) - full method for multi-component / page / shell requests, with worked illustrations\n- [references/mcp-tools-reference.md](references/mcp-tools-reference.md) - full parameter specs, placement algorithm detail, all 6 tools\n\n<!-- File: skills/theme-generator/docs/final-output-style.md (Tokens: ~685 | Category: anthropic-skill) -->\n# Final Output Style\n\nUse this guidance whenever the user wants a final `<name>.css` file for a Preline-based project.\n\n## Core rule\n\nThe deliverable is one file only: `<name>.css`.\n\nDo not create or edit any other project files.\n\n## What the final theme file should look like\n\n- A standalone `<name>.css` file that can live in the project's themes folder.\n- Theme activation via `data-theme=\"theme-<name>\"` and optional `.dark`.\n- Semantic tokens should prefer assigned variables after the theme palette has been mapped.\n\n## Required conventions\n\n- Do not assume project-specific paths other than the discovered themes directory.\n- Do not assume the project already has other custom theme files.\n- Do not force users to change HTML utility classes.\n- Do not create or edit a shared `theme.css`, docs page, or any other file.\n- Keep semantic tokens in the theme selector blocks, not inside `@theme`.\n- Put any custom brand/gray palette needed for this theme inside `@theme theme-<name> inline { }` in the new theme file.\n- If the repo already exposes shared aliases like `--color-primary-*` from its base theme, do not redefine them inside the custom theme file. Assign `--primary-*` directly from the theme-local palette instead.\n\n## Variable strategy\n\nPrefer assigned variables such as:\n\n- `--color-primary-*`\n- `--color-secondary-*`\n- `--background-*`, `--foreground`, `--border`, `--surface-*`, `--muted-*`\n- Tailwind palette vars like `--color-fuchsia-*`, `--color-orange-*`, `--color-neutral-*`\n- shared Preline palette vars exposed by the project's base theme, if available\n\nAllowed in the new theme file:\n\n- theme-local palette vars like `--color-<theme-name>-*`\n- theme-local gray palette vars like `--color-<theme-name>-gray-*`\n\nBut keep them inside `@theme theme-<name> inline { }` and avoid scattering raw palette references when an assigned semantic variable already exists.\n\nAvoid in semantic, chart, and map tokens:\n\n- raw `oklch(...)` assignments\n- literal hex values\n\n## If a unique palette is needed\n\nDefine it inside `@theme theme-<name> inline { }` in the new theme file.\n\nDo not edit any shared/base theme file.\n\n## Structural pattern\n\nUse this order:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector:\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector:\n   - `[data-theme=\"theme-<name>\"].dark`\n\n## Practical workflow\n\n1. Discover the themes folder.\n2. Generate one new `<name>.css` file.\n3. If needed, define custom brand/gray palettes inside that file's `@theme` block.\n4. Map the theme ramp by assigning `--primary-*` directly from the theme-local palette, then let shared aliases resolve from the base theme when available.\n5. Keep chart and map tokens variable-based.\n6. Re-run the validation checklist before returning.\n\n<!-- File: skills/theme-generator/docs/palette-guidance.md (Tokens: ~2861 | Category: anthropic-skill) -->\n# Palette guidance\n\nUse this file for color-direction heuristics only.\n\nImportant: references below to custom palettes like `--color-<name>-*` or `--color-<name>-gray-*` describe draft palette exploration and shared-palette design patterns. They do NOT override `docs/final-output-style.md`, which defines how the final `<name>.css` file should be structured.\n\n## Contents\n\n- [Hue reference](#hue-reference-for-workflow-normalization)\n- [Mood and palette guidance](#mood-and-palette-guidance)\n- [Accessibility and contrast guidelines](#accessibility-and-contrast-guidelines-wcag)\n- [Palette types](#palette-types)\n- [Custom gray palettes](#custom-gray-palettes-neutral-matching)\n\n## Hue reference (for workflow normalization)\n\n| Color | Hue Range | Tailwind Gray |\n|-------|-----------|---------------|\n| Red/Coral | 0-30 | neutral |\n| Orange/Amber | 30-60 | stone |\n| Yellow/Gold | 60-90 | stone |\n| Green/Lime/Avocado | 90-150 | stone |\n| Teal/Cyan | 150-200 | zinc |\n| Blue/Azure | 200-260 | slate |\n| Violet/Purple | 260-300 | zinc |\n| Magenta/Pink/Rose | 300-360 | neutral |\n\n## Mood and palette guidance\n\n### Color psychology reference\n\n| Mood | Primary Hues | Neutral Family | Characteristics |\n|------|--------------|----------------|-----------------|\n| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |\n| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |\n| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |\n| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |\n| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |\n| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |\n| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |\n| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |\n| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |\n| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |\n\n### Building cohesive palettes\n\n1. Pick a primary: the hero color for buttons, links, CTAs\n2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate\n3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast\n4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family\n\n### Accessibility and contrast guidelines (WCAG)\n\nEnsure themes meet WCAG 2.1 AA contrast requirements:\n\n| Element | Minimum Ratio | OKLCH Rule of Thumb |\n|---------|---------------|---------------------|\n| Body text on background | 4.5:1 | Lightness difference >= 50% |\n| Large text (18pt+) | 3:1 | Lightness difference >= 40% |\n| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |\n| Focus indicators | 3:1 | Use distinct color + lightness |\n\nOKLCH lightness thresholds:\n\n| Context | Light Mode | Dark Mode |\n|---------|------------|-----------|\n| Background | L >= 95% | L <= 25% |\n| Text on light bg | L <= 45% | -- |\n| Text on dark bg | -- | L >= 85% |\n| Primary button text | Check against primary-500/600 | Check against primary-400 |\n\nReadable primary foreground:\n- Do not decide from hue alone.\n- Compare candidate text colors against the actual generated primary shades used by the theme.\n- For light mode, check the generated `primary-600` and `primary-700` states.\n- For dark mode, check the generated `primary-400` and `primary-500` states.\n- Choose the text color with the stronger worst-case contrast across those states.\n- White usually wins for medium or dark primaries; dark gray should only be used when the generated brand states are genuinely light.\n- In dark mode, it is valid to shift the primary button one step deeper (for example `500/600` instead of `400/500`) when that keeps white text readable and better matches the design system.\n\nReadable inverse and decorative accents:\n- `--foreground-inverse` is not just for neutral inverse surfaces. In Preline it is also used on vivid utility colors such as red/orange badges and chips.\n- In dark mode, prefer keeping `--foreground-inverse` white unless contrast checks prove a darker value is still safe across those saturated utility colors.\n- Do not let dark mode inherit very deep brand steps for decorative tokens such as `--chart-5` when those tokens are likely to appear in gradient text or accent UI.\n- For dark decorative/chart tokens, prefer mid-to-light brand steps that stay clearly visible on dark backgrounds.\n\nQuick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.\n\n## Palette types\n\nWhen generating custom themes, there are TWO distinct types of custom color palettes:\n\n| Palette Type | Example Variable | Purpose | Chroma Level |\n|--------------|------------------|---------|--------------|\n| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |\n| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |\n\n### When to create each palette\n\nALWAYS create TWO palettes in `@theme inline { }`:\n1. Brand palette (`--color-<name>-*`) for primary/accent colors\n2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders\n\nBoth palettes are used in light mode for a cohesive feel.\n\nDark mode behavior depends on user request:\n\n| User Request | Light Mode Uses | Dark Mode Uses |\n|--------------|-----------------|----------------|\n| \"soft rose theme\" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |\n| \"soft rose theme with matching dark mode\" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |\n\nTrigger phrases for custom dark mode palette:\n- \"matching dark mode\"\n- \"cohesive dark mode\"\n- \"consistent dark colors\"\n- \"unified light and dark\"\n- \"matching neutrals\"\n\nIf none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.\n\n### Brand color style (vibrant vs soft)\n\nBrand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:\n\n| User Says | Brand Palette Style | Chroma Level |\n|-----------|---------------------|--------------|\n| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |\n| \"soft\", \"muted\", \"ash\", \"pastel\", \"dusty\", \"desaturated\" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |\n\nExample comparison:\n\n```css\n/* Vibrant brand (default) */\n--color-<name>-500: oklch(55% 0.14 <hue>);\n\n/* Soft/muted brand (if requested) */\n--color-<name>-500: oklch(55% 0.04 <hue>);\n```\n\n## Custom gray palettes (neutral matching)\n\nWhen a theme benefits from unique neutrals, generate a custom neutral palette that harmonizes with the primary color. These are NOT pure grays; they have subtle warmth or coolness.\n\n### Palette characteristics\n\n| Palette Type | Hue Range | Chroma Range | Use With |\n|--------------|-----------|--------------|----------|\n| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |\n| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |\n| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |\n| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |\n\n### OKLCH pattern for gray palettes (bell curve chroma)\n\nGray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:\n- Light mode backgrounds (50-200) look clean with subtle tint\n- Dark mode backgrounds (800-950) look sophisticated, not muddy\n\n```css\n--color-<name>-gray-50:  oklch(98%   0.002 <hue>);  /* Almost neutral */\n--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);\n--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);\n--color-<name>-gray-300: oklch(82.7% 0.012 <hue>);  /* Building up */\n--color-<name>-gray-400: oklch(73%   0.018 <hue>);  /* Approaching peak */\n--color-<name>-gray-500: oklch(62.5% 0.020 <hue>);  /* PEAK chroma */\n--color-<name>-gray-600: oklch(52.8% 0.016 <hue>);  /* Starting to fade */\n--color-<name>-gray-700: oklch(41.4% 0.012 <hue>);  /* Fading */\n--color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */\n--color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */\n--color-<name>-gray-950: oklch(20.1% 0.003 <hue>);  /* Nearly pure dark */\n```\n\nCritical: Dark mode backgrounds (800-950) need very low chroma:\n\n| Shade | Chroma | Why |\n|-------|--------|-----|\n| 950 | 0.003 | Dark backgrounds must be nearly neutral |\n| 900 | 0.005 | Avoids muddy/colored appearance |\n| 800 | 0.008 | Just a hint of warmth/coolness |\n| 500 | 0.020 | Peak, midtones carry color identity |\n| 50 | 0.002 | Light backgrounds stay clean |\n\nKey principles:\n- Chroma peaks at mid-tones (400-600), very low at BOTH extremes\n- Dark end (800-950) must have lower chroma than light end for clean dark mode\n- Hue stays consistent across the scale (±5 degrees variation is acceptable)\n- Lightness follows standard 50-950 scale\n\n### Placement of custom palettes\n\nCustom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:\n\n```css\n@theme theme-<name> inline {\n  /* Custom neutral palette for this theme */\n  --color-<name>-50: oklch(98% 0.003 88);\n  --color-<name>-100: oklch(95.5% 0.005 88);\n  /* ... full 50-950 scale ... */\n}\n```\n\nThen reference these in the theme selector blocks using `var()`:\n\n```css\n[data-theme=\"theme-<name>\"] {\n  --background: var(--color-<name>-50);\n  --background-1: var(--color-<name>-100);\n  /* ... */\n}\n\n[data-theme=\"theme-<name>\"].dark {\n  --background: var(--color-<name>-950);\n  --background-1: var(--color-<name>-900);\n  /* ... */\n}\n```\n\n### When to create custom palettes\n\n| Scenario | Action |\n|----------|--------|\n| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |\n| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |\n| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |\n| User explicitly requests | Always create to match their vision |\n| Theme needs distinctive personality | Create for cohesion |\n\n### Using custom palettes in both modes\n\nSoft gray palettes work seamlessly across light and dark modes:\n- Light mode: use 50-300 for backgrounds, 600-950 for text\n- Dark mode: use 800-950 for backgrounds, 50-300 for text\n\nThis creates consistent warmth/coolness across modes while maintaining readability.\n\n<!-- File: skills/theme-generator/docs/token-reference.md (Tokens: ~5585 | Category: anthropic-skill) -->\n# Preline Theme Token Reference\n\nThis document provides the complete reference for all semantic tokens available in Preline themes. Use this when generating themes to ensure comprehensive coverage.\n\n---\n\n## Contents\n\n- [Overview](#overview)\n- [Core token groups](#core-token-groups)\n- [Component preset patterns](#component-preset-patterns)\n- [Full token reference by category](#full-token-reference-by-category)\n- [Custom color palettes](#custom-color-palettes)\n- [Default values reference](#default-values-reference)\n- [Theme-scoped overrides](#theme-scoped-overrides)\n- [Color format guidelines](#color-format-guidelines)\n- [Quick token count summary](#quick-token-count-summary)\n\n## Overview\n\nPreline themes use CSS custom properties (variables) as semantic tokens. These tokens are:\n- Defined in theme files under `:root` (light) and `.dark` selectors\n- Mapped to Tailwind utilities via `@theme inline` in the base theme\n- Consumed by Preline components for consistent styling\n\nImportant: Generated themes override token values only. Never redefine the Tailwind mappings.\n\n---\n\n## Core token groups\n\n| Group | Tokens | Used for |\n|-------|--------|----------|\n| Brand | `--primary`, `--primary-*` | Primary actions (buttons, links) |\n| Secondary | `--secondary`, `--secondary-*` | Secondary emphasis |\n| Muted | `--muted`, `--muted-*` | Muted/subdued elements |\n| Destructive | `--destructive`, `--destructive-*` | Destructive/danger actions |\n| Background | `--background`, `--background-*` | App surfaces |\n| Foreground | `--foreground`, `--foreground-*` | Default typography colors |\n| Inverse | `--inverse` | Inverted color scheme |\n| Border | `--border` | Default border color |\n| Border line | `--border-line-*` | Border colors for different shades |\n| Layer | `--layer`, `--layer-*` | Layered elements (e.g. white buttons on white panel) |\n| Surface | `--surface`, `--surface-*` | Elevated surfaces |\n| Navbar | `--navbar`, `--navbar-*` | Navigation bar (background, border, divider) |\n| Navbar nav | `--navbar-nav-*` | Navigation bar items (foreground, hover, active) |\n| Sidebar | `--sidebar`, `--sidebar-*` | Sidebar (background, border, divider) |\n| Sidebar nav | `--sidebar-nav-*` | Sidebar items (foreground, hover, active) |\n| Card | `--card`, `--card-*` | Card component (border, divider, header, footer) |\n| Dropdown | `--dropdown`, `--dropdown-*` | Dropdown menus (items, hover, active) |\n| Select | `--select`, `--select-*` | Select component (items, hover, active) |\n| Overlay | `--overlay`, `--overlay-*` | Modal/overlay backgrounds |\n| Popover | `--popover`, `--popover-*` | Popover component |\n| Tooltip | `--tooltip`, `--tooltip-*` | Tooltip component |\n| Table | `--table-line` | Table borders |\n| Switch | `--switch` | Switch/toggle component |\n| Footer | `--footer`, `--footer-*` | Footer component |\n| Scrollbar | `--scrollbar-*` | Custom scrollbar (track, thumb) |\n| Chart | `--chart-*` | Chart colors (Apexcharts) |\n| Map | `--map-colors-*` | Map colors (jsvectormap) |\n\n---\n\n## Component preset patterns\n\nPreline uses numbered variants for component groups to offer different ready-made looks:\n\n| Pattern | Meaning |\n|---------|---------|\n| `--navbar-*` | Default navbar preset (base style) |\n| `--navbar-1-*` | Variant 1 preset (typically subtle/tinted surface) |\n| `--navbar-2-*` | Variant 2 preset (typically stronger tint or different border) |\n| `*` (no suffix) | Background/surface color of the component |\n| `*-line` | Border color for the component container |\n| `*-divider` | Divider lines inside the component |\n| `*-nav-*` | Nav item colors (foreground, hover, focus, active) |\n| `*-nav-list-divider` | Divider used in nav dropdown/list layouts |\n| `*-inverse` | Inverted/contrast variant |\n\nThis pattern applies to: navbar, sidebar, and similar navigation components.\n\n---\n\n## Full token reference by category\n\n### 1. Global surfaces + text\n\n```css\n/* Background tokens */\n--background          /* Main app background */\n--background-1        /* Secondary background (slightly tinted) */\n--background-2        /* Tertiary background (more tinted) */\n--background-plain    /* Plain white/black background */\n\n/* Foreground tokens */\n--foreground          /* Default text color */\n--foreground-inverse  /* Inverted text color (for dark backgrounds) */\n\n/* Inverse */\n--inverse             /* Inverted surface color */\n```\n\n### 2. Borders (full scale)\n\n```css\n--border              /* Default border color */\n--border-line-inverse /* Inverse border color */\n--border-line-1       /* Lightest border */\n--border-line-2\n--border-line-3\n--border-line-4\n--border-line-5\n--border-line-6\n--border-line-7\n--border-line-8       /* Darkest border */\n```\n\n### 3. Primary ramp + states\n\n```css\n/* Full color ramp (11 shades) */\n--primary-50\n--primary-100\n--primary-200\n--primary-300\n--primary-400\n--primary-500\n--primary-600\n--primary-700\n--primary-800\n--primary-900\n--primary-950\n\n/* State tokens */\n--primary             /* Base primary color */\n--primary-line        /* Primary border (usually transparent) */\n--primary-foreground  /* Text on primary background */\n--primary-hover       /* Hover state */\n--primary-focus       /* Focus state */\n--primary-active      /* Active/pressed state */\n--primary-checked     /* Checked state (checkboxes, radios) */\n```\n\n### 4. Secondary\n\n```css\n--secondary            /* Base secondary color */\n--secondary-line       /* Secondary border */\n--secondary-foreground /* Text on secondary background */\n--secondary-hover      /* Hover state */\n--secondary-focus      /* Focus state */\n--secondary-active     /* Active state */\n```\n\n### 5. Layer\n\n```css\n--layer               /* Layer background */\n--layer-line          /* Layer border */\n--layer-foreground    /* Text on layer */\n--layer-hover         /* Hover state */\n--layer-focus         /* Focus state */\n--layer-active        /* Active state */\n```\n\n### 6. Surface\n\n```css\n--surface             /* Base surface */\n--surface-1           /* Surface variant 1 */\n--surface-2           /* Surface variant 2 */\n--surface-3           /* Surface variant 3 */\n--surface-4           /* Surface variant 4 */\n--surface-5           /* Surface variant 5 */\n--surface-line        /* Surface border */\n--surface-foreground  /* Text on surface */\n--surface-hover       /* Hover state */\n--surface-focus       /* Focus state */\n--surface-active      /* Active state */\n```\n\n### 7. Muted\n\n```css\n--muted               /* Muted background */\n--muted-foreground    /* Muted text (lightest) */\n--muted-foreground-1  /* Muted text variant 1 */\n--muted-foreground-2  /* Muted text variant 2 (darkest) */\n--muted-hover         /* Hover state */\n--muted-focus         /* Focus state */\n--muted-active        /* Active state */\n```\n\n### 8. Destructive\n\n```css\n--destructive            /* Destructive/danger color */\n--destructive-foreground /* Text on destructive background */\n--destructive-hover      /* Hover state */\n--destructive-focus      /* Focus state */\n```\n\n### 9. Navbar (base + 2 variants)\n\n```css\n/* Base navbar */\n--navbar                /* Navbar background */\n--navbar-line           /* Navbar border */\n--navbar-divider        /* Navbar divider */\n--navbar-nav-foreground /* Nav item text */\n--navbar-nav-hover      /* Nav item hover */\n--navbar-nav-focus      /* Nav item focus */\n--navbar-nav-active     /* Nav item active */\n--navbar-nav-list-divider /* Nav list divider */\n--navbar-inverse        /* Inverse navbar background */\n\n/* Navbar variant 1 (subtle/tinted) */\n--navbar-1\n--navbar-1-line\n--navbar-1-divider\n--navbar-1-nav-foreground\n--navbar-1-nav-hover\n--navbar-1-nav-focus\n--navbar-1-nav-active\n--navbar-1-nav-list-divider\n\n/* Navbar variant 2 (stronger tint) */\n--navbar-2\n--navbar-2-line\n--navbar-2-divider\n--navbar-2-nav-foreground\n--navbar-2-nav-hover\n--navbar-2-nav-focus\n--navbar-2-nav-active\n--navbar-2-nav-list-divider\n```\n\n### 10. Sidebar (base + 2 variants)\n\n```css\n/* Base sidebar */\n--sidebar\n--sidebar-line\n--sidebar-divider\n--sidebar-nav-foreground\n--sidebar-nav-hover\n--sidebar-nav-focus\n--sidebar-nav-active\n--sidebar-nav-list-divider\n--sidebar-inverse\n\n/* Sidebar variant 1 */\n--sidebar-1\n--sidebar-1-line\n--sidebar-1-divider\n--sidebar-1-nav-foreground\n--sidebar-1-nav-hover\n--sidebar-1-nav-focus\n--sidebar-1-nav-active\n--sidebar-1-nav-list-divider\n\n/* Sidebar variant 2 */\n--sidebar-2\n--sidebar-2-line\n--sidebar-2-divider\n--sidebar-2-nav-foreground\n--sidebar-2-nav-hover\n--sidebar-2-nav-focus\n--sidebar-2-nav-active\n--sidebar-2-nav-list-divider\n```\n\n### 11. Card\n\n```css\n--card                /* Card background */\n--card-line           /* Card border */\n--card-divider        /* Card divider */\n--card-header         /* Card header background */\n--card-footer         /* Card footer background */\n--card-inverse        /* Inverse card background */\n```\n\n### 12. Dropdown\n\n```css\n--dropdown                /* Dropdown background */\n--dropdown-1              /* Dropdown variant background */\n--dropdown-line           /* Dropdown border */\n--dropdown-divider        /* Dropdown divider */\n--dropdown-header         /* Dropdown header background */\n--dropdown-footer         /* Dropdown footer background */\n--dropdown-item-foreground /* Item text */\n--dropdown-item-hover     /* Item hover */\n--dropdown-item-focus     /* Item focus */\n--dropdown-item-active    /* Item active */\n--dropdown-inverse        /* Inverse dropdown */\n```\n\n### 13. Select\n\n```css\n--select                /* Select background */\n--select-1              /* Select variant background */\n--select-line           /* Select border */\n--select-item-foreground /* Item text */\n--select-item-hover     /* Item hover */\n--select-item-focus     /* Item focus */\n--select-item-active    /* Item active */\n--select-inverse        /* Inverse select */\n```\n\n### 14. Overlay\n\n```css\n--overlay             /* Overlay/modal background */\n--overlay-line        /* Overlay border */\n--overlay-divider     /* Overlay divider */\n--overlay-header      /* Overlay header background */\n--overlay-footer      /* Overlay footer background */\n--overlay-inverse     /* Inverse overlay */\n```\n\n### 15. Popover, tooltip, table, switch\n\n```css\n/* Popover */\n--popover             /* Popover background */\n--popover-line        /* Popover border */\n\n/* Tooltip */\n--tooltip             /* Tooltip background */\n--tooltip-foreground  /* Tooltip text */\n--tooltip-line        /* Tooltip border */\n\n/* Table */\n--table-line          /* Table border */\n\n/* Switch */\n--switch              /* Switch knob color */\n```\n\n### 16. Footer\n\n```css\n--footer              /* Footer background */\n--footer-line         /* Footer border */\n--footer-inverse      /* Inverse footer */\n```\n\n### 17. Scrollbar\n\n```css\n--scrollbar-track         /* Scrollbar track */\n--scrollbar-thumb         /* Scrollbar thumb */\n--scrollbar-track-inverse /* Inverse track */\n--scrollbar-thumb-inverse /* Inverse thumb */\n```\n\n### 18. Charts (Apexcharts)\n\nImportant: keep chart `-hex` tokens variable-based in generated theme source. Do not hardcode literal hex or oklch values into the final theme file.\n\n```css\n/* Primary chart color */\n--chart-primary\n--chart-colors-primary\n--chart-colors-primary-inverse\n--chart-colors-primary-hex\n--chart-colors-primary-hex-inverse\n\n/* Chart palette (1-10) */\n--chart-1 through --chart-10\n--chart-colors-chart-1 through --chart-colors-chart-10\n--chart-colors-chart-1-inverse through --chart-colors-chart-10-inverse\n--chart-colors-chart-1-hex through --chart-colors-chart-10-hex\n--chart-colors-chart-1-hex-inverse through --chart-colors-chart-10-hex-inverse\n\n/* Chart backgrounds */\n--chart-colors-background\n--chart-colors-background-inverse\n--chart-colors-chart-inverse\n\n/* Chart foregrounds */\n--chart-colors-foreground\n--chart-colors-foreground-inverse\n\n/* Chart labels */\n--chart-colors-labels\n--chart-colors-labels-inverse\n--chart-colors-xaxis-labels\n--chart-colors-xaxis-labels-inverse\n--chart-colors-yaxis-labels\n--chart-colors-yaxis-labels-inverse\n\n/* Chart grid */\n--chart-colors-grid-border\n--chart-colors-grid-border-inverse\n\n/* Chart special */\n--chart-colors-bar-ranges\n--chart-colors-bar-ranges-inverse\n--chart-colors-candlestick-upward\n--chart-colors-candlestick-upward-inverse\n--chart-colors-candlestick-downward\n--chart-colors-candlestick-downward-inverse\n```\n\n### 19. Maps (jsvectormap)\n\n```css\n--map-colors-primary\n--map-colors-primary-inverse\n--map-colors-default\n--map-colors-default-inverse\n--map-colors-highlight\n--map-colors-highlight-inverse\n--map-colors-border\n--map-colors-border-inverse\n```\n\n---\n\n## Custom color palettes\n\nThe base theme defines custom color palettes that themes can use:\n\n### Khaki (earthy/warm neutral)\n\n```css\n--color-khaki-50 through --color-khaki-950\n```\n\n### Lavender (soft purple/mauve neutral)\n\n```css\n--color-lavender-50 through --color-lavender-950\n```\n\nThese are useful for creating warm or soft-toned themes instead of using standard gray/neutral.\n\n---\n\n## Default values reference\n\n### Light mode defaults (:root)\n\n```css\n:root {\n  /* Backgrounds */\n  --background: var(--color-white);\n  --background-1: var(--color-gray-50);\n  --background-2: var(--color-gray-100);\n  --background-plain: var(--color-white);\n\n  /* Foregrounds */\n  --foreground: var(--color-gray-800);\n  --foreground-inverse: var(--color-white);\n  --inverse: var(--color-gray-800);\n\n  /* Borders */\n  --border: var(--color-gray-200);\n  --border-line-inverse: var(--color-white);\n  --border-line-1: var(--color-gray-100);\n  --border-line-2: var(--color-gray-200);\n  --border-line-3: var(--color-gray-300);\n  --border-line-4: var(--color-gray-400);\n  --border-line-5: var(--color-gray-500);\n  --border-line-6: var(--color-gray-600);\n  --border-line-7: var(--color-gray-700);\n  --border-line-8: var(--color-gray-800);\n\n  /* Primary (blue by default) */\n  --primary-50: var(--color-blue-50);\n  --primary-100: var(--color-blue-100);\n  --primary-200: var(--color-blue-200);\n  --primary-300: var(--color-blue-300);\n  --primary-400: var(--color-blue-400);\n  --primary-500: var(--color-blue-500);\n  --primary-600: var(--color-blue-600);\n  --primary-700: var(--color-blue-700);\n  --primary-800: var(--color-blue-800);\n  --primary-900: var(--color-blue-900);\n  --primary-950: var(--color-blue-950);\n\n  --primary: var(--color-primary-600);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  --primary-focus: var(--color-primary-700);\n  --primary-active: var(--color-primary-700);\n  --primary-checked: var(--color-primary-600);\n\n  /* Secondary */\n  --secondary: var(--color-gray-900);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-white);\n  --secondary-hover: var(--color-gray-800);\n  --secondary-focus: var(--color-gray-800);\n  --secondary-active: var(--color-gray-800);\n\n  /* Layer */\n  --layer: var(--color-white);\n  --layer-line: var(--color-gray-200);\n  --layer-foreground: var(--color-gray-800);\n  --layer-hover: var(--color-gray-50);\n  --layer-focus: var(--color-gray-50);\n  --layer-active: var(--color-gray-50);\n\n  /* Surface */\n  --surface: var(--color-gray-100);\n  --surface-1: var(--color-gray-200);\n  --surface-2: var(--color-gray-300);\n  --surface-3: var(--color-gray-400);\n  --surface-4: var(--color-gray-500);\n  --surface-5: var(--color-gray-600);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-gray-800);\n  --surface-hover: var(--color-gray-200);\n  --surface-focus: var(--color-gray-200);\n  --surface-active: var(--color-gray-200);\n\n  /* Muted */\n  --muted: var(--color-gray-50);\n  --muted-foreground: var(--color-gray-400);\n  --muted-foreground-1: var(--color-gray-500);\n  --muted-foreground-2: var(--color-gray-600);\n  --muted-hover: var(--color-gray-100);\n  --muted-focus: var(--color-gray-100);\n  --muted-active: var(--color-gray-100);\n\n  /* Destructive */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Navbar (base) */\n  --navbar: var(--color-white);\n  --navbar-line: var(--color-gray-200);\n  --navbar-divider: var(--color-gray-200);\n  --navbar-nav-foreground: var(--color-gray-800);\n  --navbar-nav-hover: var(--color-gray-100);\n  --navbar-nav-focus: var(--color-gray-100);\n  --navbar-nav-active: var(--color-gray-100);\n  --navbar-nav-list-divider: var(--color-gray-200);\n  --navbar-inverse: var(--color-primary-950);\n\n  /* Navbar-1 */\n  --navbar-1: var(--color-gray-50);\n  --navbar-1-line: var(--color-gray-200);\n  --navbar-1-divider: var(--color-gray-200);\n  --navbar-1-nav-foreground: var(--color-gray-800);\n  --navbar-1-nav-hover: var(--color-gray-200);\n  --navbar-1-nav-focus: var(--color-gray-200);\n  --navbar-1-nav-active: var(--color-gray-200);\n  --navbar-1-nav-list-divider: var(--color-gray-200);\n\n  /* Navbar-2 */\n  --navbar-2: var(--color-gray-100);\n  --navbar-2-line: transparent;\n  --navbar-2-divider: var(--color-gray-300);\n  --navbar-2-nav-foreground: var(--color-gray-800);\n  --navbar-2-nav-hover: var(--color-gray-200);\n  --navbar-2-nav-focus: var(--color-gray-200);\n  --navbar-2-nav-active: var(--color-gray-200);\n  --navbar-2-nav-list-divider: var(--color-gray-200);\n\n  /* Sidebar follows same pattern as navbar */\n  /* Card, Dropdown, Select, Overlay, Popover, Tooltip, Table, Switch, Footer, Scrollbar */\n  /* See the project's existing base Preline theme source for complete default values */\n}\n```\n\n### Dark mode defaults (.dark)\n\n```css\n.dark {\n  /* Backgrounds */\n  --background: var(--color-neutral-800);\n  --background-1: var(--color-neutral-900);\n  --background-2: var(--color-neutral-900);\n  --foreground: var(--color-neutral-200);\n  --inverse: var(--color-neutral-950);\n\n  /* Borders */\n  --border: var(--color-neutral-700);\n  --border-line-1: var(--color-neutral-800);\n  --border-line-2: var(--color-neutral-700);\n  --border-line-3: var(--color-neutral-600);\n  --border-line-4: var(--color-neutral-500);\n  --border-line-5: var(--color-neutral-400);\n  --border-line-6: var(--color-neutral-300);\n  --border-line-7: var(--color-neutral-200);\n  --border-line-8: var(--color-neutral-100);\n\n  /* Primary (shifts to 500 in dark mode) */\n  --primary: var(--color-primary-500);\n  --primary-line: transparent;\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-600);\n  --primary-focus: var(--color-primary-600);\n  --primary-active: var(--color-primary-600);\n  --primary-checked: var(--color-primary-500);\n\n  /* Secondary */\n  --secondary: var(--color-white);\n  --secondary-line: transparent;\n  --secondary-foreground: var(--color-neutral-800);\n  --secondary-hover: var(--color-neutral-100);\n  --secondary-focus: var(--color-neutral-100);\n  --secondary-active: var(--color-neutral-100);\n\n  /* Layer */\n  --layer: var(--color-neutral-800);\n  --layer-line: var(--color-neutral-700);\n  --layer-foreground: var(--color-white);\n  --layer-hover: var(--color-neutral-700);\n  --layer-focus: var(--color-neutral-700);\n  --layer-active: var(--color-neutral-700);\n\n  /* Surface */\n  --surface: var(--color-neutral-700);\n  --surface-1: var(--color-neutral-600);\n  --surface-2: var(--color-neutral-500);\n  --surface-3: var(--color-neutral-600);\n  --surface-4: var(--color-neutral-500);\n  --surface-5: var(--color-neutral-400);\n  --surface-line: transparent;\n  --surface-foreground: var(--color-neutral-200);\n  --surface-hover: var(--color-neutral-600);\n  --surface-focus: var(--color-neutral-600);\n  --surface-active: var(--color-neutral-600);\n\n  /* Muted */\n  --muted: var(--color-neutral-800);\n  --muted-foreground: var(--color-neutral-500);\n  --muted-foreground-1: var(--color-neutral-400);\n  --muted-foreground-2: var(--color-neutral-300);\n  --muted-hover: var(--color-neutral-700);\n  --muted-focus: var(--color-neutral-700);\n  --muted-active: var(--color-neutral-700);\n\n  /* Destructive (same red, readable in dark) */\n  --destructive: var(--color-red-500);\n  --destructive-foreground: var(--color-white);\n  --destructive-hover: var(--color-red-600);\n  --destructive-focus: var(--color-red-600);\n\n  /* Components shift to neutral-800/900 backgrounds */\n  /* Nav items shift to neutral-700 hover states */\n  /* See the project's existing base Preline theme source for complete dark mode values */\n}\n```\n\n---\n\n## Theme-scoped overrides\n\nWhen creating a theme, override values under theme-scoped selectors:\n\n```css\n/* Light mode */\n:root[data-theme=\"theme-<name>\"],\n[data-theme=\"theme-<name>\"] {\n  /* Override tokens here */\n}\n\n/* Dark mode */\n[data-theme=\"theme-<name>\"].dark {\n  /* Override dark-specific tokens here */\n}\n```\n\n---\n\n## Color format guidelines\n\n- For most tokens: use Tailwind color variables `var(--color-<color>-<shade>)`\n- For oklch colors: acceptable for standard tokens\n- For chart `-hex` tokens: keep them as stable variable references in generated source\n- For custom colors: can use hex, rgb, hsl, or oklch\n\n---\n\n## Quick token count summary\n\n| Category | Token Count |\n|----------|-------------|\n| Global surfaces + text | 7 |\n| Borders | 10 |\n| Primary (ramp + states) | 18 |\n| Secondary | 6 |\n| Layer | 6 |\n| Surface | 11 |\n| Muted | 7 |\n| Destructive | 4 |\n| Navbar (3 variants) | 27 |\n| Sidebar (3 variants) | 27 |\n| Card | 6 |\n| Dropdown | 11 |\n| Select | 8 |\n| Overlay | 6 |\n| Popover | 2 |\n| Tooltip | 3 |\n| Table, Switch, Footer | 5 |\n| Scrollbar | 4 |\n| Charts | ~50+ |\n| Maps | 8 |\n| Total | ~220+ tokens\n\n<!-- File: skills/theme-generator/docs/validation-checklist.md (Tokens: ~537 | Category: anthropic-skill) -->\n# Validation checklist (must self-check before returning)\n\n## Final source style\n- [ ] Only one project file is created or edited: `<name>.css`\n- [ ] The final file is a production-style `<name>.css` file\n- [ ] `@theme theme-<name> inline { }` is present\n- [ ] Any custom brand/gray palette lives inside `@theme theme-<name> inline { }` in the new theme file\n- [ ] Semantic tokens prefer assigned vars such as `--color-primary-*`, `--background-*`, `--foreground`, `--border`, `--surface-*`, or shared palette vars when available\n- [ ] The custom theme does not redefine shared aliases like `--color-primary-*` inside `@theme` when the repo base theme already owns them\n- [ ] No raw `oklch(...)` or literal hex values remain in semantic, chart, or map token assignments in the final theme file\n\n## Structure\n- [ ] Theme key consistent: `theme-<name>` everywhere\n- [ ] Light selector uses `:root[data-theme=...]`, `[data-theme=...]`\n- [ ] Dark selector is `[data-theme=\"theme-<name>\"].dark`\n- [ ] Semantic token definitions are in selector blocks (NOT inside @theme block)\n- [ ] No requirement to change HTML utility classes (only `data-theme`, optional `.dark`)\n\n## Palette strategy\n- [ ] Primary ramp maps `--primary-*` directly to theme-local palette vars\n- [ ] Neutral/background tokens use assigned palette vars\n- [ ] If a new custom palette is needed, it is declared inside the new theme file's `@theme` block, not by editing another file\n\n## Token coverage\n- [ ] Full coverage present across global tokens, states, and major component groups\n- [ ] CSS syntax valid (balanced braces, no broken comments)\n- [ ] If behavior overrides exist, they are theme-scoped only\n- [ ] Chart/map tokens use variable references matching shipped theme patterns\n\n## Dark mode consistency\n- [ ] Dark mode uses CSS variables (`var(--color-*)`) not hardcoded color literals\n- [ ] Dark mode uses one consistent grayscale family unless there is a deliberate, reference-backed reason not to\n- [ ] Hover/focus/active states remain readable and distinguishable\n- [ ] `--foreground-inverse` remains readable on vivid utility colors in dark mode (for example red badges/chips)\n- [ ] Dark-mode chart/decorative tokens avoid very deep brand steps when those tokens are used in gradient text or accent UI\n\n<!-- File: skills/theme-generator/docs/workflow.md (Tokens: ~1373 | Category: anthropic-skill) -->\n# Workflow\n\nUse this file as the default operating guide for the active theme skill.\n\n## Task modes\n\nClassify the request before doing any work:\n\n- `preview`: generate CSS and return it without writing a project file\n- `write`: create one new `<name>.css` theme file\n- `validate`: review or fix an existing generated theme file\n\n## Normalize the request\n\nConvert natural language into the generator's small, explicit input set:\n\n| Input | Required | Notes |\n|------|----------|-------|\n| `name` | yes | kebab-case theme name |\n| `hue` or `primaryColor` | yes | use `primaryColor` only when user gave a brand hex |\n| `style` | no | `vibrant` by default, `soft` for muted/pastel/desaturated requests |\n| `useCustomDarkGray` | no | true only when user explicitly wants matching/cohesive dark neutrals |\n| `tailwindGray` | no | choose `stone`, `neutral`, `zinc`, `slate`, or `gray` based on brand warmth |\n| `fontSans` / `fontSerif` / `fontMono` | no | include only when requested |\n\nUse `docs/palette-guidance.md` for hue ranges, mood mapping, contrast heuristics, and gray-family decisions.\n\n### Natural-language interpretation\n\nUse the user's language to infer the palette direction:\n\n| User language | Default interpretation |\n|--------------|------------------------|\n| warm sunset, cozy, inviting | orange/amber primary, warm neutrals, medium contrast |\n| professional, clean, SaaS | blue/indigo primary, slate or zinc neutrals |\n| playful, candy, youthful | pink/purple/cyan primary, higher contrast |\n| earthy, organic, calm | green/teal primary, stone neutrals |\n| soft, muted, ash, dusty | `style: soft` |\n| matching dark mode, cohesive dark, unified neutrals | `useCustomDarkGray: true` |\n\nWhen the request is vague:\n\n1. Make an opinionated, coherent palette choice.\n2. Keep contrast readable.\n3. Prefer a consistent system over a flashy but uneven palette.\n\nForeground rule:\n- When mapping text or icon colors onto the primary brand color, choose the foreground by contrast against the generated primary states, not by hue bucket alone.\n- Treat `--foreground-inverse` separately from primary foregrounds. In Preline it is reused on hardcoded vivid utility backgrounds such as red badges, so dark mode should keep it readable on saturated colors instead of tinting it into a dark neutral.\n- Treat chart tokens as decorative UI tokens, not just data-series tokens. Dark mode must override any chart shades that would become too dark for gradient text, badges, or chart accents on dark surfaces.\n\n## Discover the target directory\n\nUse the bundled resolver instead of broad repo traversal:\n\n```bash\nnode scripts/find-themes-dir.js\n```\n\nTrusted theme directories are checked in this order:\n\n- `src/assets/css/themes/`\n- `src/css/themes/`\n- `src/styles/themes/`\n- `assets/css/themes/`\n- `styles/themes/`\n- `dist/assets/css/themes/`\n- `npm/preline/css/themes/`\n\nRules:\n\n- Prefer source theme directories over generated outputs.\n- Do not use repo-wide `find .` traversal for this task.\n- If no trusted theme directory is found and the user did not provide a path, ask where the file should be written.\n\n## Generate CSS\n\nUse the local wrapper as the only execution entry point:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --stdout\n```\n\nWrite a theme file with:\n\n```bash\nnode scripts/run-theme-generator.js --name seafoam --hue 180 --style vibrant --tailwind-gray zinc --output src/assets/css/themes/seafoam.css\n```\n\nRules:\n\n- Do not use `npx`.\n- Do not interpolate raw user text into shell commands.\n- Do not chain temp-file cleanup or other unrelated shell work into the command.\n- Do not create or edit any project file other than the new theme file.\n\n## Theme structure expectations\n\nThe wrapper should emit a production-style standalone `<name>.css` file:\n\n1. Header comment\n2. Optional theme-scoped `@layer utilities` overrides\n3. `@theme theme-<name> inline { }`\n4. Light selector\n   - `:root[data-theme=\"theme-<name>\"],`\n   - `[data-theme=\"theme-<name>\"]`\n5. Dark selector\n   - `[data-theme=\"theme-<name>\"].dark`\n\nKeep custom brand and gray palettes inside the file's `@theme` block. Keep semantic tokens in the selector blocks.\n\nSee `docs/final-output-style.md` for the final file rules and `docs/token-reference.md` only when detailed token coverage inspection is necessary.\n\n## Dark-mode decision rule\n\nAlways generate both palettes in `@theme theme-<name> inline { }`:\n\n- `--color-<name>-*` for the brand ramp\n- `--color-<name>-gray-*` for the theme's neutral ramp\n\nDark-mode neutrals depend on user intent:\n\n- Explicit matching/cohesive dark request: use the theme's custom gray palette in dark mode.\n- Default path: keep light mode custom and use a Tailwind gray family in dark mode.\n\nPrimary foreground decision:\n- Use the generated brand shades to determine whether white or dark text is more readable on the primary color in each mode.\n- In dark mode, prefer a deeper primary step with white text when that profile remains readable; only fall back to lighter primary steps with dark text when white would fail.\n\nUse CSS variables in semantic tokens and dark-mode overrides. Do not hardcode raw `oklch(...)` or hex values in semantic, chart, or map tokens.\n\n## Validation focus\n\nBefore returning:\n\n- confirm only one project file was created or edited\n- confirm the theme key is `theme-<name>` everywhere\n- confirm the file has full light and dark token coverage\n- confirm chart and map tokens remain variable-based\n- confirm dark-mode states remain readable\n\nUse `docs/validation-checklist.md` as the final gate.\n\n## Response shape\n\nMatch the response to the mode:\n\n- Preview: return the CSS first, then the enable snippet if useful\n- Write: return the created path, a short summary, and the enable snippet\n- Validate: list findings first, then the applied or recommended fix\n\nEnable snippet:\n\n```html\n<html data-theme=\"theme-<theme-name>\">\n```\n\n<!-- File: skills/theme-generator/examples.md (Tokens: ~1816 | Category: anthropic-skill) -->\n# Examples — Preline Theme Generator\n\nThese examples are palette-and-structure references, not literal final-output templates.\n\nImportant: examples below are structural references. Final delivered theme files should follow `docs/final-output-style.md` and should never require edits to any companion project file.\n\nThese examples define what \"good output\" looks like.\n\n## Contents\n\n- [Example 1 — Calm teal SaaS theme](#example-1--calm-teal-saas-theme-full-theme-clean)\n- [Example 2 — Retro sharp corners](#example-2--retro-sharp-corners-behavior-change-must-be-scoped)\n- [Example 3 — Theme-scoped font token](#example-3--theme-scoped-font-token-no-mapping-changes)\n- [Example 4 — Brand match with exact primary color](#example-4--brand-match-with-exact-primary-color)\n\n## Example 1 — Calm teal SaaS theme (full theme, clean)\n\n**Input**\n- themeName: `seafoam`\n- mood: calm, modern SaaS\n- primaryHue: teal (180)\n- contrast: medium\n- style: vibrant\n- darkNeutralFamily: zinc\n\n**Expected Structure**\n- `@theme theme-seafoam inline { }` contains brand + gray palettes\n- Light mode selector: `:root[data-theme=\"theme-seafoam\"], [data-theme=\"theme-seafoam\"]`\n- Dark mode selector: `[data-theme=\"theme-seafoam\"].dark`\n- Full coverage: global surfaces, borders, primary ramp, states, components\n- Dark mode uses Tailwind zinc for neutrals (no matching dark requested)\n\n### Output (truncated)\n\n```css\n/* ------------------------------ */\n/* -------- Seafoam ------------- */\n/* ------------------------------ */\n\n@theme theme-seafoam inline {\n  /* BRAND PALETTE (vibrant) */\n  --color-seafoam-50: oklch(97% 0.08 180);\n  --color-seafoam-100: oklch(94% 0.10 180);\n  --color-seafoam-200: oklch(88% 0.12 180);\n  --color-seafoam-300: oklch(80% 0.14 180);\n  --color-seafoam-400: oklch(70% 0.16 180);\n  --color-seafoam-500: oklch(60% 0.14 180);\n  --color-seafoam-600: oklch(52% 0.12 180);\n  --color-seafoam-700: oklch(44% 0.10 180);\n  --color-seafoam-800: oklch(36% 0.08 180);\n  --color-seafoam-900: oklch(28% 0.06 180);\n  --color-seafoam-950: oklch(20% 0.05 180);\n  \n  /* GRAY PALETTE (bell curve chroma) */\n  --color-seafoam-gray-50: oklch(98% 0.002 180);\n  --color-seafoam-gray-100: oklch(95.5% 0.004 180);\n  --color-seafoam-gray-200: oklch(89.7% 0.008 180);\n  /* ... 300-700 ... */\n  --color-seafoam-gray-800: oklch(34.6% 0.008 180);\n  --color-seafoam-gray-900: oklch(30.6% 0.005 180);\n  --color-seafoam-gray-950: oklch(20.1% 0.003 180);\n\n}\n\n:root[data-theme=\"theme-seafoam\"],\n[data-theme=\"theme-seafoam\"] {\n  /* Global */\n  --background: var(--color-white);\n  --background-1: var(--color-seafoam-gray-50);\n  --foreground: var(--color-seafoam-gray-800);\n  \n  /* Primary ramp */\n  --primary-50: var(--color-seafoam-50);\n  --primary-100: var(--color-seafoam-100);\n  /* ... full ramp ... */\n  \n  /* Primary states */\n  --primary: var(--color-primary-600);\n  --primary-foreground: var(--color-white);\n  --primary-hover: var(--color-primary-700);\n  \n  /* Components prefer assigned semantic tokens */\n  --navbar: var(--background-plain);\n  --navbar-line: var(--border);\n  /* ... */\n}\n\n[data-theme=\"theme-seafoam\"].dark {\n  /* Dark mode uses Tailwind zinc */\n  --background: var(--color-zinc-800);\n  --background-1: var(--color-zinc-900);\n  --foreground: var(--color-zinc-200);\n  \n  --primary: var(--color-primary-400);\n  --primary-foreground: var(--color-zinc-900); /* Contrast-selected because the generated light-brand states are too light for white */\n  /* ... */\n}\n```\n\n---\n\n## Example 2 — Retro sharp corners (behavior change must be scoped)\n\n**Input**\n- themeName: `arcade`\n- mood: retro 90s\n- primaryHue: magenta (320)\n- contrast: high\n- radiusStyle: `retro-sharp`\n\n**Expected Structure**\n- Full token coverage\n- Theme sets radius tokens in theme selector (NOT in @theme block)\n- Any \"rounded\" behavior override inside `@layer utilities` scoped to theme\n\n### Output (radius section only)\n\n```css\n:root[data-theme=\"theme-arcade\"],\n[data-theme=\"theme-arcade\"] {\n  /* RETRO SHARP CORNERS */\n  --radius-sm: 0px;\n  --radius-md: 2px;\n  --radius-lg: 4px;\n  --radius-xl: 4px;\n  --radius-2xl: 6px;\n  --radius-3xl: 6px;\n  \n  /* ... rest of tokens ... */\n}\n\n/* Optional: Override rounded utility if needed */\n@layer utilities {\n  :is(:root[data-theme=\"theme-arcade\"], [data-theme=\"theme-arcade\"]) .rounded {\n    border-radius: var(--radius-md);\n  }\n}\n```\n\n---\n\n## Example 3 — Theme-scoped font token (no mapping changes)\n\n**Input**\n- themeName: `leafy-modern`\n- mood: organic, clean\n- primaryHue: emerald (145)\n- fontSans: `\"Roboto\", ui-sans-serif, system-ui, sans-serif`\n\n**Expected Structure**\n- Theme sets `--font-sans` inside theme selector (NOT in @theme block)\n- Theme does NOT import Google Fonts (font loading is separate)\n- User enables via `data-theme=\"theme-leafy-modern\"` only\n\n### Output (font section only)\n\n```css\n:root[data-theme=\"theme-leafy-modern\"],\n[data-theme=\"theme-leafy-modern\"] {\n  /* TYPOGRAPHY */\n  --font-sans: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n  \n  /* ... rest of tokens ... */\n}\n```\n\n---\n\n## Example 4 — Brand match with exact primary color\n\n**Input**\n- themeName: `brand-x`\n- primaryColor: `#2F6BFF` (converts to hue ~225)\n- mood: confident, high clarity\n- contrast: medium-high\n\n**Expected Structure**\n- Script converts hex to hue automatically\n- Primary ramp derived coherently from the brand hue\n- `--primary-foreground` readable (white on this blue)\n- Chart `-hex` tokens remain variable-based placeholders in generated source\n\n### Output (chart section showing variable-based chart tokens)\n\n```css\n:root[data-theme=\"theme-brand-x\"],\n[data-theme=\"theme-brand-x\"] {\n  /* ... other tokens ... */\n  \n  /* CHARTS */\n  --chart-primary: var(--color-primary-500);\n  --chart-colors-primary: var(--color-primary-500);\n  --chart-colors-primary-hex: var(--color-primary-500);\n  --chart-colors-primary-hex-inverse: var(--color-primary-300);\n  \n  --chart-1: var(--color-primary-500);\n  --chart-colors-chart-1-hex: var(--color-primary-500);\n  /* ... */\n}\n```\n\n<!-- File: skills/theme-generator/SKILL.md (Tokens: ~557 | Category: anthropic-skill) -->\n---\nname: preline-theme-generator\ndescription: Generate, preview, save, or validate Preline theme CSS using the bundled local generator in this skill. Use when the user asks to create a new Preline theme, match a brand color, translate a mood into a theme, generate light and dark theme CSS, preview theme output without writing files, or review and fix generated theme tokens. Do not use for generic non-Preline CSS work or arbitrary command execution.\n---\n\n# Preline Theme Generator\n\nUse the bundled local scripts. Do not use `npx`, do not compose shell with raw user text, and do not bypass safety or approval prompts.\n\n## Read Order\n\n1. Read `docs/workflow.md`.\n2. Read `docs/palette-guidance.md`.\n3. Read `docs/final-output-style.md` before writing a final theme file.\n4. Read `docs/validation-checklist.md` before closing the task.\n5. Read `examples.md` only if the user wants stylistic guidance or sample outputs.\n6. Read `docs/token-reference.md` only if you need to inspect token coverage in detail.\n\n## Workflow\n\nFollow `docs/workflow.md` for the full operating path.\n\nKey requirements:\n\n1. Identify whether the task is preview, write, or validate.\n2. Normalize the request into explicit generator inputs.\n3. Resolve the target directory with `scripts/find-themes-dir.js`.\n4. Generate CSS only through `scripts/run-theme-generator.js`.\n5. Create or edit only the new theme file.\n6. Validate with `docs/validation-checklist.md`.\n7. Return the result in the mode-appropriate format.\n\n## Security Constraints\n\n- Never use wording that attempts to bypass tool or approval safeguards.\n- Never use `npx` or any network-fetched package for this skill.\n- Never interpolate raw user text directly into shell commands.\n- Never use broad `find .` traversal for path discovery; use `scripts/find-themes-dir.js` or a user-confirmed path.\n- Never write outside a confirmed theme directory.\n- Use `scripts/run-theme-generator.js` as the only execution entry point for generation.\n- Never create or edit additional project files as part of theme generation.\n\n## Common Prompts\n\n- \"Create a sunset theme.\"\n- \"Match this brand color: #2F6BFF.\"\n- \"Generate a cyberpunk dark theme with matching dark mode.\"\n- \"Preview the CSS before writing a file.\"\n- \"Review this generated theme and fix token coverage.\"\n\n## Key References\n\n- `docs/workflow.md`\n- `docs/palette-guidance.md`\n- `docs/final-output-style.md`\n- `docs/validation-checklist.md`\n- `examples.md`\n- `docs/token-reference.md`\n\n<!-- File: src/plugins/accordion/README.md (Tokens: ~3847 | Category: plugin-manifest) -->\n# Accordion\n\nBuild vertically collapsing accordions in combination with Accordion JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/accordion) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/accordion.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Accordion component allows you to create collapsible content sections that can be expanded or collapsed by clicking on toggle buttons. It supports multiple accordion items within a group, with options for keeping items open, ensuring at least one item stays open, and treeview mode for hierarchical structures.\n\n**Key Features:**\n- Multiple accordion items in a single group\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Treeview mode support\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Accordion plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/accordion\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/accordion */\n@source \"../node_modules/@preline/accordion/*.js\";\n@import \"./node_modules/@preline/accordion/variants.css\";\n@import \"./node_modules/@preline/accordion/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/accordion/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSAccordion from \"@preline/accordion/non-auto.mjs\";\n\n  new HSAccordion(document.querySelector(\"#accordion\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/accordion` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/accordion\";\n```\n\n`@preline/accordion/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSAccordion from \"@preline/accordion/non-auto\";\n\nHSAccordion.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#accordion\");\nif (el) new HSAccordion(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an accordion component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three accordion items within a group, with the first item open by default.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion active\" id=\"hs-unstyled-heading-one\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"true\" aria-controls=\"hs-unstyled-collapse-one\">\n      Accordion #1\n    </button>\n    <div id=\"hs-unstyled-collapse-one\" class=\"hs-accordion-content overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-one\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-two\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-two\">\n      Accordion #2\n    </button>\n    <div id=\"hs-unstyled-collapse-two\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-two\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-unstyled-heading-three\">\n    <button class=\"hs-accordion-toggle\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-three\">\n      Accordion #3\n    </button>\n    <div id=\"hs-unstyled-collapse-three\" class=\"hs-accordion-content hidden overflow-hidden transition-[height] duration-300\" role=\"region\" aria-labelledby=\"hs-unstyled-heading-three\">\n      Accordion content that expands and collapses when the toggle button is clicked.\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-accordion-group`: Required wrapper container for all accordion items\n- `hs-accordion`: Required container for each accordion item\n- `hs-accordion-toggle`: Required button element that triggers expand/collapse\n- `hs-accordion-content`: Required container for collapsible content\n- Unique `id` attributes for each accordion item and its content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- To have an accordion item open by default, add the `active` class to the `hs-accordion` element\n- For closed items, add the `hidden` class to the `hs-accordion-content` element\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure accordion behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-accordion-always-open` | `hs-accordion-group` | boolean attribute | `false` (absent) | When present (boolean attribute), accordion items stay open when another item is opened. By default, opening one item closes others. Presence of attribute = `true`, absence = `false`. |\n\n**Example:**\n```html\n<!-- Boolean attribute: presence = true, absence = false -->\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <!-- Multiple items can be open simultaneously -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `--stop-propagation:*` | `hs-accordion-toggle` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, interrupts event propagation when toggle button is clicked. Useful when accordion is nested inside clickable elements. |\n| `--keep-one-open:*` | `hs-accordion-group` | `\"true\"` \\| `\"false\"` | `\"false\"` | When `\"true\"`, ensures at least one accordion item remains open at all times. Prevents closing the last open item. |\n\n**Example:**\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <!-- At least one item will always be open -->\n</div>\n\n<button class=\"hs-accordion-toggle --stop-propagation:true\">\n  <!-- Click event won't bubble up -->\n</button>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the accordion to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-group` | Container wrapper | Groups multiple accordion items together |\n| `hs-accordion` | Each accordion item container | Identifies an individual accordion item |\n| `hs-accordion-toggle` | Button element | The clickable element that expands/collapses content |\n| `hs-accordion-content` | Content container | The collapsible content area |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-accordion-treeview-root` | Parent container | When present, activates treeview mode for hierarchical accordion structures |\n| `hs-accordion-selectable` | Elements inside treeview | When treeview mode is active, elements with this class become selectable |\n\n**Treeview Mode Example:**\n```html\n<div class=\"hs-accordion-treeview-root\">\n  <div class=\"hs-accordion-group\">\n    <div class=\"hs-accordion\">\n      <button class=\"hs-accordion-toggle\">Parent</button>\n      <div class=\"hs-accordion-content\">\n        <div class=\"hs-accordion-selectable\">Selectable item</div>\n      </div>\n    </div>\n  </div>\n</div>\n```\n\n## JavaScript API\n\nThe `HSAccordion` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an accordion instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed accordion item. Returns `false` if item is already open, `true` otherwise. If other items are open and `always-open` is not set, they will be closed. |\n| `hide()` | None | `boolean` | Collapses the currently open accordion item. Returns `false` if item is already closed or if `keep-one-open` is enabled and this is the last open item, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the accordion instance, removes all generated markup, classes, and event listeners. Use when removing accordion from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSAccordion` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSAccordion.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \\| number, element: HSAccordion } \\| HTMLElement \\| null` | Returns the accordion instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSAccordion` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if accordion instance is not found. |\n| `HSAccordion.show(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. Only opens if item is not already open. |\n| `HSAccordion.hide(target)` | `target`: `HSAccordion \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. Only closes if item is open and `keep-one-open` allows it. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new accordion instance\nconst accordion = new HSAccordion(document.querySelector('#hs-accordion'));\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion programmatically\nshowBtn.addEventListener('click', () => {\n  accordion.show();\n});\n\n// Close accordion programmatically\nhideBtn.addEventListener('click', () => {\n  accordion.hide();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\nconst hideBtn = document.querySelector('#hs-hide-btn');\n\n// Open accordion using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSAccordion.show('#hs-accordion');\n});\n\n// Close accordion using static method\nhideBtn.addEventListener('click', () => {\n  HSAccordion.hide('#hs-accordion');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSAccordion instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeAccordion() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying accordion instance**\n```javascript\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-accordion').remove();\n  });\n}\n```\n\n## Events\n\nAccordion instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeOpen` | Before accordion item opens | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts opening. Can be used to cancel or modify behavior. |\n| `on:open` | After accordion item opens | `HTMLElement` (current accordion element) | Fires after the accordion item has fully opened. Use for post-open actions like loading content. |\n| `on:beforeClose` | Before accordion item closes | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts closing. Can be used to cancel or modify behavior. |\n| `on:close` | After accordion item closes | `HTMLElement` (current accordion element) | Fires after the accordion item has fully closed. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get accordion instance\nconst instance = HSAccordion.getInstance('#hs-accordion', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (accordionElement) => {\n    console.log('Accordion opened:', accordionElement);\n    // Perform actions after accordion opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (accordionElement) => {\n    console.log('Accordion closed:', accordionElement);\n    // Perform cleanup or state updates\n  });\n\n  // Listen to beforeOpen event (can be used to cancel)\n  element.on('beforeOpen', (accordionElement) => {\n    console.log('About to open:', accordionElement);\n    // Can add conditional logic here\n  });\n\n  // Listen to beforeClose event (can be used to cancel)\n  element.on('beforeClose', (accordionElement) => {\n    console.log('About to close:', accordionElement);\n    // Can add conditional logic here\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Multiple Open Items\n\nAllow multiple accordion items to be open simultaneously.\n\n```html\n<div class=\"hs-accordion-group\" data-hs-accordion-always-open>\n  <div class=\"hs-accordion\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content hidden\">Content 1</div>\n  </div>\n\n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: Always Keep One Open\n\nEnsure at least one item is always open.\n\n```html\n<div class=\"hs-accordion-group --keep-one-open:true\">\n  <div class=\"hs-accordion active\" id=\"hs-item-first\">\n    <button class=\"hs-accordion-toggle\">Item 1</button>\n    <div class=\"hs-accordion-content\">Content 1</div>\n  </div>\n  \n  <div class=\"hs-accordion\" id=\"hs-item-second\">\n    <button class=\"hs-accordion-toggle\">Item 2</button>\n    <div class=\"hs-accordion-content hidden\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl accordion from external buttons.\n\n```html\n<div class=\"hs-accordion-group\">\n  <div class=\"hs-accordion\" id=\"hs-accordion-first\">\n    <button class=\"hs-accordion-toggle\">Content</button>\n    <div class=\"hs-accordion-content hidden\">Content here</div>\n  </div>\n</div>\n\n<button id=\"hs-open-all\">Open All</button>\n<button id=\"hs-close-all\">Close All</button>\n\n<script>\n  const instance = HSAccordion.getInstance('#hs-accordion-first', true);\n\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-all').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-close-all').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/carousel/README.md (Tokens: ~3611 | Category: plugin-manifest) -->\n# Carousel\n\nBuild carousels and sliders with Carousel JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/carousel) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/carousel.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Carousel component provides a flexible, feature-rich slider for displaying multiple items in a scrollable container. It supports autoplay, infinite loop, drag gestures, responsive breakpoints, and multiple display modes.\n\n**Key Features:**\n- Multiple slides in a scrollable container\n- Autoplay and infinite loop support\n- Drag/swipe gestures for navigation\n- Responsive breakpoints for different screen sizes\n- Multiple display modes (default, snap, bounded)\n- Programmatic control via JavaScript API\n- Keyboard navigation support\n- Customizable pagination dots\n\n## Installation\n\nTo get started, install Carousel plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/carousel\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/carousel */\n@source \"../node_modules/@preline/carousel/*.js\";\n@import \"./node_modules/@preline/carousel/variants.css\";\n@import \"./node_modules/@preline/carousel/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/carousel/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCarousel from \"@preline/carousel/non-auto.mjs\";\n\n  new HSCarousel(document.querySelector(\"#carousel\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/carousel` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/carousel\";\n```\n\n`@preline/carousel/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCarousel from \"@preline/carousel/non-auto\";\n\nHSCarousel.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#carousel\");\nif (el) new HSCarousel(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a carousel component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The carousel displays three slides with previous/next navigation buttons.\n\n```html\n<div data-hs-carousel='{\n    \"loadingClasses\": \"opacity-0\"\n  }' class=\"relative\">\n  <div class=\"hs-carousel relative overflow-hidden w-full min-h-64\">\n    <div class=\"hs-carousel-body absolute top-0 bottom-0 start-0 flex flex-nowrap transition-transform duration-700 opacity-0\">\n      <div class=\"hs-carousel-slide\">\n        1\n      </div>\n      <div class=\"hs-carousel-slide\">\n        2\n      </div>\n      <div class=\"hs-carousel-slide\">\n        3\n      </div>\n    </div>\n  </div>\n\n  <button type=\"button\" class=\"hs-carousel-prev\">\n    <span aria-hidden=\"true\">«</span>\n    <span class=\"sr-only\">Previous</span>\n  </button>\n  <button type=\"button\" class=\"hs-carousel-next\">\n    <span class=\"sr-only\">Next</span>\n    <span aria-hidden=\"true\">»</span>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-carousel`: Required on the container element, contains configuration options\n- `hs-carousel`: Required wrapper for slides collection, must contain `overflow-hidden` class\n- `hs-carousel-body`: Required slides container\n- `hs-carousel-slide`: Required for each slide element\n- `hs-carousel-prev`: Optional previous slide button\n- `hs-carousel-next`: Optional next slide button\n- `hs-carousel-pagination`: Optional pagination container\n\n**Initial State:**\n- Carousel body typically starts with `opacity-0` which is removed after loading\n- First slide is active by default (index 0)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-carousel` attribute as a JSON object.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `currentIndex` | number | `0` | Specifies the index of the current slide initially (from 0 to slides quantity). |\n| `loadingClasses` | string | `\"opacity-0\"` | Specifies which classes should be removed after the carousel is loaded. CSS classes should be separated with space. |\n| `dotsItemClasses` | string | - | Specifies which classes will be added to the point elements (which are generated automatically). CSS classes should be separated with space. |\n| `isAutoHeight` | boolean | `false` | Sets the height of the carousel to the height of the current slide. |\n| `isAutoPlay` | boolean | `false` | Enables autoplay. Slides advance automatically at the interval specified by `speed`. |\n| `isInfiniteLoop` | boolean | `true` | Enables infinite loop. When reaching the last slide, it wraps to the first slide. |\n| `isCentered` | boolean | `false` | Enables centered mode. This mode adds space on the sides to center the slides. |\n| `isSnap` | boolean | `false` | Enables a mode in which you can scroll the contents of the slider using the scroll along the x-axis, while the active slider is centered relative to the slider. |\n| `isScrollBlocked` | boolean | `false` | Prevents the user from scrolling the slider. Useful when you need to prevent scrolling while the carousel is active. |\n| `isDraggable` | boolean | `false` | Adds the ability to change slides using dragging. Requires adding `hs-carousel:dragging:transition-none` class to the `hs-carousel-body`. Not working with `isSnap: true` option. |\n| `isRTL` | boolean | `false` | Turns on RTL (right-to-left) mode. |\n| `hasSnapSpacers` | boolean | `true` | Adds additional elements to create a constant centering effect. |\n| `slidesQty` | object \\| number | `1` | Allows to set the number of slides to display at a certain screen resolution if passed as an object (e.g., `{\"xs\": 1, \"md\": 2, \"lg\": 3}`). If passed as a number, the specified number of slides will be displayed for all screen resolutions. |\n| `slideBy` | object \\| number | `1` | Specifies how many slides to move when navigating through the carousel. Can be a number for all screen sizes or an object with breakpoint-specific values. |\n| `speed` | number | `4000` | Autoplay animation speed in milliseconds. Available if `isAutoplay: true`. |\n| `updateDelay` | number | `0` | Allows you to delay the update function when resizing a window. Suitable for a slider with images, for more accurate calculation of the size of the images. |\n| `mode` | `\"default\"` \\| `\"snap\"` \\| `\"bounded\"` | `\"default\"` | Defines the carousel's behavior mode. `default` for standard sliding, `snap` for scroll-based navigation with snap points, and `bounded` for a fixed-width container with responsive slides. |\n| `boundedOptions` | object | `null` | Configuration options for the bounded mode carousel. Includes `maxWidth` to set the maximum width of the carousel container, `slidesGap` to define spacing between slides, and `spacersWidth` to control the width of side spacers. When `spacersWidth` is set to `\"auto\"`, it automatically calculates equal spacing on both sides. |\n\n**Example:**\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 3000,\n  \"slidesQty\": {\"xs\": 1, \"md\": 2, \"lg\": 3},\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the carousel to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel` | Wrapper container | A wrapper for a collection of slides. Must contain the `overflow-hidden` class for correct functionality. |\n| `hs-carousel-body` | Slides container | Container for all slide elements. |\n| `hs-carousel-slide` | Each slide element | Identifies an individual slide. |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-carousel-prev` | Previous button | Previous slide navigation button. |\n| `hs-carousel-next` | Next button | Next slide navigation button. |\n| `hs-carousel-pagination` | Pagination container | Pagination container. If it doesn't contain nested elements, dots are generated automatically. If it contains elements with `hs-carousel-pagination-item` class, they will act as dots. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-carousel-active:*` | A modifier that allows you to set Tailwind classes when the active slide was shown. |\n| `hs-carousel-disabled:*` | A modifier that allows you to set Tailwind classes for arrow buttons when they are `disabled`. |\n| `hs-carousel:dragging:*` | A modifier that allows you to set Tailwind classes for `hs-carousel-body` when dragging. |\n\n## JavaScript API\n\nThe `HSCarousel` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a carousel instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `goToPrev()` | None | `void` | Navigates to the previous slide. |\n| `goToNext()` | None | `void` | Navigates to the next slide. |\n| `goTo(index)` | `index`: `number` | `void` | Navigates to the slide at the specified index. Index starts from 0. |\n| `recalculateWidth()` | None | `void` | Recalculates the width of the carousel. Useful after dynamically adding or removing slides, or when window is resized. |\n| `destroy()` | None | `void` | Destroys the carousel instance, removes all generated markup, classes, and event listeners. Use when removing carousel from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCarousel` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCarousel.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCarousel \\| { id: string \\| number, element: HSCarousel } \\| null` | Returns the carousel instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCarousel` instance. If `isInstance` is `false` or omitted, returns the `HSCarousel` instance directly. Returns `null` if carousel instance is not found. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new carousel instance\nconst carousel = new HSCarousel(document.querySelector('#hs-carousel'));\nconst goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n\n// Navigate to slide by index\ngoToSecondBtn.addEventListener('click', () => {\n  carousel.goTo(1); // Go to second slide (index 1)\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCarousel instance\n  const goToSecondBtn = document.querySelector('#hs-go-to-second-btn');\n  const prevBtn = document.querySelector('#hs-prev-btn');\n  const nextBtn = document.querySelector('#hs-next-btn');\n\n  // Navigate to specific slide\n  goToSecondBtn.addEventListener('click', () => {\n    element.goTo(1);\n  });\n\n  // Navigate to previous slide\n  prevBtn.addEventListener('click', () => {\n    element.goToPrev();\n  });\n\n  // Navigate to next slide\n  nextBtn.addEventListener('click', () => {\n    element.goToNext();\n  });\n\n  // Recalculate width after dynamic changes\n  function updateCarousel() {\n    element.recalculateWidth();\n  }\n\n  // Clean up when removing from DOM\n  function removeCarousel() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying carousel instance**\n```javascript\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-carousel').remove();\n  });\n}\n```\n\n## Events\n\nCarousel instances emit events that can be listened to for slide lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | After the active slide changes | `number` (zero-based index of the current slide) | Fires after navigating to the next or previous slide, or after `goTo()` is called. |\n\n### Event Usage Example\n\n```javascript\n// Get carousel instance\nconst instance = HSCarousel.getInstance('#hs-carousel', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for slide changes\n  element.on('update', (currentIndex) => {\n    console.log('Active slide:', currentIndex);\n    // Update external indicators, counters, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Responsive Carousel\n\nDisplay different number of slides at different breakpoints.\n\n```html\n<div data-hs-carousel='{\n  \"slidesQty\": {\"xs\": 1, \"sm\": 2, \"md\": 3, \"lg\": 4}\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 2: Autoplay Carousel\n\nAutomatically advance slides at regular intervals.\n\n```html\n<div data-hs-carousel='{\n  \"isAutoPlay\": true,\n  \"speed\": 5000,\n  \"isInfiniteLoop\": true\n}'>\n  <!-- Carousel content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl carousel from external buttons.\n\n```html\n<div id=\"hs-carousel-first\" data-hs-carousel>\n  <!-- Carousel content -->\n</div>\n\n<button id=\"hs-go-to-first\">Go to First</button>\n<button id=\"hs-go-to-last\">Go to Last</button>\n\n<script>\n  const instance = HSCarousel.getInstance('#hs-carousel-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-go-to-first').addEventListener('click', () => {\n      element.goTo(0);\n    });\n\n    document.querySelector('#hs-go-to-last').addEventListener('click', () => {\n      // Assuming 5 slides (indices 0-4)\n      element.goTo(4);\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/collapse/README.md (Tokens: ~2497 | Category: plugin-manifest) -->\n# Collapse\n\nToggle the visibility of content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/collapse) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/collapse.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Collapse component allows you to show and hide content with smooth transitions. It's a simple, lightweight solution for creating expandable/collapsible sections without the complexity of accordion groups.\n\n**Key Features:**\n- Simple show/hide functionality\n- Smooth CSS transitions\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Collapse plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/collapse\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/collapse */\n@source \"../node_modules/@preline/collapse/*.js\";\n@import \"./node_modules/@preline/collapse/variants.css\";\n@import \"./node_modules/@preline/collapse/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/collapse/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCollapse from \"@preline/collapse/non-auto.mjs\";\n\n  new HSCollapse(document.querySelector(\"#collapse\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/collapse` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/collapse\";\n```\n\n`@preline/collapse/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCollapse from \"@preline/collapse/non-auto\";\n\nHSCollapse.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#collapse\");\nif (el) new HSCollapse(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a collapse component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The content is hidden by default and expands when the button is clicked.\n\n```html\n<button type=\"button\" class=\"hs-collapse-toggle\" id=\"hs-unstyled-collapse\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-collapse-heading\" data-hs-collapse=\"#hs-unstyled-collapse-heading\" role=\"region\">\n  Collapse\n</button>\n<div id=\"hs-unstyled-collapse-heading\" class=\"hs-collapse hidden w-full overflow-hidden transition-[height] duration-300\" aria-labelledby=\"hs-unstyled-collapse\">\n  This is a collapse body. The content expands and collapses when the toggle button is clicked, with smooth height transitions.\n</div>\n```\n\n**Structure Requirements:**\n- `hs-collapse-toggle`: Required button element that triggers the collapse\n- `hs-collapse`: Required container for collapsible content\n- `data-hs-collapse`: Required attribute on the toggle button, must be a valid CSS selector pointing to the collapse content\n- Unique `id` attributes for the toggle button and collapse content\n- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role=\"region\"`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the collapse content initially\n- Content visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure collapse behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-collapse` | `hs-collapse-toggle` (button) | string (CSS selector) | `null` | Collapse container selector. Must be a valid CSS selector pointing to the `hs-collapse` element. |\n\n**Example:**\n```html\n<button data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the collapse to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-collapse` | Content container | Identifies the collapsible content area |\n| `hs-collapse-toggle` | Button element | The clickable element that expands/collapses content |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-collapse-open:*` | A modifier that allows you to set Tailwind classes when the collapse is open. |\n\n## JavaScript API\n\nThe `HSCollapse` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a collapse instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `show()` | None | `boolean` | Opens the collapsed content. Returns `false` if content is already open, `true` otherwise. |\n| `hide()` | None | `boolean` | Collapses the currently open content. Returns `false` if content is already closed, `true` otherwise. |\n| `destroy()` | None | `void` | Destroys the collapse instance, removes all generated markup, classes, and event listeners. Use when removing collapse from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCollapse` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCollapse.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSCollapse } \\| null` | Returns the collapse instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCollapse` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if collapse instance is not found. |\n| `HSCollapse.show(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Shows the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. |\n| `HSCollapse.hide(target)` | `target`: `HSCollapse \\| HTMLElement \\| string` (CSS selector) | `void` | Hides the collapse content identified by target. Accepts a collapse instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new collapse instance\nconst collapse = new HSCollapse(document.querySelector('#hs-collapse'));\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse programmatically\nshowBtn.addEventListener('click', () => {\n  collapse.show();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst showBtn = document.querySelector('#hs-show-btn');\n\n// Show collapse using static method (no instance needed)\nshowBtn.addEventListener('click', () => {\n  HSCollapse.show('#hs-collapse');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSCollapse instance\n  const showBtn = document.querySelector('#hs-show-btn');\n\n  // Use instance methods\n  showBtn.addEventListener('click', () => {\n    element.show();\n  });\n\n  // Clean up when removing from DOM\n  function removeCollapse() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying collapse instance**\n```javascript\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-collapse').remove();\n  });\n}\n```\n\n## Events\n\nCollapse instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After collapse content is opened | `HTMLElement` (collapse element) | Fires after the collapse content has been displayed. Use for post-open actions like loading content. |\n| `on:hide` | After collapse content is closed | `HTMLElement` (collapse element) | Fires after the collapse content has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get collapse instance\nconst instance = HSCollapse.getInstance('#hs-collapse', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (collapseElement) => {\n    console.log('Collapse opened:', collapseElement);\n    // Perform actions after collapse opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to hide event\n  element.on('hide', (collapseElement) => {\n    console.log('Collapse closed:', collapseElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Programmatic Control\n\nControl collapse from external buttons.\n\n```html\n<button class=\"hs-collapse-toggle\" data-hs-collapse=\"#hs-collapse-content\">Toggle</button>\n<div id=\"hs-collapse-content\" class=\"hs-collapse hidden\">Content</div>\n\n<button id=\"hs-show-collapse\">Show</button>\n<button id=\"hs-hide-collapse\">Hide</button>\n\n<script>\n  const instance = HSCollapse.getInstance('#hs-collapse-content', true);\n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-show-collapse').addEventListener('click', () => {\n      element.show();\n    });\n\n    document.querySelector('#hs-hide-collapse').addEventListener('click', () => {\n      element.hide();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/combobox/README.md (Tokens: ~3940 | Category: plugin-manifest) -->\n# ComboBox\n\nDynamic suggestions as users type in an input field.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/combobox) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/combobox.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe ComboBox component provides an autocomplete input field with dynamic suggestions. It supports both client-side filtering and API-based data fetching, customizable templates, and flexible data rendering options.\n\n**Key Features:**\n- Dynamic autocomplete suggestions\n- Client-side and API-based data fetching\n- Customizable item templates\n- Keyboard navigation support\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install ComboBox plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/combobox\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/combobox */\n@source \"../node_modules/@preline/combobox/*.js\";\n@import \"./node_modules/@preline/combobox/variants.css\";\n@import \"./node_modules/@preline/combobox/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/combobox/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCombobox from \"@preline/combobox/non-auto.mjs\";\n\n  new HSCombobox(document.querySelector(\"#combobox\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/combobox` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/combobox\";\n```\n\n`@preline/combobox/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCombobox from \"@preline/combobox/non-auto\";\n\nHSCombobox.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#combobox\");\nif (el) new HSCombobox(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a combobox component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The combobox shows suggestions as the user types.\n\n```html\n<div class=\"inline-flex relative\" data-hs-combo-box>\n  <div class=\"relative\">\n    <input type=\"text\" role=\"combobox\" aria-expanded=\"false\" value=\"\" data-hs-combo-box-input />\n  </div>\n  \n  <div class=\"absolute z-50\" style=\"display: none;\" data-hs-combo-box-output>\n    <div class=\"cursor-pointer\" tabindex=\"0\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"1\" data-hs-combo-box-value>1</span>\n    </div>\n    <div class=\"cursor-pointer\" tabindex=\"1\" data-hs-combo-box-output-item>\n      <span data-hs-combo-box-search-text=\"2\" data-hs-combo-box-value>2</span>\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-combo-box`: Required on the container element\n- `data-hs-combo-box-input`: Required on the input element\n- `data-hs-combo-box-output`: Required on the dropdown container\n- `data-hs-combo-box-output-item`: Required on each suggestion item\n- `data-hs-combo-box-search-text`: Required on the searchable text element within each item\n- Proper ARIA attributes (`role=\"combobox\"`, `aria-expanded`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the input initially\n- Add `style=\"display: none;\"` to the output container initially\n- Output visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-combo-box` attribute and various data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-combo-box` | Container | - | - | Activate a ComboBox by specifying on an element. Should be added to the container. |\n| `:gap` | Inside `data-hs-combo-box` | number | `5` | Determines the indent from the input field to the dropdown list in pixels. |\n| `:viewport` | Inside `data-hs-combo-box` | string \\| HTMLElement | `null` | Determines which element will be the parent when calculating the position of the dropdown list. By default it is calculated relative to the document. |\n| `:minSearchLength` | Inside `data-hs-combo-box` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:apiUrl` | Inside `data-hs-combo-box` | string \\| null | `null` | Specifies the API address for fetching suggestions. |\n| `:apiDataPart` | Inside `data-hs-combo-box` | string \\| null | `null` | Determines which part of the returned data will be taken as the main data array. If this parameter is empty, then the root is taken as a basis. |\n| `:apiQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request parameters, such as limit. |\n| `:apiSearchQuery` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines the name of the search parameter. |\n| `:apiHeaders` | Inside `data-hs-combo-box` | string \\| null | `null` | Defines request headers. |\n| `:apiSearchPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that specifies the endpoint path for the API request, excluding the base URL. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:apiSearchDefaultPath` | Inside `data-hs-combo-box` | string \\| null | `null` | A string that defines the default API endpoint path to be used when the input field is empty. Note: `apiSearchPath` and `apiSearchDefaultPath` are mutually exclusive with `apiSearchQuery`. |\n| `:outputItemTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for a single item in a dropdown list. |\n| `:outputEmptyTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for an empty placeholder. |\n| `:outputLoaderTemplate` | Inside `data-hs-combo-box` | string \\| null | Default template | Defines a template for the loader when loading data. |\n| `:preventAutoPosition` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it disables dropdown auto-positioning. |\n| `:preventClientFiltering` | Inside `data-hs-combo-box` | boolean | `true` (if `:apiSearchQuery` is not empty) | If `true`, it disables client-side filtering. |\n| `:keepOriginalOrder` | Inside `data-hs-combo-box` | boolean | `false` | If `true`, it keeps the original order of items. |\n| `:preserveSelectionOnEmpty` | Inside `data-hs-combo-box` | boolean | `true` | If `true`, it preserves the selected value when the input is empty. |\n| `data-hs-combo-box-input` | Input element | - | - | Determines which element (input) inside the initialized container will be responsible for entering data. |\n| `data-hs-combo-box-output` | Output container | - | - | Determines which element inside the initialized container will be responsible for data output. |\n| `data-hs-combo-box-toggle` | Toggle element | - | - | Defines an element inside the initialized container that will be responsible for hiding/showing the dropdown list. |\n| `data-hs-combo-box-close` | Close element | - | - | Defines an element inside the initialized container that will be responsible for hiding the dropdown list. |\n| `data-hs-combo-box-open` | Open element | - | - | Defines an element inside the initialized container that will be responsible for showing the dropdown list. |\n| `data-hs-combo-box-output-item` | Item element | - | - | Defines an element inside the initialized container that will be responsible for displaying each individual data element. |\n| `data-hs-combo-box-search-text` | Search text element | - | - | Defines the element within which text will be searched. |\n| `data-hs-combo-box-value` | Value element | - | - | Defines an element whose text will be set as a value when selected. |\n| `data-hs-combo-box-output-item-field` | Item field | string \\| string[] | - | Defines a data field that will be taken to fill the element with text. |\n| `data-hs-combo-box-output-item-attr` | Item attribute | - | - | Defines the attributes that will be filled with the corresponding data. For example, the src attribute of an image. |\n| `:valueFrom` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the field from which data will be taken. |\n| `:attr` | Inside `data-hs-combo-box-output-item-attr` | string | - | Defines the name of the attribute where the data will be supplied. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-combo-box-active:*` | A modifier that allows you to set Tailwind classes when ComboBox has been opened. |\n| `hs-combo-box-has-value:*` | A modifier that allows you to set Tailwind classes when ComboBox's input has value. |\n| `hs-combo-box-selected:*` | A modifier that allows you to set Tailwind classes when `data-hs-combo-box-output-item` has been selected. |\n| `hs-combo-box-tab-active:*` | A modifier that allows you to set Tailwind classes to the active group item (tab) when `groupingType` is `tabs`. |\n\n## JavaScript API\n\nThe `HSComboBox` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a combobox instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentData()` | None | `object \\| object[]` | Returns the data of the selected element as an object. This data must first be assigned as a value to the data parameter `data-hs-combo-box-item-stored-data` when standard rendering. When rendering via the API, the data is automatically substituted into the data attribute. |\n| `setCurrent()` | None | `void` | Sets the current selection based on the input value. |\n| `open(val)` | `val`: `string` (optional) | `boolean` | Opens the autosuggestion list. Optionally accepts a value to set in the input. Returns `true` if opened successfully, `false` otherwise. |\n| `close(val, data)` | `val`: `string \\| null` (optional)<br>`data`: `object \\| null` (optional) | `boolean` | Closes the autosuggestion list. Optionally accepts a value and data to set. Returns `true` if closed successfully, `false` otherwise. |\n| `recalculateDirection()` | None | `void` | Forces recalculation of autosuggestion list positions. Useful after window resize or layout changes. |\n| `destroy()` | None | `void` | Destroys the combobox instance, removes all generated markup, classes, and event listeners. Use when removing combobox from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSComboBox` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSComboBox.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSComboBox \\| { id: string \\| number, element: HSComboBox } \\| null` | Returns the combobox instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSComboBox` instance. If `isInstance` is `false` or omitted, returns the `HSComboBox` instance directly. Returns `null` if combobox instance is not found. |\n| `HSComboBox.autoInit()` | None | `void` | Reinitializes all ComboBoxes on the page. Useful after dynamically adding comboboxes to DOM. |\n| `HSComboBox.close(target)` | `target`: `HTMLElement \\| string` (CSS selector) | `void` | Closes the combobox identified by target. Accepts a DOM element or CSS selector string. |\n| `HSComboBox.closeCurrentlyOpened()` | None | `void` | Closes an already open ComboBox. Useful for closing any open combobox before opening a new one. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new combobox instance\nconst comboBox = new HSComboBox(document.querySelector('#hs-combo-box'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open combobox programmatically\nopenBtn.addEventListener('click', () => {\n  comboBox.open();\n});\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the combobox instance\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSComboBox instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Get selected data\n  const selectedData = element.getCurrentData();\n  console.log('Selected:', selectedData);\n\n  // Clean up when removing from DOM\n  function removeComboBox() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying combobox instance**\n```javascript\nconst instance = HSComboBox.getInstance('#hs-combo-box', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-combo-box').remove();\n  });\n}\n```\n\n## Events\n\nCombobox instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:select` | When an item is selected from the dropdown | `object` (the selected item data object) | Fires after the user clicks an option in the combobox dropdown. |\n\n### Event Usage Example\n\n```javascript\n// Get combobox instance\nconst instance = HSCombobox.getInstance('#hs-combobox', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for item selection\n  element.on('select', (data) => {\n    console.log('Selected item:', data);\n    // Use selected data to update other parts of the UI\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: API-based ComboBox\n\nFetch suggestions from an API endpoint.\n\n```html\n<div data-hs-combo-box='{\n  \"apiUrl\": \"https://api.example.com/search\",\n  \"apiSearchQuery\": \"q\",\n  \"minSearchLength\": 2\n}'>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl combobox from external buttons.\n\n```html\n<div id=\"hs-combo-box-first\" data-hs-combo-box>\n  <input data-hs-combo-box-input />\n  <div data-hs-combo-box-output></div>\n</div>\n\n<button id=\"hs-open-combo-box\">Open</button>\n<button id=\"hs-close-combo-box\">Close</button>\n\n<script>\n  const instance = HSComboBox.getInstance('#hs-combo-box-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-combo-box').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-combo-box').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/copy-markup/README.md (Tokens: ~2394 | Category: plugin-manifest) -->\n# Copy Markup\n\nCopy input, select or other markups.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/copy-markup) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/copy-markup.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field.\n\n**Key Features:**\n- One-click element duplication\n- Configurable copy limits\n- Automatic element management\n- Programmatic control via JavaScript API\n- Event system for copy/delete tracking\n- Support for any HTML markup\n\n## Installation\n\nTo get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/copy-markup\n```\n\n### CSS\n\n[`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS file into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/copy-markup */\n@import \"./node_modules/@preline/copy-markup/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/copy-markup/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSCopyMarkup from \"@preline/copy-markup/non-auto.mjs\";\n\n  new HSCopyMarkup(document.querySelector(\"#copy-markup\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/copy-markup` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/copy-markup\";\n```\n\n`@preline/copy-markup/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSCopyMarkup from \"@preline/copy-markup/non-auto\";\n\nHSCopyMarkup.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#copy-markup\");\nif (el) new HSCopyMarkup(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element.\n\n```html\n<div id=\"hs-wrapper-for-copy-first\">\n  <input id=\"hs-content-for-copy-first\" type=\"text\" class=\"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500\" placeholder=\"Enter Name\">\n</div>\n\n<button type=\"button\" data-hs-copy-markup='{\n    \"targetSelector\": \"#hs-content-for-copy-first\",\n    \"wrapperSelector\": \"#hs-wrapper-for-copy-first\",\n    \"limit\": 3\n  }' id=\"hs-copy-content-first\">\n  Add Name\n</button>\n```\n\n**Structure Requirements:**\n- `data-hs-copy-markup`: Required on the trigger button, contains configuration options as JSON\n- `targetSelector`: Required, must be a valid CSS selector pointing to the element to copy\n- `wrapperSelector`: Required, must be a valid CSS selector pointing to the container where copies will be added\n- Target element must exist in the DOM\n\n**Initial State:**\n- One instance of the target element exists\n- Copy button is enabled (unless limit is reached)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-copy-markup` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-copy-markup` | Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). |\n| `:targetSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. |\n| `:wrapperSelector` | Inside `data-hs-copy-markup` | string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. |\n| `:limit` | Inside `data-hs-copy-markup` | number | `null` | Specifies how many items you can copy until the button is disabled. `null` means no limit. |\n\n**Example:**\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-input-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field\n</button>\n```\n\n## JavaScript API\n\nThe `HSCopyMarkup` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a copy markup instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `delete(target)` | `target`: `HTMLElement` | `void` | Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. |\n| `destroy()` | None | `void` | Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSCopyMarkup` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSCopyMarkup.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSCopyMarkup \\| { id: string \\| number, element: HSCopyMarkup } \\| null` | Returns the copy markup instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSCopyMarkup` instance. If `isInstance` is `false` or omitted, returns the `HSCopyMarkup` instance directly. Returns `null` if copy markup instance is not found. |\n\n### Usage Examples\n\n**Example 1: Deleting copied elements**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const deleteBtn = document.querySelector('#hs-delete-btn');\n\n  deleteBtn.addEventListener('click', () => {\n    // Delete a specific copied element\n    const copiedElement = document.querySelector('#hs-copy-markup-item-first');\n\n    if (copiedElement) {\n      element.delete(copiedElement);\n    }\n  });\n}\n```\n\n**Example 2: Using instance methods (recommended pattern)**\n```javascript\n// Get the copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Items:', element.items);\n  console.log('Limit:', element.limit);\n\n  // Delete a copied element programmatically\n  function removeCopiedItem(itemElement) {\n    element.delete(itemElement);\n  }\n\n  // Clean up when removing from DOM\n  function removeCopyMarkup() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying copy markup instance**\n```javascript\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-copy-content').remove();\n  });\n}\n```\n\n## Events\n\nCopy markup instances emit events that can be listened to for copy/delete tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:copy` | When target element was copied | `HTMLElement` (copied element) | Fires when a new copy of the target element is created. Returns the newly created element. |\n| `on:delete` | When target element was deleted | `HTMLElement` (deleted element) | Fires when a copied element is deleted. Returns the deleted element. |\n\n### Event Usage Example\n\n```javascript\n// Get copy markup instance\nconst instance = HSCopyMarkup.getInstance('#hs-copy-content', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to copy event\n  element.on('copy', (copiedElement) => {\n    console.log('Element copied:', copiedElement);\n    // Perform actions after element is copied\n    // e.g., initialize other plugins on the new element, update counters\n  });\n\n  // Listen to delete event\n  element.on('delete', (deletedElement) => {\n    console.log('Element deleted:', deletedElement);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Limited Copies\n\nLimit the number of copies that can be created.\n\n```html\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field\",\n  \"wrapperSelector\": \"#hs-wrapper\",\n  \"limit\": 5\n}'>\n  Add Field (Max 5)\n</button>\n```\n\n### Pattern 2: Dynamic Form Fields\n\nCreate dynamic form fields with copy functionality.\n\n```html\n<div id=\"hs-fields-wrapper\">\n  <input id=\"hs-field-template\" type=\"text\" placeholder=\"Field name\">\n</div>\n\n<button data-hs-copy-markup='{\n  \"targetSelector\": \"#hs-field-template\",\n  \"wrapperSelector\": \"#hs-fields-wrapper\"\n}'>\n  Add Another Field\n</button>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/datatable/README.md (Tokens: ~3846 | Category: plugin-manifest) -->\n# Datatables\n\nDatatables solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datatable) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/datatables.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [CSS](#css)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Datatables component provides a powerful data table interface with sorting, searching, pagination, and row selection capabilities. It integrates with DataTables.net for advanced table functionality.\n\n**Key Features:**\n- Column sorting\n- Search functionality\n- Pagination\n- Row selection (single and multiple)\n- Customizable paging controls\n- Integration with DataTables.net\n- Programmatic control via JavaScript API\n- Event system for table interactions\n\n## Installation\n\nTo get started, install DataTable plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i jquery datatables.net-dt @preline/datatable\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/datatable */\n@source \"../node_modules/@preline/datatable/*.js\";\n@import \"./node_modules/@preline/datatable/variants.css\";\n@import \"./node_modules/@preline/datatable/theme.css\";\n\n.dt-layout-row:has(.dt-search),\n.dt-layout-row:has(.dt-length),\n.dt-layout-row:has(.dt-paging) {\n  display: none !important;\n}\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script src=\"./node_modules/@preline/datatable/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js\"></script>\n<script type=\"module\">\n  import HSDataTable from \"@preline/datatable/non-auto.mjs\";\n\n  new HSDataTable(document.querySelector(\"#datatable\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `jQuery` and `DataTable` are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/datatable` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport \"@preline/datatable\";\n```\n\n`@preline/datatable/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport jQuery from \"jquery\";\nwindow.jQuery = jQuery;\nwindow.$ = jQuery;\nimport DataTable from \"datatables.net\";\nwindow.DataTable = DataTable;\n\nimport HSDataTable from \"@preline/datatable/non-auto\";\n\nHSDataTable.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datatable\");\nif (el) new HSDataTable(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a datatable component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The table supports sorting, searching, and pagination.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\">\n          Name\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\">\n          Age\n          <svg class=\"hidden hs-datatable-ordering-desc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n          <svg class=\"hidden hs-datatable-ordering-asc:block\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>\n        </th>\n        <th scope=\"col\" class=\"--exclude-from-ordering\">\n          Action\n        </th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>John Brown</td>\n        <td>45</td>\n        <td>\n          <button type=\"button\">Delete</button>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n\n  <div style=\"display: none;\" data-hs-datatable-paging>\n    <button type=\"button\" data-hs-datatable-paging-prev>\n      <span aria-hidden=\"true\">«</span>\n      <span class=\"sr-only\">Previous</span>\n    </button>\n    <div data-hs-datatable-paging-pages></div>\n    <button type=\"button\" data-hs-datatable-paging-next>\n      <span class=\"sr-only\">Next</span>\n      <span aria-hidden=\"true\">»</span>\n    </button>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-datatable`: Required on the container element\n- Standard HTML `<table>` structure with `<thead>` and `<tbody>`\n- Optional paging controls with `data-hs-datatable-paging`\n- Optional search input with `data-hs-datatable-search`\n- Optional row selection checkboxes\n\n**Initial State:**\n- Table displays all data\n- Sorting is enabled on columns (unless excluded)\n- Pagination controls are hidden if not needed\n\n## CSS\n\nPlease add the following CSS to the head. This is necessary because Datatables.net does not allow to add custom markup with options disabled, such as `paging: false` or `searching: false`.\n\n```html\n<style>\n  .dt-layout-row:has(.dt-search),\n  .dt-layout-row:has(.dt-length),\n  .dt-layout-row:has(.dt-paging) {\n    display: none !important;\n  }\n</style>\n```\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-datatable` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datatable` | Container | - | - | Activate a Datatable by specifying on an element. Should be added to the container. |\n| `:pageBtnClasses` | Inside `data-hs-datatable` | string | `\"-\"` | Sets the specified classes to each paging element inside the `data-hs-datatable-paging-pages`. |\n| `:rowSelectingOptions.selectAllSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-all]\"` | A valid CSS selector (the element must be a checkbox or radio button), when the element is changed, all elements specified by `rowSelectingOptions.individualSelector` (or, if not specified, then `data-hs-datatable-row-selecting-individual`) on the current page will be changed. |\n| `:rowSelectingOptions.individualSelector` | Inside `data-hs-datatable` | string (CSS selector) | `\"[data-hs-datatable-row-selecting-individual]\"` | A valid CSS selector (the element must be a checkbox or radio button), affects the change of the element specified in `rowSelectingOptions.selectAllSelector`, if not all elements are selected on the current page, then it switches to `unchecked` state, if vice versa, then in `checked`. |\n\n**Example:**\n```html\n<div data-hs-datatable='{\n  \"pageBtnClasses\": \"px-3 py-1 border rounded\",\n  \"rowSelectingOptions\": {\n    \"selectAllSelector\": \"#hs-select-all\",\n    \"individualSelector\": \".hs-row-checkbox\"\n  }\n}'>\n  <!-- Table structure -->\n</div>\n```\n\n### Class Options\n\nCSS class modifiers use `--` prefix to control datatable behavior.\n\n| Class Modifier | Target Element | Purpose |\n| --- | --- | --- |\n| `--exclude-from-ordering` | Table header (`<th>`) | A modifier that allows you to disable the ordering functionality in a column. Should be added to the thead column. |\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for specific features to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-datatable-search` | Input element | Defines which element will be responsible for searching in the datatable (must be an `input`). |\n| `data-hs-datatable-page-entities` | Select element | Determines which element will be responsible for switching the number of elements on the page (must be a `select`, depends on the change event). |\n| `data-hs-datatable-paging` | Container element | Defines a paging container, that contains pagination, next and previous buttons. |\n| `data-hs-datatable-paging-prev` | Button element | Defines a previous button element. |\n| `data-hs-datatable-paging-next` | Button element | Defines a next button element. |\n| `data-hs-datatable-row-selecting-all` | Checkbox/Radio element | Defines a \"Select all\" checkbox/radio, if it's not specified in the `rowSelectingOptions.selectAllSelector` parameter. |\n| `data-hs-datatable-row-selecting-individual` | Checkbox/Radio element | Defines a checkbox/radio that will be responsible for selecting a specific row if it is not specified in the `rowSelectingOptions.individualSelector` parameter. |\n| `data-hs-datatable-info` | Container element | Defines an info container. |\n| `data-hs-datatable-info-from` | Element | Defines an element that showing `start` number. |\n| `data-hs-datatable-info-to` | Element | Defines an element that showing `end` number. |\n| `data-hs-datatable-info-length` | Element | Defines an element that showing `recordsTotal` number. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-datatable-ordering-asc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `asc`. |\n| `hs-datatable-ordering-desc:*` | A modifier that allows you to set Tailwind classes to elements inside `thead th` or `thead td` when ordering changes to `desc`. |\n\n## JavaScript API\n\nThe `HSDataTable` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a datatable instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the datatable instance, removes all generated markup, classes, and event listeners. Use when removing datatable from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDataTable` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDataTable.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDataTable } \\| null` | Returns the datatable instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDataTable` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datatable instance is not found. |\n\n### Usage Examples\n\n**Example 1: Accessing DataTables.net instance**\n```javascript\n// Get the datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying DataTables.net instance\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Use DataTables.net methods\n    dataTable.search('query').draw();\n  }\n}\n```\n\n**Example 2: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 3: Destroying datatable instance**\n```javascript\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-datatable').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Datatables.net](https://datatables.net/) plugin. Most events available in the plugin are available in our wrapper.\n\nDatatable instances emit events through the underlying DataTables.net instance. Access the DataTables.net instance via `element.dataTable` to listen to events.\n\n**Common DataTables.net Events:**\n- `draw` - When table is redrawn\n- `order` - When column ordering changes\n- `search` - When search is performed\n- `page` - When page changes\n- `select` - When row is selected\n- `deselect` - When row is deselected\n\n### Event Usage Example\n\n```javascript\n// Get datatable instance\nconst instance = HSDataTable.getInstance('#hs-datatable', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dataTable } = element;\n\n  if (dataTable) {\n    // Listen to draw event\n    dataTable.on('draw.dt', () => {\n      console.log('Table redrawn!');\n      // Perform actions after table redraws\n      // e.g., update UI, reinitialize plugins\n    });\n\n    // Listen to order event\n    dataTable.on('order.dt', () => {\n      console.log('Column ordering changed!');\n    });\n\n    // Listen to search event\n    dataTable.on('search.dt', () => {\n      console.log('Search performed!');\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: With Search\n\nAdd search functionality to the datatable.\n\n```html\n<input type=\"text\" data-hs-datatable-search placeholder=\"Search...\">\n\n<div data-hs-datatable>\n  <!-- Table structure -->\n</div>\n```\n\n### Pattern 2: With Row Selection\n\nEnable row selection with checkboxes.\n\n```html\n<div data-hs-datatable>\n  <table>\n    <thead>\n      <tr>\n        <th>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-all>\n        </th>\n        <!-- Other columns -->\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>\n          <input type=\"checkbox\" data-hs-datatable-row-selecting-individual>\n        </td>\n        <!-- Other cells -->\n      </tr>\n    </tbody>\n  </table>\n</div>\n```\n\n### Pattern 3: Exclude Column from Ordering\n\nDisable sorting on specific columns.\n\n```html\n<th scope=\"col\" class=\"--exclude-from-ordering\">\n  Actions\n</th>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/datepicker/README.md (Tokens: ~3792 | Category: plugin-manifest) -->\n# Advanced Datepicker\n\nCreate visually appealing and interactive datepickers in combination with the Datepicker JavaScript plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/datepicker) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-datepicker.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Datepicker component provides a comprehensive date selection interface with support for custom templates, multiple date formats, time selection, and integration with Vanilla Calendar Pro. It offers flexible configuration options for styling and behavior.\n\n**Key Features:**\n- Date and time selection\n- Custom select inputs for months, years, and time\n- Multiple date formats and locales\n- Custom templates for arrows and time display\n- Integration with Vanilla Calendar Pro\n- Programmatic control via JavaScript API\n- Utility classes support\n\n## Installation\n\nTo get started, install Datepicker plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select @preline/datepicker\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\nUse `styles.css` with `theme.css` for the default themed appearance:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles.css\";\n@import \"./node_modules/@preline/datepicker/theme.css\";\n```\n\nUse `styles-utility.css` instead when you plan to style the datepicker entirely with Tailwind utility classes. In this case `theme.css` is not needed:\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n\n/* @preline/datepicker */\n@source \"../node_modules/@preline/datepicker/*.js\";\n@import \"./node_modules/@preline/datepicker/variants.css\";\n@import \"./node_modules/@preline/datepicker/styles-utility.css\";\n```\n\n`@preline/select` is required for styling the embedded select dropdowns used in `custom-select` mode.\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/datepicker/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDatepicker from \"@preline/datepicker/non-auto.mjs\";\n\n  new HSDatepicker(document.querySelector(\"#datepicker\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `vanilla-calendar-pro` and the Advanced Select plugin are bundled inside `@preline/datepicker`, so no separate imports are needed.\n\n`@preline/datepicker` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/datepicker\";\n```\n\n`@preline/datepicker/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDatepicker from \"@preline/datepicker/non-auto\";\n\nHSDatepicker.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#datepicker\");\nif (el) new HSDatepicker(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced datepicker component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The datepicker appears when the input is focused.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\" placeholder=\"\" \n  readonly\n  data-hs-datepicker='{\n    \"type\": \"default\",\n    \"dateMax\": \"2050-12-31\",\n    \"styles\": {\n      \"week\": \"vc-week\",\n      \"weekDay\": \"vc-week__day\",\n      \"dates\": \"vc-dates\",\n      \"date\": \"vc-date\",\n      \"arrowPrev\": \"vc-arrow vc-arrow_prev\",\n      \"arrowNext\": \"vc-arrow vc-arrow_next\"\n    },\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n**Structure Requirements:**\n- `hs-datepicker`: Required class on the input element\n- `data-hs-datepicker`: Required attribute containing configuration options as JSON\n- `readonly` attribute on the input to prevent manual editing\n- `type=\"text\"` on the input element\n\n**Initial State:**\n- Input field is empty by default\n- Datepicker appears when input is focused or clicked\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most options available in the plugin are available in our wrapper. Check [Vanilla Calendar Pro Options](https://vanilla-calendar.pro/docs/reference/settings/) for more details.\n\nData options are specified in the `data-hs-datepicker` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-datepicker` | Input element | - | - | Activate a Datepicker by specifying on an element. Should be added to the element itself. |\n| `:mode` | Inside `data-hs-datepicker` | `\"default\"` \\| `\"custom-select\"` | `\"default\"` | This option allows you to specify the operational mode of the datepicker. `default` uses standard layout provided by Vanilla Calendar Pro. `custom-select` activates custom select inputs for months, years and time elements. |\n| `:applyUtilityClasses` | Inside `data-hs-datepicker` | boolean | `false` | Specifies whether to apply utility classes to the datepicker. If set to `true`, the datepicker receives utility classes based on the `primary` theme. |\n| `:inputModeOptions.dateSeparator` | Inside `data-hs-datepicker` | string | `\".\"` | Specifies the character used to separate the year, month, and day in the date format displayed in the input field. E.g., `/` produces a date format like `2024/01/22`. |\n| `:inputModeOptions.itemsSeparator` | Inside `data-hs-datepicker` | string | `\", \"` | Specifies the character or string used to separate multiple date entries in the input field when multiple dates are selected. E.g., `\\|` produces a format like `2024-01-22 \\| 2024-01-23`. |\n| `:styles.customSelect.shared` | Inside `data-hs-datepicker` | object | - | Defines shared styling configurations for Advanced Custom Select components. These styles are consistently applied to all instances of the Advanced Custom Select within the wrapper. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.months` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"months\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.years` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"years\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.hours` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"hours\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.minutes` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"minutes\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:styles.customSelect.meridiem` | Inside `data-hs-datepicker` | object | - | Defines additional styling configurations specifically for the \"meridiem\" instance of the Advanced Custom Select. See [Advanced Select Options](advanced-select.md#js-data-options) for available options. |\n| `:templates.time` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for rendering the time selection interface within the datepicker. |\n| `:templates.arrowPrev` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"previous\" navigation arrow in the datepicker. |\n| `:templates.arrowNext` | Inside `data-hs-datepicker` | string | - | Allows to define a custom HTML template for the \"next\" navigation arrow in the datepicker. |\n| `:dateFormat` | Inside `data-hs-datepicker` | string | - | Allows to define a custom date format. |\n| `:dateLocale` | Inside `data-hs-datepicker` | string | - | Allows to define the locale for the datepicker. |\n| `:replaceTodayWithText` | Inside `data-hs-datepicker` | boolean | - | Replace the current day name with the text \"Today\". |\n\n**Example:**\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\",\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"dateLocale\": \"en\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-vc-date-today:*` | A modifier that applies Tailwind classes to the current date (marked as \"today\") using the attribute `data-vc-date-today`. |\n| `hs-vc-date-selected:*` | A modifier that applies Tailwind classes to the selected dates using the attribute `data-vc-date-selected`. |\n| `hs-vc-date-weekend:*` | A modifier that applies Tailwind classes to weekend dates using the attribute `data-vc-date-weekend`. |\n| `hs-vc-week-day-off:*` | A modifier that applies Tailwind classes to weekdays marked as \"day off\" using the attribute `data-vc-week-day-off`. |\n| `hs-vc-date-month-prev:*` | A modifier that applies Tailwind classes to dates belonging to the previous month using the class `data-vc-date-month=\"prev\"`. |\n| `hs-vc-date-month-next:*` | A modifier that applies Tailwind classes to dates belonging to the next month using the attribute `data-vc-date-month=\"next\"`. |\n| `hs-vc-calendar-selected-middle:*` | A modifier that applies Tailwind classes to dates that are in the middle of a selected range using the attribute `data-vc-date-selected=\"middle\"`. |\n| `hs-vc-calendar-selected-first:*` | A modifier that applies Tailwind classes to the first date in a selected range using the attribute `data-vc-date-selected=\"first\"`. |\n| `hs-vc-calendar-selected-last:*` | A modifier that applies Tailwind classes to the last date in a selected range using the attribute `data-vc-date-selected=\"last\"`. |\n| `hs-vc-months-month-selected:*` | A modifier that applies Tailwind classes to the currently selected month using the attribute `data-vc-months-month-selected`. |\n| `hs-vc-years-year-selected:*` | A modifier that applies Tailwind classes to the currently selected year using the attribute `data-vc-years-year-selected`. |\n\n## JavaScript API\n\n> **Note:** This component requires the use of the [Vanilla Calendar Pro](https://vanilla-calendar.pro/) plugin. Most methods available in the plugin are available through our wrapper. Check [Vanilla Calendar Pro Methods](https://vanilla-calendar.pro/docs/reference/methods/) for more details.\n\nThe `HSDatepicker` object is available in the global `window` object after the plugin is loaded. Most methods from Vanilla Calendar Pro are available through the wrapper.\n\n### Instance Methods\n\nThese methods are called on a datepicker instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `{ selectedDates: DatesArr, selectedTime: string }` | Returns the current state of the datepicker including selected dates and time. |\n| `formatDate(date, format)` | `date`: `string \\| number \\| Date`<br>`format`: `string` (optional) | `string` | Formats a date according to the specified format or the datepicker's default format. |\n| `destroy()` | None | `void` | Destroys the datepicker instance, removes all generated markup, classes, and event listeners. Use when removing datepicker from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDatepicker` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDatepicker.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSDatepicker } \\| null` | Returns the datepicker instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDatepicker` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if datepicker instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('Selected dates:', state.selectedDates);\n  console.log('Selected time:', state.selectedTime);\n}\n```\n\n**Example 2: Formatting dates**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  // Format a date\n  const formatted = element.formatDate(new Date(), 'YYYY-MM-DD');\n\n  console.log('Formatted date:', formatted);\n}\n```\n\n**Example 3: Destroying datepicker instance**\n```javascript\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('.hs-datepicker').remove();\n  });\n}\n```\n\n## Events\n\nDatepicker instances emit events that can be listened to for date selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected date or time changes | `{ selectedDates: string[], selectedTime: string }` (current selection state) | Fires after the user picks a date or changes the time. The payload matches the shape returned by `getCurrentState()`. |\n\n### Event Usage Example\n\n```javascript\n// Get datepicker instance\nconst instance = HSDatepicker.getInstance('.hs-datepicker', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for date or time changes\n  element.on('change', ({ selectedDates, selectedTime }) => {\n    console.log('Selected dates:', selectedDates);\n    console.log('Selected time:', selectedTime);\n    // Update dependent fields, validate form, etc.\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Select Mode\n\nUse custom select inputs for months, years, and time.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"mode\": \"custom-select\"\n  }'>\n```\n\n### Pattern 2: Custom Date Format\n\nSet a custom date format and separator.\n\n```html\n<input\n  class=\"hs-datepicker\"\n  type=\"text\"\n  readonly\n  data-hs-datepicker='{\n    \"dateFormat\": \"YYYY-MM-DD\",\n    \"inputModeOptions\": {\n      \"dateSeparator\": \"/\"\n    }\n  }'>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/dropdown/README.md (Tokens: ~3656 | Category: plugin-manifest) -->\n# Dropdown\n\nA dropdown menu displays a list of actions and more with JavaScript dropdown plugin.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/dropdown) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/dropdown.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Accessibility notes](#accessibility-notes)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.\n\n**Key Features:**\n- Multiple placement options (top, bottom, left, right, auto)\n- Customizable trigger events (click, hover, contextmenu)\n- Floating UI integration for smart positioning\n- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/dropdown\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/dropdown */\n@source \"../node_modules/@preline/dropdown/*.js\";\n@import \"./node_modules/@preline/dropdown/variants.css\";\n@import \"./node_modules/@preline/dropdown/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/dropdown/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSDropdown from \"@preline/dropdown/non-auto.mjs\";\n\n  new HSDropdown(document.querySelector(\"#dropdown\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/dropdown` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/dropdown\";\n```\n\n`@preline/dropdown/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSDropdown from \"@preline/dropdown/non-auto\";\n\nHSDropdown.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#dropdown\");\nif (el) new HSDropdown(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.\n\n```html\n<div class=\"hs-dropdown relative inline-flex\">\n  <button id=\"hs-dropdown-unstyled\" type=\"button\" class=\"hs-dropdown-toggle inline-flex justify-center items-center gap-x-2\" aria-expanded=\"false\" aria-label=\"Menu\">\n    Actions\n  </button>\n\n  <div class=\"hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white\" role=\"menu\" aria-labelledby=\"hs-dropdown-unstyled\">\n    <a class=\"block\" href=\"#\">Newsletter</a>\n    <a class=\"block\" href=\"#\">Purchases</a>\n    <a class=\"block\" href=\"#\">Downloads</a>\n    <a class=\"block\" href=\"#\">Team Account</a>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `hs-dropdown`: Required container wrapper\n- `hs-dropdown-toggle`: Required button element that triggers the dropdown\n- `hs-dropdown-menu`: Required container for dropdown menu items\n- Proper ARIA attributes (`aria-expanded`, `aria-label`, `role=\"menu\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the toggle button initially\n- Add `hidden` class to the dropdown menu initially\n- Menu visibility is controlled by `hs-dropdown-open:*` modifier classes\n\n## Accessibility notes\n\n### Keyboard interactions\n\n| Command | Description |\n| --- | --- |\n| Enter | Activates the selected menu item. |\n| ArrowUp / ArrowDown | Focuses previous/next non-disabled item. |\n| Home / End | Focuses first/last non-disabled item. |\n| Esc | Closes any open dropdown menus. |\n| A-Z / a-z | Focuses first item that matches keyboard input. |\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure dropdown behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-dropdown-transition` | `hs-dropdown` (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control dropdown behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--placement:*]` | `hs-dropdown` (container) | `\"auto\"` \\| `\"auto-start\"` \\| `\"auto-end\"` \\| `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-start\"` \\| `\"right-end\"` \\| `\"left\"` \\| `\"left-start\"` \\| `\"left-end\"` | `\"bottom\"` | Specifies the position of the menu when opened relative to the toggle button. |\n| `[--scope:*]` | `hs-dropdown` (container) | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `[--auto-close:*]` | `hs-dropdown` (container) | `\"inside\"` \\| `\"outside\"` \\| `\"false\"` \\| `\"true\"` | `\"true\"` | Specifies the zone, when clicked, which will close the menu. `inside` closes only when clicking inside, `outside` closes only when clicking outside, `true` closes on any click, `false` prevents auto-close. |\n| `[--has-autofocus:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening a dropdown. Set to `\"false\"` to disable autofocus. |\n| `[--strategy:*]` | `hs-dropdown` (container) | `\"fixed\"` \\| `\"absolute\"` | `\"fixed\"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. |\n| `[--adaptive:*]` | `hs-dropdown` (container) | `\"none\"` \\| `\"adaptive\"` | `\"adaptive\"` | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |\n| `[--gpu-acceleration:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"false\"` | Disable/enable position calculation using the transform property. Set to `\"true\"` to enable GPU acceleration. |\n| `[--offset:*]` | `hs-dropdown` (container) | number | `10` | Sets the dropdown's bottom or top offset in pixels. |\n| `[--flip:*]` | `hs-dropdown` (container) | `\"true\"` \\| `\"false\"` | `\"true\"` | Flips the menu's placement when it starts to overlap its reference element. Set to `\"false\"` to disable flipping. |\n| `[--trigger:*]` | `hs-dropdown` (container) | `\"hover\"` \\| `\"click\"` \\| `\"contextmenu\"` | `\"click\"` | Event to trigger a dropdown. `hover` shows on mouse enter, `click` shows on click, `contextmenu` shows on right-click. |\n\n**Example:**\n```html\n<div class=\"hs-dropdown --trigger:hover --placement:top --auto-close:false\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the dropdown to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown` | Container wrapper | Groups the toggle and menu elements together |\n| `hs-dropdown-toggle` | Button element | The clickable element that activates the dropdown |\n| `hs-dropdown-menu` | Menu container | The dropdown menu that appears on trigger |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-dropdown-toggle-wrapper` | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a \"+\" button inside an existing Dropdown toggle button that opens a modal. |\n| `hs-dropdown-close` | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-dropdown-open:*` | The modifier that allows you to set Tailwind classes when the dropdown menu is open. |\n\n## JavaScript API\n\nThe `HSDropdown` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a dropdown instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(target, openedViaKeyboard)` | `target`: `VirtualElement \\| HTMLElement` (optional)<br>`openedViaKeyboard`: `boolean` (optional) | `boolean` | Forces the dropdown menu to open programmatically. Returns `true` if opened successfully, `false` otherwise. |\n| `close(isAnimated)` | `isAnimated`: `boolean` (optional) | `boolean` | Forces the dropdown menu to close programmatically. `isAnimated` indicates whether the dropdown menu is closed with animation. Returns `true` if closed successfully, `false` otherwise. |\n| `forceClearState()` | None | `void` | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |\n| `destroy()` | None | `void` | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSDropdown` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSDropdown.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSDropdown \\| { id: string \\| number, element: HSDropdown } \\| null` | Returns the dropdown instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDropdown` instance. If `isInstance` is `false` or omitted, returns the `HSDropdown` instance directly. Returns `null` if dropdown instance is not found. |\n| `HSDropdown.open(target, openedViaKeyboard)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector)<br>`openedViaKeyboard`: `boolean` (optional) | `void` | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSDropdown.close(target)` | `target`: `HSDropdown \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new dropdown instance\nconst dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown programmatically\nopenBtn.addEventListener('click', () => {\n  dropdown.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open dropdown using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSDropdown.open('#hs-dropdown');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSDropdown instance\n  const openBtn = document.querySelector('#hs-open-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  // Clean up when removing from DOM\n  function removeDropdown() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying dropdown instance**\n```javascript\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-dropdown').remove();\n  });\n}\n```\n\n## Events\n\nDropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After dropdown menu is opened | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |\n| `on:close` | After dropdown menu is closed | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get dropdown instance\nconst instance = HSDropdown.getInstance('#hs-dropdown', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (dropdownInstance) => {\n    console.log('Dropdown opened:', dropdownInstance);\n    // Perform actions after dropdown opens\n    // e.g., load content, update UI, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (dropdownInstance) => {\n    console.log('Dropdown closed:', dropdownInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Hover Trigger\n\nShow dropdown on hover instead of click.\n\n```html\n<div class=\"hs-dropdown --trigger:hover\">\n  <button class=\"hs-dropdown-toggle\">Hover me</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n```\n\n### Pattern 2: Programmatic Control\n\nControl dropdown from external buttons.\n\n```html\n<div class=\"hs-dropdown\" id=\"hs-dropdown-first\">\n  <button class=\"hs-dropdown-toggle\">Actions</button>\n  <div class=\"hs-dropdown-menu\">Menu content</div>\n</div>\n\n<button id=\"hs-open-dropdown\">Open Dropdown</button>\n<button id=\"hs-close-dropdown\">Close Dropdown</button>\n\n<script>\n  const instance = HSDropdown.getInstance('#hs-dropdown-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-dropdown').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-dropdown').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/file-upload/README.md (Tokens: ~3282 | Category: plugin-manifest) -->\n# File Upload\n\nTailwind CSS File Upload plugin using Dropzone library to enable drag-and-drop functionality, previews, and progress bars.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/file-upload) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/file-upload.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe File Upload component provides a drag-and-drop file upload interface with preview functionality, progress tracking, and integration with Dropzone.js. It supports multiple file uploads, file type validation, and customizable preview templates.\n\n**Key Features:**\n- Drag-and-drop file upload\n- File previews with thumbnails\n- Upload progress tracking\n- Multiple file support\n- File type and extension validation\n- Integration with Dropzone.js\n- Programmatic control via JavaScript API\n- Event system for upload tracking\n\n## Installation\n\nTo get started, install File Upload plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i lodash dropzone @preline/file-upload\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/file-upload */\n@source \"../node_modules/@preline/file-upload/*.js\";\n@import \"./node_modules/@preline/file-upload/variants.css\";\n@import \"./node_modules/@preline/file-upload/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script src=\"./node_modules/@preline/file-upload/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/lodash/lodash.min.js\"></script>\n<script src=\"./node_modules/dropzone/dist/dropzone-min.js\"></script>\n<script type=\"module\">\n  import HSFileUpload from \"@preline/file-upload/non-auto.mjs\";\n\n  new HSFileUpload(document.querySelector(\"#file-upload\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `Dropzone` and `_` (Lodash) are referenced as globals, so expose them on `window` before importing the plugin.\n\n`@preline/file-upload` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport \"@preline/file-upload\";\n```\n\n`@preline/file-upload/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport _ from \"lodash\";\nwindow._ = _;\nimport Dropzone from \"dropzone\";\nwindow.Dropzone = Dropzone;\n\nimport HSFileUpload from \"@preline/file-upload/non-auto\";\n\nHSFileUpload.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#file-upload\");\nif (el) new HSFileUpload(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a file upload component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component supports drag-and-drop and file previews.\n\n```html\n<div id=\"hs-file-upload-first\" data-hs-file-upload='{\n  \"url\": \"/upload\"\n}'>\n  <template data-hs-file-upload-preview>\n    <button type=\"button\" data-hs-file-upload-remove>\n      Delete!\n    </button>\n    <span data-hs-file-upload-file-icon>\n      <img class=\"hidden\" data-dz-thumbnail />\n    </span>\n    <div>\n      <span data-hs-file-upload-file-name></span>.<span data-hs-file-upload-file-ext></span>\n    </div>\n    <div data-hs-file-upload-file-size></div>\n    <div role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" data-hs-file-upload-progress-bar>\n      <div style=\"width: 0\" data-hs-file-upload-progress-bar-pane></div>\n    </div>\n    <span data-hs-file-upload-progress-bar-value>0</span>%\n  </template>\n\n  <div class=\"cursor-pointer\" data-hs-file-upload-trigger>\n    Drop your file here or browse\n  </div>\n\n  <div data-hs-file-upload-previews></div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-file-upload`: Required on the container element, contains configuration options\n- `data-hs-file-upload-preview`: Required template element for file previews (should be a `<template>` element)\n- `data-hs-file-upload-trigger`: Required clickable element for file selection\n- `data-hs-file-upload-previews`: Required container for file preview items\n- Various data attributes for preview elements (file name, extension, size, progress, etc.)\n\n**Initial State:**\n- Upload trigger is visible\n- Previews container is empty\n- Template is hidden (it's a template element)\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most options available in the plugin are available in our wrapper.\n\nData options are specified in the `data-hs-file-upload` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-file-upload` | Container | - | - | Activate a File Upload by specifying on an element. Should be added to the container. |\n| `:url` | Inside `data-hs-file-upload` | string | - | The URL where files will be uploaded. Required for file upload functionality. |\n| `:extensions` | Inside `data-hs-file-upload` | object | Includes markup. See the code. | A list of extensions and associated icons that will be added inside the container with the `data-hs-file-upload-file-icon` selector if they match the extension name. |\n| `:autoHideTrigger` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then `data-hs-file-upload-trigger` will disappear if at least one item is added to the upload and will appear if more than one item is not in the upload. |\n| `:singleton` | Inside `data-hs-file-upload` | boolean | `false` | If the attribute value is `true`, then the plugin will remove any previously uploaded file. It needs for emulate simple input file behavior (only one file at a time). |\n\n**Example:**\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/api/upload\",\n  \"autoHideTrigger\": true,\n  \"singleton\": false\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the file upload to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-trigger` | Clickable element | Determines which element inside the initialized element will act as a clickable element for file selection. |\n| `data-hs-file-upload-previews` | Container element | Determines which element inside the initialized element will act as a wrapper for uploaded items. |\n| `data-hs-file-upload-preview` | Template element | Determines which element inside the initialized element will act as a template for the uploading item. Should be a `template` HTML element. |\n\n### Optional CSS Classes / Data Attributes\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-file-upload-clear` | Button element | Determines which element inside the initialized element will act as a button that deletes all files. |\n| `data-hs-file-upload-file-icon` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file icon defined in the `extensions` property. |\n| `data-hs-file-upload-file-name` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file name. |\n| `data-hs-file-upload-file-ext` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file extension. |\n| `data-hs-file-upload-file-size` | Element inside preview | Determines which element inside the uploading item will act as a wrapper for the file size. |\n| `data-hs-file-upload-remove` | Button element inside preview | Determines which element inside the uploading item will act as a remove button. |\n| `data-hs-file-upload-reload` | Button element inside preview | Determines which element inside the uploading item will act as a re-upload button. |\n| `data-hs-file-upload-progress-bar` | Element inside preview | Determines which element inside the uploading item will act as a progressbar. |\n| `data-hs-file-upload-progress-bar-pane` | Element inside preview | Determines which element inside the uploading item will act as a progressbar pane, which width will dynamically increase during the uploading process. |\n| `data-hs-file-upload-progress-bar-value` | Element inside preview | Determines which element inside the uploading item will act as a current progress value, which will dynamically increase during the uploading process. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-file-upload-complete:*` | A modifier that allows you to set Tailwind classes to items that successfully uploaded. |\n\n## JavaScript API\n\nThe `HSFileUpload` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a file upload instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the file upload instance, removes all generated markup, classes, and event listeners. Use when removing file upload from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSFileUpload` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSFileUpload.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSFileUpload } \\| null` | Returns the file upload instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSFileUpload` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if file upload instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Accessing Dropzone instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying Dropzone instance\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Use Dropzone methods\n    dropzone.removeAllFiles();\n  }\n}\n```\n\n**Example 3: Destroying file upload instance**\n```javascript\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-file-upload').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Please note that this component requires the use of the [Dropzone.js](https://www.dropzone.dev/) plugin. Most events available in the plugin are available in our wrapper.\n\nFile upload instances emit events through the underlying Dropzone instance. Access the Dropzone instance via `element.dropzone` to listen to events.\n\n**Common Dropzone Events:**\n- `addedfile` - When a file is added\n- `removedfile` - When a file is removed\n- `uploadprogress` - During file upload\n- `success` - When file upload succeeds\n- `error` - When file upload fails\n- `complete` - When upload completes (success or error)\n\n### Event Usage Example\n\n```javascript\n// Get file upload instance\nconst instance = HSFileUpload.getInstance('#hs-file-upload', true);\n\nif (instance) {\n  const { element } = instance;\n  const { dropzone } = element;\n\n  if (dropzone) {\n    // Listen to complete event\n    dropzone.on('complete', (file) => {\n      console.log('File uploaded:', file);\n      // Perform actions after file upload completes\n      // e.g., update UI, show success message\n    });\n\n    // Listen to error event\n    dropzone.on('error', (file, errorMessage) => {\n      console.log('Upload error:', errorMessage);\n      // Handle upload errors\n    });\n\n    // Listen to success event\n    dropzone.on('success', (file, response) => {\n      console.log('Upload successful:', response);\n      // Handle successful upload\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Single File Upload\n\nAllow only one file at a time.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"singleton\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n### Pattern 2: Auto-hide Trigger\n\nHide trigger after files are added.\n\n```html\n<div data-hs-file-upload='{\n  \"url\": \"/upload\",\n  \"autoHideTrigger\": true\n}'>\n  <!-- File upload structure -->\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/input-number/README.md (Tokens: ~2567 | Category: plugin-manifest) -->\n# Input Number\n\nInput Number - input idea for Shop systems or so.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/input-number) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/input-number.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Input Number component provides a numeric input field with increment and decrement buttons. It's ideal for quantity selectors, cart systems, or any interface requiring numeric input with step controls.\n\n**Key Features:**\n- Numeric input with step controls\n- Increment/decrement buttons\n- Min/max value constraints\n- Custom step values\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Input Number plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/input-number\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/input-number */\n@source \"../node_modules/@preline/input-number/*.js\";\n@import \"./node_modules/@preline/input-number/variants.css\";\n@import \"./node_modules/@preline/input-number/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/input-number/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSInputNumber from \"@preline/input-number/non-auto.mjs\";\n\n  new HSInputNumber(document.querySelector(\"#input-number\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/input-number` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/input-number\";\n```\n\n`@preline/input-number/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSInputNumber from \"@preline/input-number/non-auto\";\n\nHSInputNumber.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#input-number\");\nif (el) new HSInputNumber(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an input number component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component includes an input field and increment/decrement buttons.\n\n```html\n<div data-hs-input-number>\n  <input type=\"text\" aria-roledescription=\"Number field\" value=\"1\" data-hs-input-number-input>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Decrease\" data-hs-input-number-decrement>\n    Minus\n  </button>\n  <button type=\"button\" class=\"\" tabindex=\"-1\" aria-label=\"Increase\" data-hs-input-number-increment>\n    Add\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-input-number`: Required on the container element\n- `data-hs-input-number-input`: Required on the input element\n- `data-hs-input-number-increment`: Required on the increment button\n- `data-hs-input-number-decrement`: Required on the decrement button\n- Proper ARIA attributes (`aria-roledescription`, `aria-label`)\n\n**Initial State:**\n- Input field contains the initial value (e.g., `value=\"1\"`)\n- Buttons are enabled/disabled based on min/max constraints\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-input-number` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-input-number` | Container | - | - | Activate an Input Number by specifying on an element. Should be added to the container. |\n| `:min` | Inside `data-hs-input-number` | number \\| `\"-Infinity\"` | `0` | Defines the minimum possible value. `-Infinity` allows you to enter negative values without restrictions. |\n| `:max` | Inside `data-hs-input-number` | number \\| null | `null` | Defines the maximum possible value. `null` means no maximum limit. |\n| `:step` | Inside `data-hs-input-number` | number | `1` | Determines the step by which the value will increase or decrease when buttons are clicked. |\n| `:forceBlankValue` | Inside `data-hs-input-number` | boolean | `false` | Whether the input value should be blank when the value isn't set. When `true`, empty input shows blank instead of default value. |\n\n**Example:**\n```html\n<div data-hs-input-number='{\n  \"min\": 0,\n  \"max\": 100,\n  \"step\": 5,\n  \"forceBlankValue\": false\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Required CSS Classes / Data Attributes\n\nThese data attributes define the structure and must be present for the input number to function.\n\n| Data Attribute | Required On | Purpose |\n| --- | --- | --- |\n| `data-hs-input-number-input` | Input element | Identifies the numeric input field |\n| `data-hs-input-number-increment` | Button element | Identifies the increment button |\n| `data-hs-input-number-decrement` | Button element | Identifies the decrement button |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-input-number-disabled:*` | A modifier that allows you to set Tailwind classes when input's value is set to zero (or minimum value). |\n\n## JavaScript API\n\nThe `HSInputNumber` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an input number instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the input number instance, removes all generated markup, classes, and event listeners. Use when removing input number from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSInputNumber` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSInputNumber.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSInputNumber \\| { id: number, element: HSInputNumber } \\| null` | Returns the input number instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSInputNumber` instance. If `isInstance` is `false` or omitted, returns the `HSInputNumber` instance directly. Returns `null` if input number instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.inputValue);\n  console.log('Min value:', element.minInputValue);\n  console.log('Max value:', element.maxInputValue);\n  console.log('Step:', element.step);\n\n  // Clean up when removing from DOM\n  function removeInputNumber() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying input number instance**\n```javascript\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-input-number').remove();\n  });\n}\n```\n\n## Events\n\nInput number instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When input value was changed | `{ inputValue: number }` | Fires when the input value changes (via buttons or direct input). Returns an object with the `inputValue` containing the current numeric value. |\n\n### Event Usage Example\n\n```javascript\n// Get input number instance\nconst instance = HSInputNumber.getInstance('#hs-input-number', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to change event\n  element.on('change', ({ inputValue }) => {\n    console.log('Input value changed:', inputValue);\n    // Perform actions after value changes\n    // e.g., update cart, calculate totals, validate\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Quantity Selector\n\nCreate a quantity selector for shopping carts.\n\n```html\n<div data-hs-input-number='{\n  \"min\": 1,\n  \"max\": 99,\n  \"step\": 1\n}'>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n```\n\n### Pattern 2: Negative Values Allowed\n\nAllow negative values by setting min to -Infinity.\n\n```html\n<div data-hs-input-number='{\n  \"min\": \"-Infinity\",\n  \"max\": null,\n  \"step\": 1\n}'>\n  <!-- Input and buttons -->\n</div>\n```\n\n### Pattern 3: Handling Value Changes\n\nTrack value changes with event listener.\n\n```html\n<div id=\"hs-input-number-first\" data-hs-input-number>\n  <input type=\"text\" value=\"1\" data-hs-input-number-input>\n  <button data-hs-input-number-decrement>-</button>\n  <button data-hs-input-number-increment>+</button>\n</div>\n\n<script>\n  const instance = HSInputNumber.getInstance('#hs-input-number-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('change', ({ inputValue }) => {\n      console.log('Quantity:', inputValue);\n      // Update cart or perform other actions\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/layout-splitter/README.md (Tokens: ~3432 | Category: plugin-manifest) -->\n# Layout Splitter\n\nCustomize your layout effortlessly by adjusting block sizes to your preference with Tailwind CSS Layout Splitters.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/layout-splitter) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/layout-splitter.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Layout Splitter component provides resizable layout panels with drag handles. It allows users to adjust the size of layout sections horizontally or vertically, creating flexible and customizable interfaces.\n\n**Key Features:**\n- Horizontal and vertical resizing\n- Drag handles for intuitive resizing\n- Minimum size constraints\n- Pre-limit size detection\n- Responsive breakpoint support\n- Programmatic control via JavaScript API\n- Event system for drag tracking\n\n## Installation\n\nTo get started, install Layout Splitter plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/layout-splitter\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/layout-splitter */\n@source \"../node_modules/@preline/layout-splitter/*.js\";\n@import \"./node_modules/@preline/layout-splitter/variants.css\";\n@import \"./node_modules/@preline/layout-splitter/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/layout-splitter/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSLayoutSplitter from \"@preline/layout-splitter/non-auto.mjs\";\n\n  new HSLayoutSplitter(document.querySelector(\"#layout-splitter\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/layout-splitter` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/layout-splitter\";\n```\n\n`@preline/layout-splitter/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSLayoutSplitter from \"@preline/layout-splitter/non-auto\";\n\nHSLayoutSplitter.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#layout-splitter\");\nif (el) new HSLayoutSplitter(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a layout splitter component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The layout allows resizing blocks by dragging the splitter handle.\n\n```html\n<div data-hs-layout-splitter='{\n  \"horizontalSplitterTemplate\": \"<div><span class=\\\"absolute top-1/2 start-1/2 -translate-x-2/4 -translate-y-2/4 block w-[11px] h-5 bg-gray-400\\\"></span></div>\",\n  \"horizontalSplitterClasses\": \"relative flex border-s border-gray-200\"\n}'>\n  <div class=\"flex h-50\" data-hs-layout-splitter-horizontal-group>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"48.2\">Horizontal Block 1</div>\n    <div class=\"h-full\" data-hs-layout-splitter-item=\"30.0\">Horizontal Block 2</div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-layout-splitter`: Required on the container element, contains configuration options as JSON\n- `data-hs-layout-splitter-horizontal-group`: Required for horizontal splitting\n- `data-hs-layout-splitter-vertical-group`: Required for vertical splitting\n- `data-hs-layout-splitter-item`: Required on resizable blocks, accepts float number or object for initial size\n\n**Initial State:**\n- Blocks are sized according to `data-hs-layout-splitter-item` values\n- Splitter handles are visible and draggable\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-layout-splitter` attribute and on splitter items.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-layout-splitter` | Container | - | - | Activate a Layout Splitter by specifying on an element. Should be added to the container. |\n| `:horizontalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the horizontal splitter template. HTML markup for the drag handle. |\n| `:horizontalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the horizontal splitter CSS classes. CSS classes must be separated by a space. |\n| `:verticalSplitterTemplate` | Inside `data-hs-layout-splitter` | string (HTML markup) | - | Defines the vertical splitter template. HTML markup for the drag handle. |\n| `:verticalSplitterClasses` | Inside `data-hs-layout-splitter` | string | - | Defines the vertical splitter CSS classes. CSS classes must be separated by a space. |\n| `data-hs-layout-splitter-horizontal-group` | Container element | - | - | Defines a group of elements with horizontal resizable blocks. |\n| `data-hs-layout-splitter-vertical-group` | Container element | - | - | Defines a group of elements with vertical resizable blocks. |\n| `data-hs-layout-splitter-item` | Resizable block | float \\| object | - | Defines a block that can be resized depending on what group it is in. Also accepts a float number as a value to specify the initial width or height of the element. Can be an object with `dynamicSize`, `minSize`, `preLimitSize` properties. |\n| `:dynamicSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the initialized block size. |\n| `:minSize` | Inside `data-hs-layout-splitter-item` | float | - | Defines the size at which further narrowing of the block will be prevented. |\n| `:preLimitSize` | Inside `data-hs-layout-splitter-item` | float | - | Determines the size at which the `onPrevPreLimit` event will be called. For example, on this event you can add specific classes to the block to the right or left of the divider. |\n\n**Example:**\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0,\n  \"preLimitSize\": 20.0\n}'>\n  Resizable Block\n</div>\n```\n\n## JavaScript API\n\nThe `HSLayoutSplitter` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a layout splitter instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getSplitterItemSingleParam(item, name)` | `item`: `HTMLElement` (splitter item element)<br>`name`: `string` (parameter name) | `any` | Retrieves the value of a specific parameter (name) from the data attribute of a splitter item (item). The parameter is extracted from the parsed JSON object stored in the `data-hs-layout-splitter-item` attribute. |\n| `getData(el)` | `el`: `HTMLElement` (splitter item element) | `object` | Retrieves detailed information about a splitter item, including its current size, minimum size, and pre-limit size, both in pixels and flex values. Returns a structured object. |\n| `setSplitterItemSize(el, size)` | `el`: `HTMLElement` (splitter item element)<br>`size`: `number` (percentage) | `void` | Updates the size of a specified splitter item (`el`) to the given value (size). The new size is converted into a flex value and applied to the element. The method also updates the corresponding data attribute (`data-hs-layout-splitter-item`). |\n| `updateFlexValues(data)` | `data`: `Array<{ id: string, breakpoints: Record<number, number> }>` | `void` | Updates the flex values of splitter items based on the provided configuration. Each configuration object specifies an element's ID and its size values for different screen widths (breakpoints). The method dynamically adjusts the flex values of the specified elements to match the current screen width. |\n| `destroy()` | None | `void` | Cleans up and removes all event listeners, styles, and data attributes associated with the layout splitter. This method restores the affected elements to their original state and releases resources. |\n\n### Static Methods\n\nThese methods are called directly on the `HSLayoutSplitter` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSLayoutSplitter.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSLayoutSplitter } \\| null` | Returns the layout splitter instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSLayoutSplitter` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if layout splitter instance is not found. |\n| `HSLayoutSplitter.autoInit()` | None | `void` | Reinitializes all Layout Splitters on the page. Useful after dynamically adding layout splitters to DOM. |\n\n### Usage Examples\n\n**Example 1: Getting splitter item data**\n```javascript\n// Get the layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n  // Get item data\n  const data = element.getData(item);\n\n  console.log('Item data:', data);\n}\n```\n\n**Example 2: Setting splitter item size**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const item = document.querySelector('#hs-splitter-item');\n\n  // Set item size to 50%\n  element.setSplitterItemSize(item, 50);\n}\n```\n\n**Example 3: Responsive breakpoints**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  // Update flex values based on window size\n  const updateLayoutSplitter = (splitter) => {\n    splitter.updateFlexValues([\n      {\n        id: \"hs-section-first\",\n        breakpoints: {\n          \"0\": 0,\n          \"1280\": 15.0\n        }\n      },\n      {\n        id: \"hs-section-second\",\n        breakpoints: {\n          \"0\": 0,\n          \"1024\": 25.4,\n          \"1280\": 21.0\n        }\n      },\n      {\n        id: \"hs-section-third\",\n        breakpoints: {\n          \"0\": 100,\n          \"1024\": 74.6,\n          \"1280\": 64.0\n        }\n      }\n    ]);\n  };\n\n  window.addEventListener('resize', () => {\n    if (instance) updateLayoutSplitter(instance.element);\n  });\n}\n```\n\n**Example 4: Destroying layout splitter instance**\n```javascript\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-layout-splitter').remove();\n  });\n}\n```\n\n## Events\n\nLayout splitter instances emit events that can be listened to for drag tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:drag` | When the splitter is dragged | `{ prev: HTMLElement, next: HTMLElement, previousSize: number, previousFlexSize: number, nextSize: number, nextFlexSize: number }` | Fires when the splitter is being dragged. Returns an object with previous and next elements and their sizes. |\n| `on:onPrevLimit` | When the previous section size limit is reached | `HTMLElement` (previous element) | Fires when the previous section reaches its minimum size limit. |\n| `on:onPrevPreLimit` | When the previous section size is less than `preLimitSize` | `HTMLElement` (previous element) | Fires when the previous section size is less than the `preLimitSize` parameter value. |\n| `on:onNextLimit` | When the next section size limit is reached | `HTMLElement` (next element) | Fires when the next section reaches its minimum size limit. |\n| `on:onNextPreLimit` | When the next section size is less than `preLimitSize` | `HTMLElement` (next element) | Fires when the next section size is less than the `preLimitSize` parameter value. |\n\n### Event Usage Example\n\n```javascript\n// Get layout splitter instance\nconst instance = HSLayoutSplitter.getInstance('#hs-layout-splitter', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to limit events\n  element.on('onPrevLimit', (prev) => {\n    prev.classList.add('hs-prev-limit-reached');\n  });\n\n  element.on('onPrevPreLimit', (prev) => {\n    prev.classList.add('hs-prev-pre-limit-reached');\n  });\n\n  // Listen to drag event\n  element.on('drag', ({ prev, previousFlexSize }) => {\n    if (previousFlexSize > 10) {\n      prev.classList.remove('hs-prev-limit-reached');\n    }\n\n    if (previousFlexSize > 20) {\n      prev.classList.remove('hs-prev-pre-limit-reached');\n    }\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Horizontal Splitter\n\nCreate horizontal resizable panels.\n\n```html\n<div data-hs-layout-splitter>\n  <div data-hs-layout-splitter-horizontal-group>\n    <div data-hs-layout-splitter-item=\"50\">Left Panel</div>\n    <div data-hs-layout-splitter-item=\"50\">Right Panel</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Minimum Size\n\nSet minimum size constraints.\n\n```html\n<div data-hs-layout-splitter-item='{\n  \"dynamicSize\": 30.0,\n  \"minSize\": 10.0\n}'>\n  Resizable Panel\n</div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/overlay/README.md (Tokens: ~4110 | Category: plugin-manifest) -->\n# Overlay\n\nThe Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/overlay) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/overlay.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Overlay component provides a flexible foundation for creating modals, drawers, sidebars, and other overlay-based UI elements. It handles backdrop management, focus trapping, keyboard navigation, and z-index stacking automatically.\n\n**Key Features:**\n- Modal and drawer support\n- Automatic backdrop management\n- Focus trapping and keyboard navigation (Tab, Esc)\n- Z-index stacking for multiple overlays\n- Responsive breakpoint support\n- Auto-close on screen size changes\n- Programmatic control via JavaScript API\n- Event system for lifecycle hooks\n- Accessibility attributes (ARIA) built-in\n\n## Installation\n\nTo get started, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/overlay\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/overlay */\n@source \"../node_modules/@preline/overlay/*.js\";\n@import \"./node_modules/@preline/overlay/variants.css\";\n@import \"./node_modules/@preline/overlay/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/overlay/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSOverlay from \"@preline/overlay/non-auto.mjs\";\n\n  new HSOverlay(document.querySelector(\"#overlay\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/overlay` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/overlay\";\n```\n\n`@preline/overlay/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSOverlay from \"@preline/overlay/non-auto\";\n\nHSOverlay.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#overlay\");\nif (el) new HSOverlay(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an overlay component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The overlay appears when the button is clicked.\n\n```html\n<button type=\"button\" aria-haspopup=\"dialog\" aria-expanded=\"false\" aria-controls=\"hs-unstyled-modal\" data-hs-overlay=\"#hs-unstyled-modal\">\n  Open modal\n</button>\n\n<div id=\"hs-unstyled-modal\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-60 overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"hs-unstyled-modal-label\">\n  <div class=\"hs-overlay-open:opacity-100 hs-overlay-open:duration-500 opacity-0 transition-all sm:max-w-lg sm:w-full m-3 sm:mx-auto\">\n    <div class=\"pointer-events-auto\">\n      <h3 id=\"hs-unstyled-modal-label\">Title</h3>\n      Modal content\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-overlay`: Required on the trigger button, must be a valid CSS selector pointing to the overlay element\n- `hs-overlay`: Required class on the overlay content container\n- `hidden` class on the overlay initially\n- Proper ARIA attributes (`aria-haspopup`, `aria-expanded`, `aria-controls`, `role=\"dialog\"`, `aria-labelledby`)\n\n**Initial State:**\n- Set `aria-expanded=\"false\"` on the trigger button initially\n- Add `hidden` class to the overlay initially\n- Overlay visibility is controlled by the plugin\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure overlay behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-overlay-options` | Trigger button | object (JSON) | - | Defines the modal configuration. Should be added to the button (trigger). |\n| `:hiddenClass` | Inside `data-hs-overlay-options` | string | `\"hidden\"` | Defines which classes will be added/removed when modal toggles. |\n| `:emulateScrollbarSpace` | Inside `data-hs-overlay-options` | boolean | `false` | If `true`, then adds right padding to the body equal to the size of the scrollbar. Useful to prevent layout shift when overlay opens. |\n| `:isClosePrev` | Inside `data-hs-overlay-options` | boolean | `true` | Determines whether the previous open modal will be closed when the next one is opened. |\n| `:backdropClasses` | Inside `data-hs-overlay-options` | string | `\"hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80\"` | Defines default backdrop classes. |\n| `:backdropExtraClasses` | Inside `data-hs-overlay-options` | string | - | Allows to add additional classes besides those specified in `backdropClasses`. |\n| `:backdropParent` | Inside `data-hs-overlay-options` | string | - | Allows to specify the selector of the element where the backdrop will be generated. |\n| `:moveOverlayToBody` | Inside `data-hs-overlay-options` | number \\| null | `null` | Moves the overlay to the body tag if the screen width is less than the value specified. |\n| `:isToggleClassesImmediately` | Inside `data-hs-overlay-options` | boolean | `false` | When set to `true`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n| `data-hs-overlay-backdrop-container` | Overlay content | string (CSS selector) | `null` | Backdrop element selector. Should be added to the overlay (content). |\n| `data-hs-overlay-keyboard` | Overlay content | boolean | `true` | When set to `false`, the modal will not close when pressing the ESC button on the keyboard. Should be added to the overlay (content). |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control overlay behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--overlay-backdrop:*]` | Overlay content | `\"static\"` \\| null | `null` | When backdrop is set to `\"static\"`, the modal will not close when clicking outside it. Should be added to the overlay (content). |\n| `[--auto-hide:*]` | Overlay content | number | `0` | Milliseconds for auto-closing a modal. When set to `0`, the modal will not close automatically. Should be added to the overlay (content). |\n| `[--auto-close:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the overlay will close automatically. Should be added to the overlay (content). |\n| `[--auto-close-equality-type:*]` | Overlay content | `'less-than'` \\| null | `null` | This option determines the condition under which an overlay should automatically close based on the window's width. When set to `less-than`, the overlay will close if the window's width is less than or equal to the value defined in `[--auto-close:*]`. Otherwise the overlay will close if the window's width is greater than or equal to the `[--auto-close:*]` threshold. Should be added to the overlay (content). |\n| `[--opened:*]` | Overlay content | `'sm'` \\| `'md'` \\| `'lg'` \\| `'xl'` \\| `'2xl'` \\| number | `null` | Screen resolution at which the plugin will apply functionality for the initially opened overlay. Should be added to the overlay (content). |\n| `[--body-scroll:*]` | Overlay content | boolean | `true` | When set to `false`, the body scroll will be hidden as soon as the modal opens. Should be added to the overlay (content). |\n| `[--tab-accessibility-limited:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Restricts focus to elements within an overlay (or modal). Should be added to the overlay (content). |\n| `[--has-autofocus:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"true\"` | Disables autofocus on the first focusable element when opening an overlay. Should be added to the overlay (content). |\n| `[--has-dynamic-z-index:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, each newly opened element in the group will receive an incremented `z-index` inline, ensuring it appears above previously opened elements with the same base `z-index`. Should be added to the overlay (content). |\n| `[--is-layout-affect:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | Informs the plugin that the instance affects the website layout. Should be added to the overlay (content). |\n| `[--toggle-classes-immediately:*]` | Overlay content | `\"true\"` \\| `\"false\"` | `\"false\"` | When set to `\"true\"`, toggles `hidden`, `open`, and `opened` classes immediately instead of waiting for the open delay, resize debounce, or close transition. |\n\n**Example:**\n```html\n<div class=\"hs-overlay --overlay-backdrop:static --auto-hide:5000\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the overlay to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay` | Overlay content container | Identifies the overlay content element |\n\n### Optional CSS Classes\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-overlay-animation-target` | Element inside overlay | Defines an element inside the popup, after the end of whose animation the popup will be completely hidden. Has no parameters. |\n| `autofocus` | Input element inside overlay | Focus the first input in a modal with the `autofocus` attribute on opening. Must be added to an input element inside a modal window. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-overlay-open:*` | Defines classes that will be applied to any elements inside the `hs-overlay` when the overlay is open. |\n| `hs-overlay-backdrop-open:*` | Defines classes that will be applied to backdrop when the overlay is open. |\n| `hs-overlay-layout-open:*` | Defines classes that will be applied to any elements inside the body tag when the overlay is open. |\n\n## JavaScript API\n\nThe `HSOverlay` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on an overlay instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open(cb)` | `cb`: `Function \\| null` (optional) | `Promise<void>` | Forces the overlay to open programmatically. Optionally accepts a callback function that will be called after opening. Returns a Promise. |\n| `close(forceClose, cb)` | `forceClose`: `boolean` (optional)<br>`cb`: `Function \\| null` (optional) | `Promise<unknown>` | Forces the overlay to close programmatically. `forceClose` bypasses animations and closes immediately. Optionally accepts a callback function. Returns a Promise. |\n| `minify(isMinified, cb)` | `isMinified`: `boolean`<br>`cb`: `Function \\| null` (optional) | `void` | Minimizes or restores the overlay. Useful for drawer/sidebar components. |\n| `updateToggles()` | None | `void` | Updates the toggle buttons. Useful after dynamically adding or removing toggle buttons. |\n| `destroy()` | None | `void` | Destroys the overlay instance, removes all generated markup, classes, and event listeners. Use when removing overlay from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSOverlay` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSOverlay.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSOverlay } \\| null` | Returns the overlay instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSOverlay` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if overlay instance is not found. |\n| `HSOverlay.open(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Opens the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. |\n| `HSOverlay.close(target)` | `target`: `HSOverlay \\| HTMLElement \\| string` (CSS selector) | `void` | Closes the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. |\n\n### Usage Examples\n\n**Example 1: Using instance methods (public API)**\n```javascript\n// Create a new overlay instance\nconst modal = new HSOverlay(document.querySelector('#hs-modal'));\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay programmatically\nopenBtn.addEventListener('click', () => {\n  modal.open();\n});\n```\n\n**Example 2: Using static methods**\n```javascript\nconst openBtn = document.querySelector('#hs-open-btn');\n\n// Open overlay using static method (no instance needed)\nopenBtn.addEventListener('click', () => {\n  HSOverlay.open('#hs-modal');\n});\n```\n\n**Example 3: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance; // element is the HSOverlay instance\n  const openBtn = document.querySelector('#hs-open-btn');\n  const closeBtn = document.querySelector('#hs-close-btn');\n\n  // Use instance methods\n  openBtn.addEventListener('click', () => {\n    element.open();\n  });\n\n  closeBtn.addEventListener('click', () => {\n    element.close();\n  });\n\n  // Clean up when removing from DOM\n  function removeOverlay() {\n    element.destroy();\n  }\n}\n```\n\n**Example 4: Destroying overlay instance**\n```javascript\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-modal').remove();\n  });\n}\n```\n\n## Events\n\nOverlay instances emit events that can be listened to for lifecycle hooks and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:open` | After overlay is opened | `HSOverlay` (overlay instance) | Fires after the overlay has been displayed. Use for post-open actions like loading content or focusing elements. |\n| `on:close` | After overlay is closed | `HSOverlay` (overlay instance) | Fires after the overlay has been hidden. Use for cleanup or state updates. |\n\n### Event Usage Example\n\n```javascript\n// Get overlay instance\nconst instance = HSOverlay.getInstance('#hs-modal', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to open event\n  element.on('open', (overlayInstance) => {\n    console.log('Overlay opened:', overlayInstance);\n    // Perform actions after overlay opens\n    // e.g., load content, focus input, track analytics\n  });\n\n  // Listen to close event\n  element.on('close', (overlayInstance) => {\n    console.log('Overlay closed:', overlayInstance);\n    // Perform cleanup or state updates\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Static Backdrop\n\nPrevent closing when clicking outside the overlay.\n\n```html\n<div class=\"hs-overlay --overlay-backdrop:static\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 2: Auto-close on Screen Size\n\nClose overlay automatically at specific breakpoints.\n\n```html\n<div class=\"hs-overlay --auto-close:md --auto-close-equality-type:less-than\">\n  <!-- Overlay content -->\n</div>\n```\n\n### Pattern 3: Programmatic Control\n\nControl overlay from external buttons.\n\n```html\n<button id=\"hs-open-modal\">Open Modal</button>\n<button id=\"hs-close-modal\">Close Modal</button>\n\n<div id=\"hs-modal-first\" class=\"hs-overlay hidden\">\n  <!-- Overlay content -->\n</div>\n\n<script>\n  const instance = HSOverlay.getInstance('#hs-modal-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    document.querySelector('#hs-open-modal').addEventListener('click', () => {\n      element.open();\n    });\n\n    document.querySelector('#hs-close-modal').addEventListener('click', () => {\n      element.close();\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/pin-input/README.md (Tokens: ~2421 | Category: plugin-manifest) -->\n# PIN Input\n\nA PIN Input plugin that allows to fill in data for inputs.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/pin-input) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/pin-input.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe PIN Input component provides a multi-field input interface for entering PIN codes, verification codes, or similar numeric/alphanumeric sequences. It automatically moves focus between fields and supports paste operations.\n\n**Key Features:**\n- Multiple input fields for PIN entry\n- Automatic focus management\n- Paste support for bulk entry\n- Character validation via regex\n- Programmatic control via JavaScript API\n- Event system for completion tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install PIN Input plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/pin-input\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/pin-input */\n@source \"../node_modules/@preline/pin-input/*.js\";\n@import \"./node_modules/@preline/pin-input/variants.css\";\n@import \"./node_modules/@preline/pin-input/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/pin-input/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSPinInput from \"@preline/pin-input/non-auto.mjs\";\n\n  new HSPinInput(document.querySelector(\"#pin-input\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/pin-input` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/pin-input\";\n```\n\n`@preline/pin-input/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSPinInput from \"@preline/pin-input/non-auto\";\n\nHSPinInput.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#pin-input\");\nif (el) new HSPinInput(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a PIN input component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component consists of multiple input fields that automatically advance focus.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-pin-input`: Required on the container element\n- `data-hs-pin-input-item`: Required on each input field\n- All input fields should be direct children of the container\n\n**Initial State:**\n- Input fields are empty by default\n- Focus starts on the first input field (if `autofocus` class is used)\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-pin-input` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-pin-input` | Container | - | - | Activate a Pin Input by specifying on an element. Should be added to the container. |\n| `:availableCharsRE` | Inside `data-hs-pin-input` | RegExp (string) | `\"^[a-zA-Z0-9]+$\"` | Regular expression string for available characters. Only characters matching this regex will be accepted. |\n| `data-hs-pin-input-item` | Input element | - | - | Determines which element inside the initialized container will be responsible for entering data. Should be added to the input. |\n\n**Example:**\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <!-- Input fields -->\n</div>\n```\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control PIN input behavior.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `autofocus` | One of the input fields | If one of the fields has this class, it will be focused when the page loads. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-pin-input-active:*` | The class is added to the input when the PIN has been set up (all fields are filled). |\n\n## JavaScript API\n\nThe `HSPinInput` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a PIN input instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the PIN input instance, removes all generated markup, classes, and event listeners. Use when removing PIN input from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSPinInput` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSPinInput.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSPinInput \\| { id: string \\| number, element: HSPinInput } \\| null` | Returns the PIN input instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSPinInput` instance. If `isInstance` is `false` or omitted, returns the `HSPinInput` instance directly. Returns `null` if PIN input instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Current value:', element.currentValue);\n  console.log('Items:', element.items);\n\n  // Clean up when removing from DOM\n  function removePinInput() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying PIN input instance**\n```javascript\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-pin-input').remove();\n  });\n}\n```\n\n## Events\n\nPIN input instances emit events that can be listened to for completion tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:completed` | When the PIN has been set up (all fields filled) | `{ currentValue: string }` | Fires when all input fields have been filled. Returns an object with the `currentValue` containing the complete PIN string. |\n\n### Event Usage Example\n\n```javascript\n// Get PIN input instance\nconst instance = HSPinInput.getInstance('#hs-pin-input', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to completed event\n  element.on('completed', ({ currentValue }) => {\n    console.log('PIN completed:', currentValue);\n    // Perform actions after PIN is completed\n    // e.g., validate PIN, submit form, proceed to next step\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Numeric PIN Only\n\nRestrict input to numbers only.\n\n```html\n<div data-hs-pin-input='{\n  \"availableCharsRE\": \"^[0-9]+$\"\n}'>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 2: Auto-focus First Field\n\nAutomatically focus the first field on page load.\n\n```html\n<div data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item class=\"autofocus\">\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n```\n\n### Pattern 3: Handling Completion\n\nHandle PIN completion with event listener.\n\n```html\n<div id=\"hs-pin-input-first\" data-hs-pin-input>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n  <input type=\"text\" data-hs-pin-input-item>\n</div>\n\n<script>\n  const instance = HSPinInput.getInstance('#hs-pin-input-first', true);\n  \n  if (instance) {\n    const { element } = instance;\n\n    element.on('completed', ({ currentValue }) => {\n      console.log('PIN entered:', currentValue);\n      // Validate or submit the PIN\n    });\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/range-slider/README.md (Tokens: ~2641 | Category: plugin-manifest) -->\n# Advanced Range Slider\n\nAdvanced Range Slider solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/range-slider) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-range-slider.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Range Slider component provides a flexible range input interface for selecting numeric values within a specified range. It integrates with noUiSlider for advanced functionality including dual handles, custom formatting, and smooth animations.\n\n**Key Features:**\n- Single and dual handle support\n- Custom value formatting\n- Smooth animations and transitions\n- Integration with noUiSlider\n- Programmatic control via JavaScript API\n- Event system for value tracking\n- Disabled state support\n\n## Installation\n\nTo get started, install Advanced Range Slider plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i nouislider @preline/range-slider\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/range-slider */\n@source \"../node_modules/@preline/range-slider/*.js\";\n@import \"./node_modules/@preline/range-slider/variants.css\";\n@import \"./node_modules/@preline/range-slider/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script src=\"./node_modules/@preline/range-slider/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script src=\"./node_modules/nouislider/dist/nouislider.min.js\"></script>\n<script type=\"module\">\n  import HSRangeSlider from \"@preline/range-slider/non-auto.mjs\";\n\n  new HSRangeSlider(document.querySelector(\"#range-slider\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `noUiSlider` is referenced as a global, so expose it on `window` before importing the plugin.\n\n`@preline/range-slider` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport \"@preline/range-slider\";\n```\n\n`@preline/range-slider/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport * as noUiSlider from \"nouislider\";\nwindow.noUiSlider = noUiSlider;\n\nimport HSRangeSlider from \"@preline/range-slider/non-auto\";\n\nHSRangeSlider.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#range-slider\");\nif (el) new HSRangeSlider(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced range slider component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The slider allows selecting a value between 0 and 100.\n\n```html\n<div data-hs-range-slider='{\n  \"cssClasses\": {},\n  \"start\": 50,\n  \"connect\": \"lower\",\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n**Structure Requirements:**\n- `data-hs-range-slider`: Required on the container element, contains configuration options as JSON\n- Container element will be automatically populated with slider markup by the plugin\n\n**Initial State:**\n- Slider starts at the value specified in `start` option\n- Range is defined by `min` and `max` in the `range` option\n\n## Configuration Options\n\n### Data Options\n\n> **Note:** Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin.\n\nData options are specified in the `data-hs-range-slider` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-range-slider` | Container element | - | - | Activate a Range Slider by specifying on an element. Should be added to the initialized element. Since our plugin is an extension of the [noUiSlider](https://refreshless.com/nouislider/) plugin, all its [options](https://refreshless.com/nouislider/slider-options/) can be passed as parameters to our plugin. |\n| `:disabled` | Inside `data-hs-range-slider` | boolean | `false` | Makes the element unavailable for interaction. When `true`, the slider cannot be moved. |\n| `:formatter` | Inside `data-hs-range-slider` | string \\| object | - | Output value formatting options. Can be a string or an object with formatting configuration. |\n| `:formatter:type` | Inside `data-hs-range-slider` | `\"integer\"` \\| `\"thousandsSeparatorAndDecimalPoints\"` \\| null | `null` | Defines an output formatter type. `integer` formats as integer, `thousandsSeparatorAndDecimalPoints` adds thousands separators and decimal points. |\n| `:formatter:prefix` | Inside `data-hs-range-slider` | string | - | Adds a prefix to the output value (e.g., \"$\" for currency). |\n| `:formatter:postfix` | Inside `data-hs-range-slider` | string | - | Adds a postfix to the output value (e.g., \"px\" for pixels). |\n\n**Example:**\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  },\n  \"disabled\": false\n}'></div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-range-slider-disabled:*` | A modifier that allows you to set Tailwind classes when slider has \"disabled\" attribute set to \"true\". |\n\n## JavaScript API\n\nThe `HSRangeSlider` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a range slider instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `formattedValue()` | None | `any` | Returns the current values formatted using the `formatter` option. Returns formatted values as specified in the formatter configuration. |\n| `destroy()` | None | `void` | Destroys the range slider instance, removes all generated markup, classes, and event listeners. Use when removing slider from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRangeSlider` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRangeSlider.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRangeSlider } \\| null` | Returns the range slider instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRangeSlider` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if range slider instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting formatted value**\n```javascript\n// Get the range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get formatted value\n  const formatted = element.formattedValue();\n\n  console.log('Formatted value:', formatted);\n}\n```\n\n**Example 2: Accessing noUiSlider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  // Access underlying noUiSlider instance\n  const noUiSlider = element.el.noUiSlider;\n\n  if (noUiSlider) {\n    // Use noUiSlider methods\n    const currentValue = noUiSlider.get();\n\n    console.log('Current value:', currentValue);\n  }\n}\n```\n\n**Example 3: Destroying range slider instance**\n```javascript\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-range-slider').remove();\n  });\n}\n```\n\n## Events\n\n> **Note:** Note that all [events](https://refreshless.com/nouislider/events-callbacks/) available in the [noUiSlider](https://refreshless.com/nouislider/) plugin are also available in our plugin, just use `el.noUiSlider` to handle events.\n\nRange slider instances emit events that can be listened to for value tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:update` | When slider value is updated | `[integer, integer?]` | Fires when the slider value changes. Returns an array with current values. For single handle, array contains one value. For dual handles, array contains two values. |\n\n### Event Usage Example\n\n```javascript\n// Get range slider instance\nconst instance = HSRangeSlider.getInstance('#hs-range-slider', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen to update event via noUiSlider\n  if (element.el.noUiSlider) {\n    element.el.noUiSlider.on('update', (values) => {\n      console.log('Slider updated:', values);\n      console.log('Formatted value:', element.formattedValue());\n      // Perform actions after slider value changes\n      // e.g., update UI, send to server, filter data\n    });\n  }\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dual Handle Range\n\nCreate a range slider with two handles for selecting a range.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": [20, 80],\n  \"connect\": true,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  }\n}'></div>\n```\n\n### Pattern 2: Formatted Values\n\nAdd prefix and postfix to slider values.\n\n```html\n<div data-hs-range-slider='{\n  \"start\": 50,\n  \"range\": {\n    \"min\": 0,\n    \"max\": 100\n  },\n  \"formatter\": {\n    \"type\": \"integer\",\n    \"prefix\": \"$\",\n    \"postfix\": \"\"\n  }\n}'></div>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/remove-element/README.md (Tokens: ~1989 | Category: plugin-manifest) -->\n# Remove Element\n\nDismiss any content.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/remove-element) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/remove-element.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Remove Element component provides a simple way to remove or dismiss elements from the DOM with optional animation. It's commonly used for dismissible alerts, notifications, modals, or any content that should be removable.\n\n**Key Features:**\n- One-click element removal\n- Optional removal animation\n- Programmatic control via JavaScript API\n- Simple configuration\n- Event system support\n\n## Installation\n\nTo get started, install Remove Element plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/remove-element\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/remove-element */\n@source \"../node_modules/@preline/remove-element/*.js\";\n@import \"./node_modules/@preline/remove-element/variants.css\";\n@import \"./node_modules/@preline/remove-element/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/remove-element/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSRemoveElement from \"@preline/remove-element/non-auto.mjs\";\n\n  new HSRemoveElement(document.querySelector(\"#remove-element\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/remove-element` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/remove-element\";\n```\n\n`@preline/remove-element/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSRemoveElement from \"@preline/remove-element/non-auto\";\n\nHSRemoveElement.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#remove-element\");\nif (el) new HSRemoveElement(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a remove element component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will remove the target element.\n\n```html\n<div id=\"hs-dismiss-unstyled-first\" class=\"inline-flex justify-between items-center w-full\">\n  <h3>\n    Remove element card\n  </h3>\n  <button type=\"button\" class=\"inline-flex justify-center items-center size-4\" data-hs-remove-element=\"#hs-dismiss-unstyled-first\">\n    <span class=\"sr-only\">Close</span>\n    <svg class=\"shrink-0 size-4\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n  </button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-remove-element`: Required on the trigger button, must be a valid CSS selector pointing to the element to remove\n- Target element must exist in the DOM\n- Button can be inside or outside the target element\n\n**Initial State:**\n- Target element is visible\n- Clicking the button removes the element\n\n## Configuration Options\n\n### Data Options\n\nData options are specified via data attributes.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-remove-element` | Trigger button | string (CSS selector) | - | The element to be removed. This must be a valid CSS selector. Should be added to the button (trigger). Required. |\n| `data-hs-remove-element-options` | Trigger button | object (JSON) | - | Options that can be added via the data attribute. Should be added to the button (trigger). |\n| `:removeTargetAnimationClass` | Inside `data-hs-remove-element-options` | string | `\"hs-removing\"` | Some valid CSS class that will be applied to the target element before removal. Useful for animations. |\n\n**Example:**\n```html\n<button data-hs-remove-element=\"#hs-alert\" data-hs-remove-element-options='{\n  \"removeTargetAnimationClass\": \"fade-out\"\n}'>\n  Close\n</button>\n```\n\n## JavaScript API\n\nThe `HSRemoveElement` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a remove element instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the remove element instance, removes all generated markup, classes, and event listeners. Use when removing remove element from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSRemoveElement` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSRemoveElement.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSRemoveElement } \\| null` | Returns the remove element instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSRemoveElement` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if remove element instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and accessing properties**\n```javascript\n// Get the remove element instance\nconst instance = HSRemoveElement.getInstance('[data-hs-remove-element]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Target element:', element.removeTarget);\n  console.log('Animation class:', element.removeTargetAnimationClass);\n\n  // Clean up when removing from DOM\n  function removeRemoveElement() {\n    element.destroy();\n  }\n}\n```\n\n**Example 3: Destroying remove element instance**\n```javascript\nconst instance = HSRemoveElement.getInstance('#hs-remove-element', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-remove-element').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Dismissible Alert\n\nCreate a dismissible alert notification.\n\n```html\n<div id=\"hs-alert-first\" class=\"alert\">\n  <p>This is an alert message.</p>\n  <button data-hs-remove-element=\"#hs-alert-first\">×</button>\n</div>\n```\n\n### Pattern 2: With Animation\n\nAdd removal animation using CSS classes.\n\n```html\n<div id=\"hs-card-first\" class=\"card\">\n  <h3>Card Title</h3>\n  <button data-hs-remove-element=\"#hs-card-first\" data-hs-remove-element-options='{\n    \"removeTargetAnimationClass\": \"fade-out\"\n  }'>\n    Remove\n  </button>\n</div>\n\n<style>\n  .fade-out {\n    opacity: 0;\n    transition: opacity 0.3s;\n  }\n</style>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/scroll-nav/README.md (Tokens: ~2614 | Category: plugin-manifest) -->\n# Scroll Nav\n\nScroll Nav is a versatile and lightweight JavaScript plugin designed to enhance navigation menus with smooth scrolling capabilities.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scroll-nav) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scroll-nav.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scroll Nav component provides horizontal scrolling navigation with previous/next buttons and automatic centering. It's ideal for navigation menus with many items that need to be scrolled horizontally.\n\n**Key Features:**\n- Horizontal scrolling navigation\n- Previous/next navigation buttons\n- Automatic centering of active items\n- Programmatic control via JavaScript API\n- Smooth scrolling animations\n- Current state tracking\n\n## Installation\n\nTo get started, install Scroll Nav plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scroll-nav\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scroll-nav */\n@source \"../node_modules/@preline/scroll-nav/*.js\";\n@import \"./node_modules/@preline/scroll-nav/variants.css\";\n@import \"./node_modules/@preline/scroll-nav/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scroll-nav/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollNav from \"@preline/scroll-nav/non-auto.mjs\";\n\n  new HSScrollNav(document.querySelector(\"#scroll-nav\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scroll-nav` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scroll-nav\";\n```\n\n`@preline/scroll-nav/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollNav from \"@preline/scroll-nav/non-auto\";\n\nHSScrollNav.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scroll-nav\");\nif (el) new HSScrollNav(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scroll nav component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation items scroll horizontally with previous/next buttons.\n\n```html\n<div data-hs-scroll-nav class=\"relative px-6\">\n  <button type=\"button\" class=\"hs-scroll-nav-prev\">Previous</button>\n\n  <nav class=\"hs-scroll-nav-body flex flex-nowrap overflow-x-auto snap-x snap-mandatory\">\n    <a class=\"snap-start\" href=\"#\">Electronics</a>\n    <a class=\"snap-start\" href=\"#\">Clothing</a>\n    <a class=\"snap-start\" href=\"#\">House & Garden</a>\n    <a class=\"snap-start\" href=\"#\">Baby Care</a>\n    <a class=\"snap-start\" href=\"#\">Home appliances</a>\n    <a class=\"snap-start\" href=\"#\">Sports & Fitness</a>\n    <a class=\"snap-start\" href=\"#\">Constraction & Renovation</a>\n    <a class=\"snap-start\" href=\"#\">Food</a>\n    <a class=\"snap-start\" href=\"#\">Pharmacy</a>\n    <a class=\"snap-start\" href=\"#\">Pet Products</a>\n    <a class=\"snap-start\" href=\"#\">Books</a>\n    <a class=\"snap-start\" href=\"#\">Tourism, fishing, hunting</a>\n    <a class=\"snap-start\" href=\"#\">Furniture</a>\n    <a class=\"snap-start\" href=\"#\">Jewelery</a>\n    <a class=\"snap-start\" href=\"#\">Accessories</a>\n    <a class=\"snap-start\" href=\"#\">Games & Consoles</a>\n    <a class=\"snap-start\" href=\"#\">Stationery</a>\n    <a class=\"snap-start\" href=\"#\">Digital Goods</a>\n    <a class=\"snap-start\" href=\"#\">Music</a>\n  </nav>\n\n  <button type=\"button\" class=\"hs-scroll-nav-next\">Next</button>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scroll-nav`: Required on the container element\n- `hs-scroll-nav-prev`: Required class on the previous button\n- `hs-scroll-nav-next`: Required class on the next button\n- `hs-scroll-nav-body`: Required class on the scrollable navigation container\n- Navigation items inside the scrollable container\n\n**Initial State:**\n- Navigation items are visible\n- Previous/next buttons are enabled/disabled based on scroll position\n- Active item can be centered automatically if `autoCentering` is enabled\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-scroll-nav` attribute.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scroll-nav` | Container | - | - | Activate a Scroll Nav by specifying on an element. Should be added to the container. |\n| `:autoCentering` | Inside `data-hs-scroll-nav` | boolean | `false` | Defines whether the Scroll Nav should align with the active item when the window loads. When `true`, the active item is automatically centered on page load. |\n\n**Example:**\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Required CSS Classes\n\nThese classes define the structure and must be present for the scroll nav to function.\n\n| Class | Required On | Purpose |\n| --- | --- | --- |\n| `hs-scroll-nav-prev` | Previous button | Identifies the previous navigation button |\n| `hs-scroll-nav-next` | Next button | Identifies the next navigation button |\n| `hs-scroll-nav-body` | Scrollable container | Identifies the scrollable navigation container |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scroll-nav-active:*` | The class is applied to the navigation item to style the currently active element. |\n| `hs-scroll-nav-disabled:*` | The class is added to the prev/next buttons for style the disabled state. |\n\n## JavaScript API\n\nThe `HSScrollNav` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scroll nav instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `getCurrentState()` | None | `IScrollNavCurrentState` | Retrieves the currently visible first, last, and center elements within the scrollable navigation container. Returns an object with `first`, `last`, and `center` properties. |\n| `goTo(el, cb)` | `el`: `Element` (target element)<br>`cb`: `Function` (optional callback) | `void` | Smoothly scrolls the navigation container to bring the specified element into view. The callback function is executed after scrolling completes. |\n| `centerElement(el, behavior)` | `el`: `HTMLElement` (target element)<br>`behavior`: `ScrollBehavior` (optional, `\"auto\"` \\| `\"smooth\"`) | `void` | Scrolls the navigation container to position the specified element at the center of the visible area. Default behavior is `\"smooth\"`. |\n| `destroy()` | None | `void` | Destroys the scroll nav instance, removes all generated markup, classes, and event listeners. Use when removing scroll nav from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollNav` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollNav.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollNav } \\| null` | Returns the scroll nav instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollNav` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scroll nav instance is not found. |\n\n### Usage Examples\n\n**Example 1: Getting current state**\n```javascript\n// Get the scroll nav instance\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  // Get current state\n  const state = element.getCurrentState();\n\n  console.log('First visible:', state.first);\n  console.log('Last visible:', state.last);\n  console.log('Center visible:', state.center);\n}\n```\n\n**Example 2: Scrolling to an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Scroll to element\n  element.goTo(targetLink, () => {\n    console.log('Scrolled to element!');\n    // Perform actions after scrolling\n  });\n}\n```\n\n**Example 3: Centering an element**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const targetLink = document.querySelector('#hs-e-commerce-link');\n\n  // Center element\n  element.centerElement(targetLink, 'smooth');\n}\n```\n\n**Example 4: Destroying scroll nav instance**\n```javascript\nconst instance = HSScrollNav.getInstance('#hs-scroll-nav', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-scroll-nav').remove();\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Auto-centering Active Item\n\nAutomatically center the active item on page load.\n\n```html\n<div data-hs-scroll-nav='{\n  \"autoCentering\": true\n}'>\n  <!-- Navigation structure -->\n</div>\n```\n\n### Pattern 2: Programmatic Navigation\n\nControl navigation programmatically.\n\n```html\n<div id=\"hs-scroll-nav-first\" data-hs-scroll-nav>\n  <!-- Navigation structure -->\n</div>\n\n<script>\n  const instance = HSScrollNav.getInstance('#hs-scroll-nav-first', true);\n  if (instance) {\n    const { element } = instance;\n    // Scroll to specific item\n    const item = document.querySelector('#hs-item-electronics');\n    \n    element.goTo(item);\n  }\n</script>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/scrollspy/README.md (Tokens: ~2427 | Category: plugin-manifest) -->\n# Scrollspy\n\nAutomatically update navigation or list group components based on scroll position to indicate which link is currently active in the viewport.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/scrollspy) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/scrollspy.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Scrollspy component automatically highlights navigation links based on the scroll position of the page. It tracks which section is currently visible in the viewport and updates the corresponding navigation item accordingly.\n\n**Key Features:**\n- Automatic navigation highlighting based on scroll position\n- Customizable scroll offset\n- Support for custom scrollable containers\n- Programmatic control via JavaScript API\n- Event system for scroll tracking\n- Accessibility support\n\n## Installation\n\nTo get started, install Scrollspy plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/scrollspy\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/scrollspy */\n@source \"../node_modules/@preline/scrollspy/*.js\";\n@import \"./node_modules/@preline/scrollspy/variants.css\";\n@import \"./node_modules/@preline/scrollspy/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag:\n\n```html\n<script src=\"./node_modules/@preline/scrollspy/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<script type=\"module\">\n  import HSScrollspy from \"@preline/scrollspy/non-auto.mjs\";\n\n  new HSScrollspy(document.querySelector(\"#scrollspy\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.\n\n`@preline/scrollspy` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\nimport \"@preline/scrollspy\";\n```\n\n`@preline/scrollspy/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\nimport HSScrollspy from \"@preline/scrollspy/non-auto\";\n\nHSScrollspy.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#scrollspy\");\nif (el) new HSScrollspy(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for a scrollspy component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The navigation links are automatically highlighted as you scroll through the sections.\n\n```html\n<div id=\"hs-scrollspy-scrollable-parent-first\" class=\"max-h-96 overflow-y-auto\">\n  <div data-hs-scrollspy=\"#hs-scrollspy-first\" class=\"flex flex-wrap gap-3\">\n    <a class=\"active\" href=\"#hs-first\">First</a>\n    <a href=\"#hs-second\">Second</a>\n    <a href=\"#hs-third\">Third</a>\n    <a href=\"#hs-fourth\">Fourth</a>\n    <a href=\"#hs-fifth\">Fifth</a>\n  </div>\n\n  <div id=\"hs-scrollspy-first\">\n    <div id=\"hs-first\" class=\"h-48\">\n      First\n    </div>\n    <div id=\"hs-second\" class=\"h-48\">\n      Second\n    </div>\n    <div id=\"hs-third\" class=\"h-48\">\n      Third\n    </div>\n    <div id=\"hs-fourth\" class=\"h-48\">\n      Fourth\n    </div>\n    <div id=\"hs-fifth\" class=\"h-48\">\n      Fifth\n    </div>\n  </div>\n</div>\n```\n\n**Structure Requirements:**\n- `data-hs-scrollspy`: Required on the navigation container, must be a valid CSS selector pointing to the sections container\n- Navigation links with `href` attributes pointing to section IDs\n- Sections with matching `id` attributes\n- `active` class on the initially active navigation link\n\n**Initial State:**\n- Add `active` class to the navigation link that corresponds to the initially visible section\n- Ensure sections have sufficient height to enable scrolling\n\n## Configuration Options\n\n### Data Attributes\n\nData attributes are added directly to HTML elements to configure scrollspy behavior.\n\n| Attribute | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-scrollspy` | Navigation container | string (CSS selector) | - | A container containing sections. This must be a valid CSS selector pointing to the sections container. Should be added to the nav that contains Scrollspy links. |\n| `data-hs-scrollspy-scrollable-parent` | Navigation container | string (CSS selector) | `\"window\"` | Specifies the element to be scrolled. This must be a valid CSS selector. Should be added to the nav that contains Scrollspy links. Defaults to `window` if not specified. |\n\n### CSS Classes (Modifiers)\n\nCSS class modifiers use Tailwind-style syntax with `--` prefix to control scrollspy behavior.\n\n| Class Modifier | Target Element | Values | Default | Description |\n| --- | --- | --- | --- | --- |\n| `[--scrollspy-offset:*]` | Navigation container | number | `0` | Adds offset when scrolling to the section and to determine if the section is active. Useful for fixed headers or navigation bars. |\n\n**Example:**\n```html\n<div data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</div>\n```\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-scrollspy-active:*` | A modifier that allows you to set Tailwind classes when the section is active for the corresponding link. |\n\n## JavaScript API\n\nThe `HSScrollspy` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a scrollspy instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `destroy()` | None | `void` | Destroys the scrollspy instance, removes all generated markup, classes, and event listeners. Use when removing scrollspy from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSScrollspy` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSScrollspy.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSScrollspy } \\| null` | Returns the scrollspy instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSScrollspy` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if scrollspy instance is not found. |\n\n### Usage Examples\n\n**Example 1: Destroying scrollspy instance**\n```javascript\nconst instance = HSScrollspy.getInstance('#hs-scrollspy', true);\n\nif (instance) {\n  const { element } = instance;\n  const destroyBtn = document.querySelector('#hs-destroy-btn');\n\n  destroyBtn.addEventListener('click', () => {\n    element.destroy();\n  });\n}\n```\n\n**Example 2: Getting instance and using methods (recommended pattern)**\n```javascript\n// Get the scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Access instance properties\n  console.log('Scrollspy links:', element.links);\n  console.log('Scrollspy sections:', element.sections);\n\n  // Clean up when removing from DOM\n  function removeScrollspy() {\n    element.destroy();\n  }\n}\n```\n\n## Events\n\nScrollspy instances emit events that can be listened to for scroll tracking and custom behavior.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:beforeScroll` | Before scrolling begins | `HSScrollspy` (scrollspy instance) | Fires before scrolling to a section begins. Can return a Promise to delay scrolling until async operations complete. Useful for animations or other preparations. |\n\n### Event Usage Example\n\n```javascript\n// Get scrollspy instance\nconst instance = HSScrollspy.getInstance('[data-hs-scrollspy=\"#hs-scrollspy\"]', true);\n\nif (instance) {\n  const { element } = instance;\n  const collapse = HSCollapse.getInstance('[data-hs-collapse=\"#hs-navbar-collapse\"]', true);\n\n  // Listen to beforeScroll event\n  element.on('beforeScroll', (scrollspyInstance) => {\n    return new Promise((resolve) => {\n      if (collapse && collapse.element.el.classList.contains('open')) {\n        collapse.element.hide();\n        HSStaticMethods.afterTransition(collapse.element.content, () => resolve(true));\n      } else {\n        resolve(true);\n      }\n    });\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Custom Scrollable Container\n\nUse scrollspy with a custom scrollable container instead of the window.\n\n```html\n<div id=\"hs-scrollable-container\" class=\"max-h-96 overflow-y-auto\">\n  <nav data-hs-scrollspy=\"#hs-sections\" data-hs-scrollspy-scrollable-parent=\"#hs-scrollable-container\">\n    <a href=\"#hs-section-first\">Section 1</a>\n    <a href=\"#hs-section-second\">Section 2</a>\n  </nav>\n  \n  <div id=\"hs-sections\">\n    <div id=\"hs-section-first\">Content 1</div>\n    <div id=\"hs-section-second\">Content 2</div>\n  </div>\n</div>\n```\n\n### Pattern 2: With Offset\n\nAdd offset for fixed navigation bars.\n\n```html\n<nav data-hs-scrollspy=\"#hs-sections\" --scrollspy-offset:100>\n  <!-- Navigation links -->\n</nav>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- File: src/plugins/select/README.md (Tokens: ~6435 | Category: plugin-manifest) -->\n# Advanced Select\n\nAdvanced select solutions for massive datasets.\n\n[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/select) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/advanced-select.html)\n\n## Contents\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Configuration Options](#configuration-options)\n- [JavaScript API](#javascript-api)\n- [Events](#events)\n- [Common Patterns](#common-patterns)\n- [License](#license)\n\n## Overview\n\nThe Advanced Select component provides a customizable dropdown select interface with support for search, tags, API integration, and extensive styling options. It's designed for handling large datasets and complex selection scenarios.\n\n**Key Features:**\n- Search functionality\n- Tags mode for multiple selections\n- API integration for dynamic options\n- Custom templates and styling\n- Multiple selection support\n- Option grouping\n- Icons and descriptions for options\n- Programmatic control via JavaScript API\n- Event system for selection tracking\n\n## Installation\n\nTo get started, install Select plugin via npm, else you can skip this step if you are already using Preline UI as a package.\n\n```bash\nnpm i @preline/select\n```\n\nSome positioning features require [Floating UI](https://floating-ui.com). Install it if you plan to use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`:\n\n```bash\nnpm i @floating-ui/dom\n```\n\n### CSS\n\nUse [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.\n\n```css\n@import \"tailwindcss\";\n\n/* @preline/select */\n@source \"../node_modules/@preline/select/*.js\";\n@import \"./node_modules/@preline/select/variants.css\";\n@import \"./node_modules/@preline/select/theme.css\";\n```\n\n### JavaScript\n\nInclude the JavaScript that powers the interactive elements near the end of your `</body>` tag. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, also load the [Floating UI](https://floating-ui.com) UMD bundle before the plugin. It exposes the `FloatingUIDOM` global the plugin relies on.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script src=\"./node_modules/@preline/select/index.js\"></script>\n```\n\n### Manual Initialization\n\nUse the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.\n\n```html\n<!-- Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement -->\n<script src=\"https://cdn.jsdelivr.net/npm/@floating-ui/dom@latest/dist/floating-ui.dom.umd.min.js\"></script>\n<script type=\"module\">\n  import HSSelect from \"@preline/select/non-auto.mjs\";\n\n  new HSSelect(document.querySelector(\"#select\"));\n</script>\n```\n\n### Via Bundler\n\nWhen using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. If you use `dropdownScope: \"window\"`, `dropdownPlacement`, or `dropdownAutoPlacement`, expose `FloatingUIDOM` on `window` before importing the plugin.\n\n`@preline/select` is the auto-init entry: it scans the DOM and initializes matching elements automatically.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport \"@preline/select\";\n```\n\n`@preline/select/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.\n\n```js\n// Optional: required for dropdownScope: \"window\", dropdownPlacement, dropdownAutoPlacement\nimport * as FloatingUIDOM from \"@floating-ui/dom\";\nwindow.FloatingUIDOM = FloatingUIDOM;\n\nimport HSSelect from \"@preline/select/non-auto\";\n\nHSSelect.autoInit();\n\n// Or initialize a specific element manually\nconst el = document.querySelector(\"#select\");\nif (el) new HSSelect(el);\n```\n\n### TypeScript\n\nThis package ships with TypeScript type definitions. No additional `@types/` package is needed.\n\n## Basic usage\n\nThe following example demonstrates the minimal HTML structure required for an advanced select component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component transforms a standard select element into an advanced dropdown.\n\n```html\n<select data-hs-select='{\n    \"placeholder\": \"Select option...\",\n    \"toggleTag\": \"<button type=\\\"button\\\"></button>\",\n    \"toggleClasses\": \"\",\n    \"dropdownClasses\": \"\",\n    \"optionClasses\": \"hs-selected:\"\n  }'>\n  <option>Select option</option>\n  <option>Name</option>\n  <option>Email address</option>\n  <option>Description</option>\n  <option>User ID</option>\n</select>\n```\n\n**Structure Requirements:**\n- `data-hs-select`: Required on the `<select>` element, contains configuration options as JSON\n- Standard `<option>` elements inside the select\n- Optional `<optgroup>` elements for grouping\n\n**Initial State:**\n- Select displays placeholder text when no option is selected\n- Dropdown is closed by default\n\n## Configuration Options\n\n### Data Options\n\nData options are specified in the `data-hs-select` attribute as a JSON object.\n\n| Option | Target Element | Type | Default | Description |\n| --- | --- | --- | --- | --- |\n| `data-hs-select` | Select element | - | - | Activate a Custom Select by specifying on an element. Should be added to the select. |\n| `:isOpened` | Inside `data-hs-select` | boolean | `false` | Opens the select if the value is `true`. |\n| `:placeholder` | Inside `data-hs-select` | string | `\"Select...\"` | Define a default placeholder when nothing is selected. |\n| `:hasSearch` | Inside `data-hs-select` | boolean | `false` | Define a search field inside the dropdown if the value is `true`. |\n| `:minSearchLength` | Inside `data-hs-select` | number | `0` | Specifies the minimum number of characters that must be entered before the search function becomes active. |\n| `:preventSearchFocus` | Inside `data-hs-select` | boolean | `false` | Sets autofocus for the search field inside a dropdown list if the value is `true`. |\n| `:preventSearchInsideDescription` | Inside `data-hs-select` | boolean | `false` | Prevents searching inside the description when set to `true`. |\n| `:mode` | Inside `data-hs-select` | `'default'` \\| `'tags'` | `'default'` | Define a select mode. `default` for single/multiple selection, `tags` for tag-based selection. |\n| `:scrollToSelected` | Inside `data-hs-select` | boolean | `false` | Scroll to the selected option when the dropdown is opened. |\n| `:value` | Inside `data-hs-select` | string \\| string[] | - | Defines the initial selected value. Use an array when the select is multiple. |\n| `:viewport` | Inside `data-hs-select` | string | - | Defines the viewport selector used when recalculating dropdown direction and available space. |\n| `:toggleTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the select toggle. Note: `data-title` is required if you are using a custom placeholder. |\n| `:toggleClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' toggle. CSS classes must be separated by a space. |\n| `:toggleSeparators` | Inside `data-hs-select` | object | - | Define separators for the selects' toggle. |\n| `:toggleSeparators:items` | Inside `data-hs-select` | string | `\", \"` | Define which separator will be used for separate selected items. |\n| `:toggleSeparators:betweenItemsAndCounter` | Inside `data-hs-select` | string | `\"and\"` | Define which separator will be used for separate selected items and counter text. |\n| `:toggleCountText` | Inside `data-hs-select` | string | - | This option is only available for multiple select. Determines what text will be after the counter. It also activates the counting mode. |\n| `:toggleCountTextPlacement` | Inside `data-hs-select` | `'postfix'` \\| `'prefix'` \\| `'postfix-no-space'` \\| `'prefix-no-space'` | `'postfix'` | Allows to specify where the text specified in the `toggleCountText` parameter will be located relative to the counter. |\n| `:toggleCountTextMinItems` | Inside `data-hs-select` | number | `1` | This option is only available for multiple select. Defines the minimum number of selected items at which the counting mode will be activated. |\n| `:toggleCountTextMode` | Inside `data-hs-select` | `'countAfterLimit'` \\| `'nItemsAndCount'` | `'countAfterLimit'` | This option is only available for multiple select. Controls the display of the contents of the button title. |\n| `:wrapperClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the selects' wrapper. CSS classes must be separated by a space. |\n| `:tagsItemTemplate` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define template for the single tag. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-remove` the target tag will be deleted when click on tag with this data attribute. |\n| `:tagsItemClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the single tags. CSS classes must be separated by a space. |\n| `:tagsInputId` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. This option is useful if there is a need to associate a `label` outside the initialized element with the tags input. |\n| `:tagsInputClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the input. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownTag` | Inside `data-hs-select` | string (HTML markup) | - | This option is only available when tags mode is set up. Define a markup for the dropdown. |\n| `:dropdownClasses` | Inside `data-hs-select` | string | - | This option is only available when tags mode is set up. Define CSS classes for the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:dropdownPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"top-left\"` \\| `\"top-right\"` \\| `\"bottom\"` \\| `\"bottom-left\"` \\| `\"bottom-right\"` \\| `\"right\"` \\| `\"right-top\"` \\| `\"right-bottom\"` \\| `\"left\"` \\| `\"left-top\"` \\| `\"left-bottom\"` | `\"bottom\"` | Specifies the position of the menu when opened. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownAutoPlacement` | Inside `data-hs-select` | boolean | `false` | Automatically determine the placement of the menu based on the available space. Requires the `Floating UI` and `dropdownScope` option to be `window`. |\n| `:dropdownVerticalFixedPlacement` | Inside `data-hs-select` | `\"top\"` \\| `\"bottom\"` \\| null | `null` | Specifies a fixed vertical position for the menu when opened. It will not change when scrolling. |\n| `:dropdownScope` | Inside `data-hs-select` | `\"window\"` \\| `\"parent\"` | `\"parent\"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. |\n| `:dropdownDirectionClasses` | Inside `data-hs-select` | `{ top?: string; bottom?: string; }` | `null` | Defines classes that are applied when the dropdown is placed above or below the toggle. |\n| `:dropdownSpace` | Inside `data-hs-select` | number | `10` | Defines the spacing in pixels between the toggle and dropdown. |\n| `:searchTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<input type=\\\"text\\\">\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search input. |\n| `:searchWrapperTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<div></div>\"` | This option is only available when `hasSearch: true`. Define a custom markup for the search wrapper. |\n| `:searchWrapperClasses` | Inside `data-hs-select` | string | `\"bg-white p-2 sticky top-0\"` | This option is only available when `hasSearch: true`. Define CSS classes for the search wrapper. |\n| `:searchId` | Inside `data-hs-select` | string | - | This option is only available when `hasSearch: true`. This option is useful if there is a need to associate a `label` outside the initialized element with the search input. |\n| `:searchLimit` | Inside `data-hs-select` | number | `Infinity` | This option is only available when `hasSearch: true`. If this option is enabled, the search will display only the first 'n' matching items. |\n| `:isSearchDirectMatch` | Inside `data-hs-select` | boolean | `true` | This option is only available when `hasSearch: true`. If the option is disabled, then in the search results you will be able to see non-direct matches by text. For example, if you entered `england` in the search field, then `Eng-land`, `Eng.land`, `Eng_land` will also be shown. |\n| `:searchClasses` | Inside `data-hs-select` | string | `\"block w-[calc(100%-32px)] text-sm border-gray-200 rounded-md focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 py-2 px-3 my-2 mx-4\"` | This option is only available when `:hasSearch` attribute is `true`. Define CSS classes for the search field inside the dropdown. Defines CSS classes for the search field inside a dropdown list. CSS classes must be separated by a space. |\n| `:searchPlaceholder` | Inside `data-hs-select` | string | `\"Search...\"` | This option is only available when `:hasSearch` attribute is `true`. Determines placeholder for the search field inside the dropdown. |\n| `:searchNoResultTemplate` | Inside `data-hs-select` | string (HTML markup) | `\"<span></span>\"` | This option is only available when `:hasSearch` attribute is `true`. Define a markup for the \"no result\" text. |\n| `:searchNoResultText` | Inside `data-hs-select` | string | `\"No options found...\"` | This option is only available when `:hasSearch` attribute is `true`. Defines the text that will be displayed if no results are found. |\n| `:searchNoResultClasses` | Inside `data-hs-select` | string | `\"px-4 text-sm\"` | This option is only available when `:hasSearch` attribute is `true`. Defines CSS classes for the \"no results\" wrapper. CSS classes must be separated by a space. |\n| `:optionAllowEmptyOption` | Inside `data-hs-select` | boolean | `false` | This option determines whether an empty option should be included in the list. When set to `true`, an additional option with an empty string (`\"\"`) as its value will be displayed, allowing users to select a blank choice if needed. |\n| `:optionTemplate` | Inside `data-hs-select` | string (HTML markup) | - | Define template for the single option. It could contain: `data-icon` if target option tag has `data-hs-select-option:icon`, `data-title` the text from the target option, `data-description` if target option tag has `data-hs-select-option:description`. |\n| `:optionTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option. |\n| `:optionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option. CSS classes must be separated by a space. |\n| `:optgroupTag` | Inside `data-hs-select` | string (HTML markup) | - | Define a markup for the single option group. |\n| `:optgroupClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the single option group. CSS classes must be separated by a space. |\n| `:extraMarkup` | Inside `data-hs-select` | string \\| array (HTML markup) | - | Define a markup that could be extra added to the wrapper of the select for the decoration reasons. If it contains the `--prevent-click` class, clicking on this element will not trigger the open function. |\n| `:descriptionClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the description inside the single option. CSS classes must be separated by a space. |\n| `:iconClasses` | Inside `data-hs-select` | string | - | Define CSS classes for the icon inside the single option. CSS classes must be separated by a space. |\n| `:isAddTagOnEnter` | Inside `data-hs-select` | boolean | `true` | Determines whether a tag will be added when the enter key is pressed. |\n| `:isSelectedOptionOnTop` | Inside `data-hs-select` | boolean | `false` | Controls whether selected options should appear at the top of the dropdown list. When set to `true`, selected options will be sorted to the top of the list. |\n| `:apiUrl` | Inside `data-hs-select` | string \\| null | `null` | Defines the address where the API is located. |\n| `:apiQuery` | Inside `data-hs-select` | string \\| null | `null` | Defines query parameters that are separated by `?`. |\n| `:apiOptions` | Inside `data-hs-select` | RequestInit \\| null | `null` | Defines options for the fetch function. |\n| `:apiDataPart` | Inside `data-hs-select` | string \\| null | `null` | If data is in some first level parameter, then it allows you to specify the name of this parameter to extract the data. |\n| `:apiSearchQueryKey` | Inside `data-hs-select` | string \\| null | `null` | Defines the key for the query search parameter. |\n| `:apiFieldsMap` | Inside `data-hs-select` | `{ id: string; title: string; val?: string; icon?: string \\| null; description?: string \\| null; page?: string; offset?: string; limit?: string; pageStart?: string; totalPath?: string; [key: string]: unknown; } \\| null` | `null` | Allows you to convert fields from the API to the fields required for the plugin to work. `page`, `offset`, and `limit` customize pagination parameter names for remote data. |\n| `:apiLoadMore` | Inside `data-hs-select` | `boolean \\| { perPage?: number; scrollThreshold?: number; }` | `false` | Defines the options for the load more feature. |\n| `:apiPageStart` | Inside `data-hs-select` | number | - | Defines the first page or offset value used for remote option loading. If omitted, page-based loading starts from `1` and offset-based loading starts from `0`. |\n| `:apiTotalPath` | Inside `data-hs-select` | string \\| null | `null` | Defines the response path used to read the total result count for remote option loading. |\n| `:apiSelectedValues` | Inside `data-hs-select` | `string \\| string[] \\| null` | `null` | Allows to preselect values when loading options from an API. Can be a single value or an array of values for multiple selection. |\n| `:apiIconTag` | Inside `data-hs-select` | string \\| null | `null` | Allows to define an `img` tag that will be used when rendering options and in the trigger button. |\n| `data-hs-select-option` | Option element | object | - | Allows to define the parameters for the single option. Should be added to the option. |\n| `:description` | Inside `data-hs-select-option` | string | - | Allows to define the description of the option inside the element that have `data-description` attribute. |\n| `:icon` | Inside `data-hs-select-option` | string | - | Allows to define the icon of the option inside the element that have `data-icon` attribute. |\n| `:additionalClasses` | Inside `data-hs-select-option` | `[string, string[]][]` | - | Allows to define additional classes. The first string is the selector of the element to apply the classes to, the second string is an array of classes to apply. If the selector is set to `null`, the classes will be applied to the option itself. |\n| `:apiFields` | Inside `data-hs-select-option` | `{ [key: string]: unknown; }` | - | Allows to define the fields of the option from the API. |\n\n### Tailwind Modifiers\n\n| Name | Description |\n| --- | --- |\n| `hs-select-opened:*` | A modifier that allows you to set Tailwind classes when select has been opened. |\n| `hs-select-disabled:*` | A modifier that allows you to set Tailwind classes when select has \"disabled\" attribute. |\n\n## JavaScript API\n\nThe `HSSelect` object is available in the global `window` object after the plugin is loaded.\n\n### Instance Methods\n\nThese methods are called on a select instance.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `open()` | None | `void` | Opens the dropdown list programmatically. |\n| `close()` | None | `void` | Closes the dropdown list programmatically. |\n| `setValue(value)` | `value`: `string \\| string[]` | `void` | Set the select value. This should be a string for a simple select and an array if it's a multiple select. |\n| `addOption(items)` | `items`: `{ title: string; val: string; options?: { description: string; icon: string; }; }` or array of such objects | `void` | Adds an option (several possible) to the select. The single option should follow this interface. |\n| `removeOption(val)` | `val`: `string \\| string[]` | `void` | Removes an option by value (several possible). |\n| `recalculateDirection()` | None | `void` | Force recalculation for dropdown list. Useful when dropdown position needs to be updated after layout changes. |\n| `destroy()` | None | `void` | Destroys the select instance, removes all generated markup, classes, and event listeners. Use when removing select from DOM. |\n\n### Static Methods\n\nThese methods are called directly on the `HSSelect` class.\n\n| Method | Parameters | Return Type | Description |\n| --- | --- | --- | --- |\n| `HSSelect.getInstance(target, isInstance)` | `target`: `HTMLElement \\| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HTMLElement \\| { id: string \\| number, element: HSSelect } \\| null` | Returns the select instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSSelect` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if select instance is not found. |\n\n### Usage Examples\n\n**Example 1: Opening and closing dropdown**\n```javascript\n// Get the select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Open dropdown\n  element.open();\n\n  // Close dropdown\n  element.close();\n}\n```\n\n**Example 2: Setting value programmatically**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Set value for single select\n  element.setValue('option-value');\n\n  // Set value for multiple select\n  element.setValue(['value-1', 'value-2']);\n}\n```\n\n**Example 3: Adding and removing options**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Add option\n  element.addOption({\n    title: 'New Option',\n    val: 'new-option',\n    options: {\n      description: 'Option description',\n      icon: '<svg>...</svg>'\n    }\n  });\n\n  // Remove option\n  element.removeOption('new-option');\n}\n```\n\n**Example 4: Destroying select instance**\n```javascript\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n  const removeBtn = document.querySelector('#hs-remove-btn');\n\n  removeBtn.addEventListener('click', () => {\n    // Clean up before removing from DOM\n    element.destroy();\n    // Now safe to remove the element\n    document.querySelector('#hs-select').remove();\n  });\n}\n```\n\n## Events\n\nSelect instances emit events that can be listened to for selection lifecycle hooks.\n\n| Event Name | When Fired | Callback Parameter | Description |\n| --- | --- | --- | --- |\n| `on:change` | When the selected value changes | `string | string[]` (new selected value; array in multi-select / tags mode) | Fires after the user picks an option or removes a tag. |\n| `on:request` | Right before an `apiUrl` request is sent (search or `apiLoadMore` pagination) | `{ url: string }` | Useful for showing a loading indicator. |\n| `on:requestCompleted` | After an `apiUrl` request settles, success or failure | `{ url: string, data?: unknown, error?: unknown }` | `data` holds the parsed response on success, `error` is set on failure. Not fired when a request is superseded/aborted (e.g. a newer search query). |\n\nBoth are also dispatched as DOM `CustomEvent`s (`request.hs.select` / `requestCompleted.hs.select`) with the same payload under `event.detail.payload`, so they can be listened to via `addEventListener` as well as `element.on(...)`.\n\n### Event Usage Example\n\n```javascript\n// Get select instance\nconst instance = HSSelect.getInstance('#hs-select', true);\n\nif (instance) {\n  const { element } = instance;\n\n  // Listen for selection changes\n  element.on('change', (value) => {\n    console.log('Selected value:', value);\n    // value is a string for single-select, string[] for multi-select / tags mode\n  });\n\n  // Listen for API request lifecycle (only relevant when `apiUrl` is set)\n  element.on('request', ({ url }) => {\n    console.log('Requesting:', url);\n    // e.g. show a loading spinner\n  });\n\n  element.on('requestCompleted', ({ data, error }) => {\n    if (error) console.error('Request failed:', error);\n    else console.log('Request completed with:', data);\n    // e.g. hide the loading spinner\n  });\n}\n```\n\n## Common Patterns\n\n### Pattern 1: Search Enabled\n\nEnable search functionality in dropdown.\n\n```html\n<select data-hs-select='{\n  \"hasSearch\": true,\n  \"searchPlaceholder\": \"Search options...\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 2: Tags Mode\n\nUse tags mode for multiple selections.\n\n```html\n<select multiple data-hs-select='{\n  \"mode\": \"tags\"\n}'>\n  <!-- Options -->\n</select>\n```\n\n### Pattern 3: API Integration\n\nLoad options from API.\n\n```html\n<select data-hs-select='{\n  \"apiUrl\": \"/api/options\",\n  \"apiFieldsMap\": {\n    \"id\": \"id\",\n    \"title\": \"name\",\n    \"val\": \"value\"\n  }\n}'>\n  <!-- Options -->\n</select>\n```\n\n## License\n\nCopyright (c) 2024 Htmlstream.\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT).\n\n<!-- ... +9 more modular skills available via /api/skills/htmlstreamofficial/preline?path={path} -->\n</yakaai_skills>"}