@magic-spells/collapsible-content 0.2.0 → 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,6 +1,6 @@
1
1
  # Collapsible Content Web Component
2
2
 
3
- A lightweight, customizable Web Component for creating accessible collapsible content sections. Perfect for FAQs, accordions, or any content that needs to be toggled.
3
+ A lightweight, accessible Web Component for creating collapsible content sections. Perfect for FAQs, accordions, or any content that needs to be toggled.
4
4
 
5
5
  [**Live Demo**](https://magic-spells.github.io/collapsible-content/demo/)
6
6
 
@@ -8,10 +8,10 @@ A lightweight, customizable Web Component for creating accessible collapsible co
8
8
 
9
9
  - No dependencies
10
10
  - Lightweight
11
- - Follows accessibility best practices
12
- - Smooth open/close animations
13
- - Uses proper ARIA attributes
14
- - Keyboard accessible
11
+ - Accessible (ARIA attributes, keyboard support, focus management)
12
+ - Smooth animations with `prefers-reduced-motion` support
13
+ - Prevents rapid click issues during animation
14
+ - Safe to import multiple times (no double-registration errors)
15
15
 
16
16
  ## Installation
17
17
 
@@ -20,7 +20,6 @@ npm install @magic-spells/collapsible-content
20
20
  ```
21
21
 
22
22
  ```javascript
23
- // Add to your JavaScript file
24
23
  import '@magic-spells/collapsible-content';
25
24
  ```
26
25
 
@@ -34,86 +33,84 @@ Or include directly in your HTML:
34
33
 
35
34
  ```html
36
35
  <collapsible-component>
37
- <button aria-expanded="false">Product Information</button>
36
+ <button type="button">Product Information</button>
38
37
  <collapsible-content>
39
38
  <div class="content-wrapper">
40
39
  <h3>Details</h3>
41
- <p>
42
- This product is made with 100% organic materials. It's durable, eco-friendly, and
43
- stylish.
44
- </p>
40
+ <p>This product is made with 100% organic materials.</p>
45
41
  </div>
46
42
  </collapsible-content>
47
43
  </collapsible-component>
48
44
  ```
49
45
 
50
- ## How It Works
46
+ ### Start Expanded
51
47
 
52
- - The collapsible content is initially hidden (unless `aria-expanded="true"` is set on the button)
53
- - Clicking the button toggles the visibility of the content
54
- - The component handles all the ARIA attributes for accessibility
55
- - Smooth animations are provided for opening and closing
48
+ Add the `open` attribute to start with content visible:
56
49
 
57
- ## Customization
50
+ ```html
51
+ <collapsible-component>
52
+ <button type="button">Already Open</button>
53
+ <collapsible-content open>
54
+ <p>This content is visible by default.</p>
55
+ </collapsible-content>
56
+ </collapsible-component>
57
+ ```
58
58
 
59
- ### Styling
59
+ ## Customization
60
60
 
61
- You can style the collapsible component using CSS custom properties:
61
+ Customize the animation with CSS custom properties:
62
62
 
63
63
  ```css
64
- :root {
65
- /* Layout */
66
- --cc-component-display: block;
67
- --cc-button-width: 100%;
68
- --cc-button-text-align: left;
69
-
70
- /* Appearance */
71
- --cc-button-background: none;
72
- --cc-button-border: none;
73
- --cc-button-cursor: pointer;
74
-
75
- /* Animation */
76
- --cc-content-padding: 10px;
77
- --cc-transition-duration: 0.5s;
78
- --cc-transition-timing: ease-in-out;
64
+ collapsible-content {
65
+ --collapsible-duration: 0.5s;
66
+ --collapsible-easing: ease-in-out;
79
67
  }
80
68
  ```
81
69
 
82
- ### SCSS Integration
70
+ | Property | Default | Description |
71
+ |----------|---------|-------------|
72
+ | `--collapsible-duration` | `0.35s` | Animation duration |
73
+ | `--collapsible-easing` | `ease-out` | Animation timing function |
83
74
 
84
- For more advanced customization, you can import the SCSS directly:
75
+ ## Events
85
76
 
86
- ```scss
87
- // Option 1: Import the compiled CSS
88
- @import '@magic-spells/collapsible-content/css';
77
+ ### collapsible-error
89
78
 
90
- // Option 2: Import the SCSS and override variables
91
- @use '@magic-spells/collapsible-content/scss' with (
92
- $transition-duration: 0.5s,
93
- $transition-timing: ease-in-out,
94
- $content-padding: 10px
95
- );
79
+ Fired when the component is missing required children:
96
80
 
97
- // Option 3: Import specific parts
98
- @use '@magic-spells/collapsible-content/scss/variables' with (
99
- $button-text-align: center
100
- );
101
- @use '@magic-spells/collapsible-content/scss/collapsible-content';
81
+ ```javascript
82
+ document.addEventListener('collapsible-error', (e) => {
83
+ console.error('Collapsible setup failed:', e.detail.error);
84
+ });
85
+ ```
86
+
87
+ ## Programmatic Control
88
+
89
+ Access the `collapsed` property on the `<collapsible-content>` element:
90
+
91
+ ```javascript
92
+ const content = document.querySelector('collapsible-content');
93
+ content.collapsed = true; // collapse
94
+ content.collapsed = false; // expand
95
+ console.log(content.collapsed); // get current state
102
96
  ```
103
97
 
104
98
  ## Accessibility
105
99
 
106
- This component follows WCAG guidelines for accessible accordions:
100
+ This component follows WCAG guidelines:
107
101
 
108
- - Uses proper `aria-expanded` and `aria-controls` attributes
109
- - Content regions have proper `role="region"` and `aria-labelledby` attributes
110
- - Keyboard accessible (toggle with Space or Enter key)
111
- - Focus management for keyboard users
102
+ - `aria-expanded` on button indicates current state
103
+ - `aria-controls` links button to content
104
+ - `role="region"` and `aria-labelledby` on content
105
+ - `aria-hidden` and `inert` when collapsed
106
+ - Keyboard accessible (Space/Enter to toggle)
107
+ - Focus-visible styling for keyboard users
108
+ - Respects `prefers-reduced-motion`
112
109
 
113
110
  ## Browser Support
114
111
 
115
- This component works in all modern browsers that support Web Components.
112
+ Modern browsers with Web Components support (Chrome, Firefox, Safari, Edge).
116
113
 
117
114
  ## License
118
115
 
119
- MIT
116
+ MIT
@@ -5,6 +5,7 @@
5
5
  */
6
6
  class CollapsibleComponent extends HTMLElement {
7
7
  #handleClick;
8
+ #abortController;
8
9
 
9
10
  /**
10
11
  * Initializes the component and sets up references to child elements
@@ -23,7 +24,7 @@ class CollapsibleComponent extends HTMLElement {
23
24
  // toggle expanded value
24
25
  const expanded = _.button.getAttribute('aria-expanded') !== 'true';
25
26
  _.button.setAttribute('aria-expanded', expanded);
26
- _.content.hidden = !expanded;
27
+ _.content.collapsed = !expanded;
27
28
  };
28
29
  }
29
30
 
@@ -39,7 +40,16 @@ class CollapsibleComponent extends HTMLElement {
39
40
  _.content = _.querySelector('collapsible-content');
40
41
 
41
42
  if (!_.button || !_.content) {
42
- console.error('CollapsibleComponent requires a <button> and a <collapsible-content>.');
43
+ const error = new Error(
44
+ 'CollapsibleComponent requires a <button> and a <collapsible-content>.'
45
+ );
46
+ console.error(error.message);
47
+ _.dispatchEvent(
48
+ new CustomEvent('collapsible-error', {
49
+ bubbles: true,
50
+ detail: { error },
51
+ })
52
+ );
43
53
  return;
44
54
  }
45
55
 
@@ -48,18 +58,20 @@ class CollapsibleComponent extends HTMLElement {
48
58
  _.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;
49
59
 
50
60
  // set accessibility attributes
61
+ _.button.type ||= 'button';
51
62
  _.button.setAttribute('aria-controls', _.content.id);
52
- _.content.setAttribute('role', 'region');
53
63
  _.content.setAttribute('aria-labelledby', _.button.id);
54
64
 
55
- // set initial state based on aria-expanded attribute
56
- const expanded = _.button.getAttribute('aria-expanded') === 'true';
65
+ // set initial state based on open attribute
66
+ const open = _.content.hasAttribute('open');
67
+ _.button.setAttribute('aria-expanded', open);
68
+ _.content.collapsed = !open;
57
69
 
58
- // make sure content state matches button state
59
- _.content.hidden = !expanded;
60
-
61
- // add event listener
62
- _.button.addEventListener('click', _.#handleClick);
70
+ // use AbortController to prevent duplicate listeners on reconnection
71
+ _.#abortController = new AbortController();
72
+ _.button.addEventListener('click', _.#handleClick, {
73
+ signal: _.#abortController.signal,
74
+ });
63
75
  }
64
76
 
65
77
  /**
@@ -68,8 +80,9 @@ class CollapsibleComponent extends HTMLElement {
68
80
  */
69
81
  disconnectedCallback() {
70
82
  const _ = this;
71
- if (_.button) {
72
- _.button.removeEventListener('click', _.#handleClick);
83
+ if (_.#abortController) {
84
+ _.#abortController.abort();
85
+ _.#abortController = null;
73
86
  }
74
87
  }
75
88
  }
@@ -79,6 +92,8 @@ class CollapsibleComponent extends HTMLElement {
79
92
  */
80
93
  class CollapsibleContent extends HTMLElement {
81
94
  #handleTransitionEnd;
95
+ #abortController;
96
+ #animating = false;
82
97
 
83
98
  /**
84
99
  * Initializes the content element and binds event handlers
@@ -90,35 +105,30 @@ class CollapsibleContent extends HTMLElement {
90
105
  // define event handler using arrow function for proper binding
91
106
  _.#handleTransitionEnd = (event) => {
92
107
  // exit if isn't height property
93
- if (event.propertyName != 'height') return;
108
+ if (event.propertyName !== 'height') return;
109
+
110
+ _.#animating = false;
94
111
 
95
112
  // remove the inline height to allow dynamic content changes
96
- if (!_.hidden) {
113
+ if (!_.collapsed) {
97
114
  _.style.height = 'auto';
98
115
  }
99
116
  };
100
-
101
- // add event listener to remove height after transition
102
- _.addEventListener('transitionend', _.#handleTransitionEnd);
103
117
  }
104
118
 
105
119
  /**
106
120
  * Called when element is added to the DOM
107
- * Sets initial height based on hidden state
121
+ * Sets initial height based on open attribute
108
122
  */
109
123
  connectedCallback() {
110
124
  const _ = this;
125
+ _.style.height = _.hasAttribute('open') ? 'auto' : '0';
111
126
 
112
- // get aria-expanded state from button
113
- const component = _.closest('collapsible-component');
114
- const button = component.querySelector('button');
115
- const expanded = button.getAttribute('aria-expanded') === 'true';
116
-
117
- if (expanded) {
118
- _.style.height = 'auto';
119
- } else {
120
- _.style.height = '0';
121
- }
127
+ // use AbortController to prevent duplicate listeners on reconnection
128
+ _.#abortController = new AbortController();
129
+ _.addEventListener('transitionend', _.#handleTransitionEnd, {
130
+ signal: _.#abortController.signal,
131
+ });
122
132
  }
123
133
 
124
134
  /**
@@ -127,57 +137,74 @@ class CollapsibleContent extends HTMLElement {
127
137
  */
128
138
  disconnectedCallback() {
129
139
  const _ = this;
130
- _.removeEventListener('transitionend', _.#handleTransitionEnd);
140
+ if (_.#abortController) {
141
+ _.#abortController.abort();
142
+ _.#abortController = null;
143
+ }
131
144
  }
132
145
 
133
146
  /**
134
- * Handles setting the hidden attribute with animation
135
- * @param {boolean} value - Whether element should be hidden
147
+ * Handles setting the collapsed state with animation
148
+ * @param {boolean} value - Whether element should be collapsed
136
149
  */
137
- set hidden(value) {
150
+ set collapsed(value) {
138
151
  const _ = this;
139
152
 
140
- // animate to closed
153
+ // prevent rapid clicking during animation
154
+ if (_.#animating) return;
155
+
156
+ // check if transitions are enabled (respects prefers-reduced-motion)
157
+ const hasTransition = getComputedStyle(_).transitionDuration !== '0s';
158
+
141
159
  if (value) {
142
- // reset height to animate from
160
+ if (hasTransition) {
161
+ _.#animating = true;
162
+ }
163
+
164
+ // animate to closed
143
165
  _.style.height = `${_.scrollHeight}px`;
144
166
 
145
- // wait one frame and animate to 0
146
- setTimeout(() => {
147
- _.style.height = '0px';
148
- }, 1);
167
+ // use requestAnimationFrame for reliable frame timing
168
+ requestAnimationFrame(() => {
169
+ requestAnimationFrame(() => {
170
+ _.style.height = '0px';
171
+ });
172
+ });
149
173
 
150
- // set accessibility attributes
174
+ _.removeAttribute('open');
151
175
  _.setAttribute('aria-hidden', 'true');
152
176
  _.setAttribute('inert', '');
153
- _.setAttribute('hidden', '');
154
177
  } else {
155
- // only animate if not set to auto
178
+ // only animate if not already auto
156
179
  if (_.style.height !== 'auto') {
157
- // animate to open
180
+ if (hasTransition) {
181
+ _.#animating = true;
182
+ }
158
183
  _.style.height = `${_.scrollHeight}px`;
159
184
  }
160
185
 
161
- // remove accessibility attributes
186
+ _.setAttribute('open', '');
162
187
  _.removeAttribute('aria-hidden');
163
188
  _.removeAttribute('inert');
164
- _.removeAttribute('hidden');
165
189
  }
166
190
  }
167
191
 
168
192
  /**
169
- * Gets the hidden state from the attribute
170
- * @returns {boolean} Whether element is hidden
193
+ * Gets the collapsed state
194
+ * @returns {boolean} Whether element is collapsed
171
195
  */
172
- get hidden() {
173
- const _ = this;
174
- return _.hasAttribute('hidden');
196
+ get collapsed() {
197
+ return !this.hasAttribute('open');
175
198
  }
176
199
  }
177
200
 
178
- // register custom elements
179
- customElements.define('collapsible-content', CollapsibleContent);
180
- customElements.define('collapsible-component', CollapsibleComponent);
201
+ // register custom elements if not already defined
202
+ if (!customElements.get('collapsible-content')) {
203
+ customElements.define('collapsible-content', CollapsibleContent);
204
+ }
205
+ if (!customElements.get('collapsible-component')) {
206
+ customElements.define('collapsible-component', CollapsibleComponent);
207
+ }
181
208
 
182
209
  exports.CollapsibleComponent = CollapsibleComponent;
183
210
  exports.CollapsibleContent = CollapsibleContent;
@@ -1 +1 @@
1
- {"version":3,"file":"collapsible-content.cjs.js","sources":["../src/collapsible-content.js"],"sourcesContent":["import './index.scss';\n\n/**\n * Custom element that creates a collapsible/expandable component with proper accessibility\n */\nclass CollapsibleComponent extends HTMLElement {\n\t#handleClick;\n\n\t/**\n\t * Initializes the component and sets up references to child elements\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// store references to elements once to avoid re-querying\n\t\t_.button = null;\n\t\t_.content = null;\n\n\t\t// define click handler with proper this binding\n\t\t_.#handleClick = (e) => {\n\t\t\te.preventDefault();\n\t\t\t// toggle expanded value\n\t\t\tconst expanded = _.button.getAttribute('aria-expanded') !== 'true';\n\t\t\t_.button.setAttribute('aria-expanded', expanded);\n\t\t\t_.content.hidden = !expanded;\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets up accessibility attributes and event listeners\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// initialize element references once\n\t\t_.button = _.querySelector('button');\n\t\t_.content = _.querySelector('collapsible-content');\n\n\t\tif (!_.button || !_.content) {\n\t\t\tconsole.error('CollapsibleComponent requires a <button> and a <collapsible-content>.');\n\t\t\treturn;\n\t\t}\n\n\t\t// generate ids if not provided\n\t\t_.button.id ||= `collapsible-button-${crypto.randomUUID().slice(0, 8)}`;\n\t\t_.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;\n\n\t\t// set accessibility attributes\n\t\t_.button.setAttribute('aria-controls', _.content.id);\n\t\t_.content.setAttribute('role', 'region');\n\t\t_.content.setAttribute('aria-labelledby', _.button.id);\n\n\t\t// set initial state based on aria-expanded attribute\n\t\tconst expanded = _.button.getAttribute('aria-expanded') === 'true';\n\n\t\t// make sure content state matches button state\n\t\t_.content.hidden = !expanded;\n\n\t\t// add event listener\n\t\t_.button.addEventListener('click', _.#handleClick);\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.button) {\n\t\t\t_.button.removeEventListener('click', _.#handleClick);\n\t\t}\n\t}\n}\n\n/**\n * Custom element that provides animated collapsible content\n */\nclass CollapsibleContent extends HTMLElement {\n\t#handleTransitionEnd;\n\n\t/**\n\t * Initializes the content element and binds event handlers\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// define event handler using arrow function for proper binding\n\t\t_.#handleTransitionEnd = (event) => {\n\t\t\t// exit if isn't height property\n\t\t\tif (event.propertyName != 'height') return;\n\n\t\t\t// remove the inline height to allow dynamic content changes\n\t\t\tif (!_.hidden) {\n\t\t\t\t_.style.height = 'auto';\n\t\t\t}\n\t\t};\n\n\t\t// add event listener to remove height after transition\n\t\t_.addEventListener('transitionend', _.#handleTransitionEnd);\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets initial height based on hidden state\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// get aria-expanded state from button\n\t\tconst component = _.closest('collapsible-component');\n\t\tconst button = component.querySelector('button');\n\t\tconst expanded = button.getAttribute('aria-expanded') === 'true';\n\n\t\tif (expanded) {\n\t\t\t_.style.height = 'auto';\n\t\t} else {\n\t\t\t_.style.height = '0';\n\t\t}\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\t_.removeEventListener('transitionend', _.#handleTransitionEnd);\n\t}\n\n\t/**\n\t * Handles setting the hidden attribute with animation\n\t * @param {boolean} value - Whether element should be hidden\n\t */\n\tset hidden(value) {\n\t\tconst _ = this;\n\n\t\t// animate to closed\n\t\tif (value) {\n\t\t\t// reset height to animate from\n\t\t\t_.style.height = `${_.scrollHeight}px`;\n\n\t\t\t// wait one frame and animate to 0\n\t\t\tsetTimeout(() => {\n\t\t\t\t_.style.height = '0px';\n\t\t\t}, 1);\n\n\t\t\t// set accessibility attributes\n\t\t\t_.setAttribute('aria-hidden', 'true');\n\t\t\t_.setAttribute('inert', '');\n\t\t\t_.setAttribute('hidden', '');\n\t\t} else {\n\t\t\t// only animate if not set to auto\n\t\t\tif (_.style.height !== 'auto') {\n\t\t\t\t// animate to open\n\t\t\t\t_.style.height = `${_.scrollHeight}px`;\n\t\t\t}\n\n\t\t\t// remove accessibility attributes\n\t\t\t_.removeAttribute('aria-hidden');\n\t\t\t_.removeAttribute('inert');\n\t\t\t_.removeAttribute('hidden');\n\t\t}\n\t}\n\n\t/**\n\t * Gets the hidden state from the attribute\n\t * @returns {boolean} Whether element is hidden\n\t */\n\tget hidden() {\n\t\tconst _ = this;\n\t\treturn _.hasAttribute('hidden');\n\t}\n}\n\n// register custom elements\ncustomElements.define('collapsible-content', CollapsibleContent);\ncustomElements.define('collapsible-component', CollapsibleComponent);\n\nexport { CollapsibleContent, CollapsibleComponent };\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collapsible-content.cjs.js","sources":["../src/collapsible-content.js"],"sourcesContent":["import './collapsible-content.css';\n\n/**\n * Custom element that creates a collapsible/expandable component with proper accessibility\n */\nclass CollapsibleComponent extends HTMLElement {\n\t#handleClick;\n\t#abortController;\n\n\t/**\n\t * Initializes the component and sets up references to child elements\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// store references to elements once to avoid re-querying\n\t\t_.button = null;\n\t\t_.content = null;\n\n\t\t// define click handler with proper this binding\n\t\t_.#handleClick = (e) => {\n\t\t\te.preventDefault();\n\t\t\t// toggle expanded value\n\t\t\tconst expanded = _.button.getAttribute('aria-expanded') !== 'true';\n\t\t\t_.button.setAttribute('aria-expanded', expanded);\n\t\t\t_.content.collapsed = !expanded;\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets up accessibility attributes and event listeners\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// initialize element references once\n\t\t_.button = _.querySelector('button');\n\t\t_.content = _.querySelector('collapsible-content');\n\n\t\tif (!_.button || !_.content) {\n\t\t\tconst error = new Error(\n\t\t\t\t'CollapsibleComponent requires a <button> and a <collapsible-content>.'\n\t\t\t);\n\t\t\tconsole.error(error.message);\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('collapsible-error', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { error },\n\t\t\t\t})\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// generate ids if not provided\n\t\t_.button.id ||= `collapsible-button-${crypto.randomUUID().slice(0, 8)}`;\n\t\t_.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;\n\n\t\t// set accessibility attributes\n\t\t_.button.type ||= 'button';\n\t\t_.button.setAttribute('aria-controls', _.content.id);\n\t\t_.content.setAttribute('aria-labelledby', _.button.id);\n\n\t\t// set initial state based on open attribute\n\t\tconst open = _.content.hasAttribute('open');\n\t\t_.button.setAttribute('aria-expanded', open);\n\t\t_.content.collapsed = !open;\n\n\t\t// use AbortController to prevent duplicate listeners on reconnection\n\t\t_.#abortController = new AbortController();\n\t\t_.button.addEventListener('click', _.#handleClick, {\n\t\t\tsignal: _.#abortController.signal,\n\t\t});\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.#abortController) {\n\t\t\t_.#abortController.abort();\n\t\t\t_.#abortController = null;\n\t\t}\n\t}\n}\n\n/**\n * Custom element that provides animated collapsible content\n */\nclass CollapsibleContent extends HTMLElement {\n\t#handleTransitionEnd;\n\t#abortController;\n\t#animating = false;\n\n\t/**\n\t * Initializes the content element and binds event handlers\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// define event handler using arrow function for proper binding\n\t\t_.#handleTransitionEnd = (event) => {\n\t\t\t// exit if isn't height property\n\t\t\tif (event.propertyName !== 'height') return;\n\n\t\t\t_.#animating = false;\n\n\t\t\t// remove the inline height to allow dynamic content changes\n\t\t\tif (!_.collapsed) {\n\t\t\t\t_.style.height = 'auto';\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets initial height based on open attribute\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\t\t_.style.height = _.hasAttribute('open') ? 'auto' : '0';\n\n\t\t// use AbortController to prevent duplicate listeners on reconnection\n\t\t_.#abortController = new AbortController();\n\t\t_.addEventListener('transitionend', _.#handleTransitionEnd, {\n\t\t\tsignal: _.#abortController.signal,\n\t\t});\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.#abortController) {\n\t\t\t_.#abortController.abort();\n\t\t\t_.#abortController = null;\n\t\t}\n\t}\n\n\t/**\n\t * Handles setting the collapsed state with animation\n\t * @param {boolean} value - Whether element should be collapsed\n\t */\n\tset collapsed(value) {\n\t\tconst _ = this;\n\n\t\t// prevent rapid clicking during animation\n\t\tif (_.#animating) return;\n\n\t\t// check if transitions are enabled (respects prefers-reduced-motion)\n\t\tconst hasTransition = getComputedStyle(_).transitionDuration !== '0s';\n\n\t\tif (value) {\n\t\t\tif (hasTransition) {\n\t\t\t\t_.#animating = true;\n\t\t\t}\n\n\t\t\t// animate to closed\n\t\t\t_.style.height = `${_.scrollHeight}px`;\n\n\t\t\t// use requestAnimationFrame for reliable frame timing\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\t_.style.height = '0px';\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t_.removeAttribute('open');\n\t\t\t_.setAttribute('aria-hidden', 'true');\n\t\t\t_.setAttribute('inert', '');\n\t\t} else {\n\t\t\t// only animate if not already auto\n\t\t\tif (_.style.height !== 'auto') {\n\t\t\t\tif (hasTransition) {\n\t\t\t\t\t_.#animating = true;\n\t\t\t\t}\n\t\t\t\t_.style.height = `${_.scrollHeight}px`;\n\t\t\t}\n\n\t\t\t_.setAttribute('open', '');\n\t\t\t_.removeAttribute('aria-hidden');\n\t\t\t_.removeAttribute('inert');\n\t\t}\n\t}\n\n\t/**\n\t * Gets the collapsed state\n\t * @returns {boolean} Whether element is collapsed\n\t */\n\tget collapsed() {\n\t\treturn !this.hasAttribute('open');\n\t}\n}\n\n// register custom elements if not already defined\nif (!customElements.get('collapsible-content')) {\n\tcustomElements.define('collapsible-content', CollapsibleContent);\n}\nif (!customElements.get('collapsible-component')) {\n\tcustomElements.define('collapsible-component', CollapsibleComponent);\n}\n\nexport { CollapsibleContent, CollapsibleComponent };\n"],"names":[],"mappings":"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"}
@@ -1,34 +1,31 @@
1
- :root {
2
- --cc-component-display: block;
3
- --cc-button-width: 100%;
4
- --cc-button-text-align: left;
5
- --cc-button-background: none;
6
- --cc-button-border: none;
7
- --cc-button-cursor: pointer;
8
- --cc-content-padding: 0px;
9
- --cc-content-display: block;
10
- --cc-content-overflow: hidden;
11
- --cc-transition-duration: 0.35s;
12
- --cc-transition-timing: ease-out;
13
- }
14
-
15
1
  collapsible-component {
16
- display: var(--cc-component-display, block);
2
+ display: block;
17
3
  }
4
+
18
5
  collapsible-component button {
19
- width: var(--cc-button-width, 100%);
20
- text-align: var(--cc-button-text-align, left);
21
- background: var(--cc-button-background, none);
22
- border: var(--cc-button-border, none);
23
- cursor: var(--cc-button-cursor, pointer);
6
+ width: 100%;
7
+ text-align: left;
8
+ background: none;
9
+ border: none;
10
+ cursor: pointer;
24
11
  }
25
- collapsible-component collapsible-content[hidden] {
26
- display: var(--cc-content-display, block);
12
+
13
+ collapsible-component button:focus-visible {
14
+ outline: 2px solid currentColor;
15
+ outline-offset: 2px;
27
16
  }
28
17
 
29
18
  collapsible-content {
30
- padding: var(--cc-content-padding, 0px);
31
- display: var(--cc-content-display, block);
32
- overflow: var(--cc-content-overflow, hidden);
33
- transition: height var(--cc-transition-duration, 0.35s) var(--cc-transition-timing, ease-out);
34
- }
19
+ --collapsible-duration: 0.35s;
20
+ --collapsible-easing: ease-out;
21
+
22
+ display: block;
23
+ overflow: hidden;
24
+ transition: height var(--collapsible-duration) var(--collapsible-easing);
25
+ }
26
+
27
+ @media (prefers-reduced-motion: reduce) {
28
+ collapsible-content {
29
+ transition: none;
30
+ }
31
+ }