@zealakit/disclosure-vanilla 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,191 @@
1
- # Temporary Holding Version
1
+ # @zealakit/disclosure-vanilla
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [English](https://github.com/zealakit/disclosure/blob/main/packages/vanilla/README.md) | [Tiếng Việt](https://github.com/zealakit/disclosure/blob/main/packages/vanilla/README_VI.md) | [中文](https://github.com/zealakit/disclosure/blob/main/packages/vanilla/README_CN.md)
4
+
5
+ ![Disclosure cover](https://github.com/zealakit/disclosure/blob/main/assets/images/disclosure-cover.jpg?raw=true)
6
+
7
+ Headless Collapsible and Accordion for vanilla JavaScript and static HTML. Provides state, accessibility, and a built-in height animation — no React, Vue, or Svelte required. You own visual styling.
8
+
9
+ Author semantic HTML with `data-*` attributes, then call `createDisclosure()` once. Put padding, borders, and backgrounds on the content element’s class (moved onto `[data-collapsible-content-wrapper]` at init) so exit animations collapse cleanly.
10
+
11
+ > **Note**
12
+ >
13
+ > Disclosure is not a replacement for a full UI library. Use it when Accordion or Collapsible in your kit is hard to restyle — or when you only need that one primitive and don’t want to pull in a large design system.
14
+
15
+ ## Install
16
+
17
+ ### npm / bundler
18
+
19
+ ```bash
20
+ npm i @zealakit/disclosure-vanilla
21
+ # or: pnpm add / yarn add / bun add
22
+ ```
23
+
24
+ No peer dependencies. Works with a bundler (Vite, esbuild, …) or a browser import map pointing at the ESM build.
25
+
26
+ ### CDN (static HTML)
27
+
28
+ Pin a published version in production. Do **not** use unversioned `latest` CDN URLs.
29
+
30
+ Published browser artifacts:
31
+
32
+ | File | Use |
33
+ | --- | --- |
34
+ | `dist/index.js` | ESM (bundlers, import maps, jsDelivr `/+esm`, esm.sh) |
35
+ | `dist/index.d.ts` | TypeScript declarations |
36
+ | `dist/index.global.js` | IIFE script tag (readable) |
37
+ | `dist/index.global.min.js` | IIFE script tag (minified, preferred for CDN) |
38
+
39
+ Global namespace for the IIFE build: **`ZealakitDisclosure`**.
40
+
41
+ #### 1. Native ESM CDN
42
+
43
+ ```html
44
+ <script type="module">
45
+ import { createDisclosure } from "https://cdn.jsdelivr.net/npm/@zealakit/disclosure-vanilla@1.0.0/+esm";
46
+
47
+ const disclosure = createDisclosure();
48
+ </script>
49
+ ```
50
+
51
+ Also works with esm.sh:
52
+
53
+ ```html
54
+ <script type="module">
55
+ import { createDisclosure } from "https://esm.sh/@zealakit/disclosure-vanilla@1.0.0";
56
+
57
+ const disclosure = createDisclosure();
58
+ </script>
59
+ ```
60
+
61
+ #### 2. Direct script CDN (IIFE)
62
+
63
+ ```html
64
+ <script src="https://cdn.jsdelivr.net/npm/@zealakit/disclosure-vanilla@1.0.0/dist/index.global.min.js"></script>
65
+ <script>
66
+ const disclosure = ZealakitDisclosure.createDisclosure();
67
+ </script>
68
+ ```
69
+
70
+ Unminified IIFE (debugging): `…/dist/index.global.js`.
71
+
72
+ ## Quick start
73
+
74
+ ```html
75
+ <div data-disclosure>
76
+ <button type="button" data-disclosure-trigger aria-expanded="false">
77
+ Toggle content
78
+ </button>
79
+ <div id="content-1" data-disclosure-content hidden>
80
+ Content
81
+ </div>
82
+ </div>
83
+
84
+ <script type="module">
85
+ import { createDisclosure } from "@zealakit/disclosure-vanilla";
86
+ const disclosure = createDisclosure();
87
+ // disclosure.refresh() after inserting new markup
88
+ // disclosure.destroy() when tearing down
89
+ </script>
90
+ ```
91
+
92
+ ## Public API
93
+
94
+ | Export | Description |
95
+ | --- | --- |
96
+ | `createDisclosure(root?)` | Initialize all `[data-disclosure]` / `[data-accordion]` under `document` or a root node |
97
+ | `createDisclosure({ root })` | Same, options object form |
98
+ | `DisclosureController.destroy()` | Remove listeners and restore content nodes |
99
+ | `DisclosureController.refresh()` | Destroy + re-scan (for dynamically inserted markup) |
100
+ | `DEFAULT_ANIMATION_*` | Same animation defaults as framework packages |
101
+ | `resolveAnimationConfig` / `getCollapsibleAnimationStyle` | Shared animation helpers |
102
+
103
+ ## Collapsible markup
104
+
105
+ | Attribute | On | Description |
106
+ | --- | --- | --- |
107
+ | `data-disclosure` | root | Marks a Collapsible root |
108
+ | `data-disclosure-trigger` | `<button type="button">` | Toggle control |
109
+ | `data-disclosure-content` | content region | Panel body |
110
+ | `data-default-open` | root | Start expanded (`false` by default) |
111
+ | `data-disabled` | root | Disable interaction |
112
+ | `data-force-mount` | content | Keep content in the DOM while closed |
113
+ | `data-animated` | root | `"false"` disables height animation (default on) |
114
+ | `data-animation-timing-function` | root | `linear` \| `ease-in-out` (default `ease-in-out`) |
115
+ | `data-animation-duration` | root | Duration ms (default `200`) |
116
+ | `data-animation-delay` | root | Delay ms (default `0`) |
117
+
118
+ Root attributes after init: `data-slot="collapsible"`, `data-state="open" \| "closed"`, `data-disabled` when disabled. Triggers get `aria-expanded` / `aria-controls`.
119
+
120
+ ## Accordion markup
121
+
122
+ ```html
123
+ <div data-accordion data-accordion-type="single" data-collapsible>
124
+ <section data-accordion-item data-value="item-1">
125
+ <h3>
126
+ <button type="button" data-accordion-trigger>
127
+ First item
128
+ </button>
129
+ </h3>
130
+ <div id="accordion-content-1" data-accordion-content hidden>
131
+ First content
132
+ </div>
133
+ </section>
134
+ </div>
135
+ ```
136
+
137
+ | Attribute | On | Description |
138
+ | --- | --- | --- |
139
+ | `data-accordion` | root | Marks an Accordion root |
140
+ | `data-accordion-type` | root | `single` (default) \| `multiple` |
141
+ | `data-collapsible` | root | Allow closing the open item in single mode |
142
+ | `data-default-value` | root | Initial value(s); space/comma-separated for `multiple` |
143
+ | `data-disabled` | root or item | Disable root or a single item |
144
+ | `data-accordion-item` + `data-value` | item | Stable item id |
145
+ | `data-accordion-trigger` | `<button>` | Item trigger (prefer inside `h2`–`h6`) |
146
+ | `data-accordion-content` | panel | Item content region |
147
+ | Animation attrs | root | Same as Collapsible |
148
+
149
+ Keyboard (enabled triggers): `ArrowDown` / `ArrowUp` / `Home` / `End`. Nested accordions are independent.
150
+
151
+ ## Animation
152
+
153
+ Height animation uses CSS `grid-template-rows` (`minmax(0, 0fr)` ↔ `minmax(0, 1fr)`), enabled by default. `prefers-reduced-motion: reduce` disables motion. Content stays mounted through the exit transition, then detaches unless `data-force-mount` is set.
154
+
155
+ CSS variables on the content viewport:
156
+
157
+ - `--collapsible-animation-duration`
158
+ - `--collapsible-animation-easing`
159
+ - `--collapsible-animation-delay`
160
+
161
+ ## Styling
162
+
163
+ Headless — no themes or icons. Style via your CSS and:
164
+
165
+ - `data-slot` — `collapsible`, `collapsible-trigger`, `collapsible-content`, `accordion`, `accordion-item`, `accordion-heading`, `accordion-trigger`, `accordion-content`
166
+ - `data-state` — `open` \| `closed`
167
+ - `data-disabled`
168
+
169
+ Put chrome on the content element’s class/style (applied to `[data-collapsible-content-wrapper]`).
170
+
171
+ ## Lifecycle
172
+
173
+ ```ts
174
+ const disclosure = createDisclosure(document.getElementById("app"));
175
+
176
+ // After dynamically inserting markup:
177
+ disclosure.refresh();
178
+
179
+ // On page teardown / SPA unmount:
180
+ disclosure.destroy();
181
+ ```
182
+
183
+ Calling `createDisclosure()` twice on the same roots does not register duplicate listeners. Use `refresh()` or `destroy()` + `createDisclosure()` when markup changes.
184
+
185
+ ## Agent skill
186
+
187
+ Practical agent instructions: [skills/disclosure-vanilla.md](https://github.com/zealakit/disclosure/blob/main/skills/disclosure-vanilla.md)
188
+
189
+ ## License
190
+
191
+ MIT © Zealakit
@@ -0,0 +1,5 @@
1
+ export type AccordionInstance = {
2
+ root: HTMLElement;
3
+ destroy: () => void;
4
+ };
5
+ export declare function initAccordion(root: HTMLElement): AccordionInstance;
@@ -0,0 +1,38 @@
1
+ export type AnimationTimingFunction = "linear" | "ease-in-out";
2
+ export type CollapsibleAnimationProps = {
3
+ /**
4
+ * Whether expand/collapse uses the built-in height animation.
5
+ * @default true
6
+ */
7
+ animated?: boolean;
8
+ /**
9
+ * CSS timing function for expand/collapse.
10
+ * @default "ease-in-out"
11
+ */
12
+ animationTimingFunction?: AnimationTimingFunction;
13
+ /**
14
+ * Transition duration in milliseconds.
15
+ * @default 200
16
+ */
17
+ animationDuration?: number;
18
+ /**
19
+ * Transition delay in milliseconds.
20
+ * @default 0
21
+ */
22
+ animationDelay?: number;
23
+ };
24
+ export type ResolvedAnimationConfig = {
25
+ animated: boolean;
26
+ animationTimingFunction: AnimationTimingFunction;
27
+ animationDuration: number;
28
+ animationDelay: number;
29
+ };
30
+ export declare const DEFAULT_ANIMATION_DURATION_MS = 200;
31
+ export declare const DEFAULT_ANIMATION_DELAY_MS = 0;
32
+ export declare const DEFAULT_ANIMATION_TIMING_FUNCTION: AnimationTimingFunction;
33
+ export declare function resolveAnimationConfig(props?: CollapsibleAnimationProps): ResolvedAnimationConfig;
34
+ export declare function getCollapsibleAnimationStyle(config: Pick<ResolvedAnimationConfig, "animationTimingFunction" | "animationDuration" | "animationDelay">): {
35
+ "--collapsible-animation-duration": string;
36
+ "--collapsible-animation-easing": AnimationTimingFunction;
37
+ "--collapsible-animation-delay": string;
38
+ };
@@ -0,0 +1,5 @@
1
+ export type CollapsibleInstance = {
2
+ root: HTMLElement;
3
+ destroy: () => void;
4
+ };
5
+ export declare function initCollapsible(root: HTMLElement): CollapsibleInstance;
@@ -0,0 +1,31 @@
1
+ export type CreateDisclosureOptions = {
2
+ /**
3
+ * Scope initialization to this subtree. Defaults to `document`.
4
+ */
5
+ root?: ParentNode | null;
6
+ };
7
+ export type DisclosureController = {
8
+ /**
9
+ * Tear down all managed instances and restore content nodes.
10
+ */
11
+ destroy: () => void;
12
+ /**
13
+ * Destroy current instances and re-scan the root for markup.
14
+ * Use after dynamically inserting disclosure/accordion HTML.
15
+ */
16
+ refresh: () => void;
17
+ };
18
+ /**
19
+ * Initialize all `[data-disclosure]` and `[data-accordion]` trees under `root`.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { createDisclosure } from "@zealakit/disclosure-vanilla";
24
+ *
25
+ * const disclosure = createDisclosure();
26
+ * // later…
27
+ * disclosure.refresh();
28
+ * disclosure.destroy();
29
+ * ```
30
+ */
31
+ export declare function createDisclosure(options?: CreateDisclosureOptions | ParentNode | null): DisclosureController;
package/dist/dom.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export declare function nextId(prefix: string): string;
2
+ export declare function parseBooleanAttribute(element: Element, name: string, defaultValue?: boolean): boolean;
3
+ export declare function parseNumberAttribute(element: Element, name: string): number | undefined;
4
+ export declare function setDataState(element: Element, open: boolean): void;
5
+ export declare function setDisabledAttr(element: Element, disabled: boolean): void;
6
+ export declare function setInert(element: HTMLElement, inert: boolean): void;
7
+ /**
8
+ * Find descendants matching `selector` that belong to `owner` via `closestAttr`,
9
+ * without descending into nested owners with the same attribute.
10
+ */
11
+ export declare function queryOwned(owner: Element, closestAttr: string, selector: string): HTMLElement[];
12
+ export declare function ensureContentWrapper(content: HTMLElement): HTMLElement;
13
+ export type PresenceController = {
14
+ sync: (open: boolean) => void;
15
+ destroy: () => void;
16
+ };
17
+ /**
18
+ * Mirrors framework presence: keep content mounted through exit animation,
19
+ * then detach from the DOM unless forceMount is set.
20
+ */
21
+ export declare function createPresence(options: {
22
+ content: HTMLElement;
23
+ animated: boolean;
24
+ forceMount: boolean;
25
+ getOpen: () => boolean;
26
+ }): PresenceController;
@@ -0,0 +1,4 @@
1
+ export { createDisclosure, type CreateDisclosureOptions, type DisclosureController, } from "./create-disclosure";
2
+ export type { AnimationTimingFunction, CollapsibleAnimationProps, ResolvedAnimationConfig, } from "./animation";
3
+ export { DEFAULT_ANIMATION_DELAY_MS, DEFAULT_ANIMATION_DURATION_MS, DEFAULT_ANIMATION_TIMING_FUNCTION, getCollapsibleAnimationStyle, resolveAnimationConfig, } from "./animation";
4
+ export { ensureCollapsibleStyles, prefersReducedMotion } from "./styles";