@excom/content-tabs 0.1.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.
Files changed (43) hide show
  1. package/.rush/temp/chunked-rush-logs/content-tabs.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/content-tabs.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/content-tabs.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +3 -0
  14. package/config/rig.json +5 -0
  15. package/content-tabs-body.ts +26 -0
  16. package/content-tabs-header.ts +64 -0
  17. package/content-tabs.ts +174 -0
  18. package/index.css +5 -0
  19. package/index.ts +33 -0
  20. package/package.json +43 -0
  21. package/rush-logs/content-tabs.apply-exports.cache.log +1 -0
  22. package/rush-logs/content-tabs.apply-exports.log +1 -0
  23. package/rush-logs/content-tabs.build_docs.cache.log +1 -0
  24. package/rush-logs/content-tabs.build_docs.log +1 -0
  25. package/rush-logs/content-tabs.build_package-metas.cache.log +1 -0
  26. package/rush-logs/content-tabs.build_package-metas.log +1 -0
  27. package/src/content-tabs.css +137 -0
  28. package/support/custom-elements.json +376 -0
  29. package/support/demos/file-tabs.html +6 -0
  30. package/support/demos/multi.html +6 -0
  31. package/support/demos/named.html +6 -0
  32. package/support/demos/simple.html +8 -0
  33. package/support/dist-docs/content-tabs-body.md +20 -0
  34. package/support/dist-docs/content-tabs-header.md +26 -0
  35. package/support/dist-docs/content-tabs.md +183 -0
  36. package/support/docs/README.md +74 -0
  37. package/support/package-meta.json +252 -0
  38. package/support/tests/content-tabs.test.ts +426 -0
  39. package/support/tests/file-tabs.view.test.ts +26 -0
  40. package/support/tests/multi.view.test.ts +29 -0
  41. package/support/tests/named.view.test.ts +27 -0
  42. package/support/tests/simple.view.test.ts +29 -0
  43. package/tsconfig.json +5 -0
@@ -0,0 +1,376 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "kind": "javascript-module",
6
+ "path": "content-tabs.ts",
7
+ "declarations": [
8
+ {
9
+ "kind": "class",
10
+ "name": "ContentTabs",
11
+ "customElement": true,
12
+ "tagName": "content-tabs",
13
+ "summary": "Tabs / accordion — single, multi, or toggle selection.",
14
+ "description": "Open state of a `<content-tabs>` group. A tab is its header's `tab-name`, or its index among the group's own headers when unnamed. Nested groups are excluded.",
15
+ "attributes": [
16
+ {
17
+ "name": "tab-type",
18
+ "type": {
19
+ "text": "string"
20
+ },
21
+ "description": "Selection mode. `single` opens one header at a time (radio-like); `multi` allows any number open at once (accordion); `toggle` is like `single`, but re-clicking the open header closes it.",
22
+ "fieldName": "tabType",
23
+ "default": "single",
24
+ "values": [
25
+ "single",
26
+ "multi",
27
+ "toggle"
28
+ ]
29
+ }
30
+ ],
31
+ "members": [
32
+ {
33
+ "kind": "field",
34
+ "name": "tabType",
35
+ "type": {
36
+ "text": "string"
37
+ },
38
+ "privacy": "public",
39
+ "readonly": false,
40
+ "description": "Selection mode. `single` opens one header at a time (radio-like); `multi` allows any number open at once (accordion); `toggle` is like `single`, but re-clicking the open header closes it.",
41
+ "default": "single",
42
+ "_neutron": {
43
+ "surface": "option"
44
+ }
45
+ },
46
+ {
47
+ "kind": "field",
48
+ "name": "provision",
49
+ "type": {
50
+ "text": "ContentTabsProvision",
51
+ "expanded": "{ tabType: string | null; openTabs: Array<string | number>; activeTab: string | number | null; }"
52
+ },
53
+ "privacy": "public",
54
+ "readonly": false,
55
+ "description": "Open state: `{ tabType, openTabs, activeTab }`. A tab is its header's `tab-name`, or its index among this group's own headers when unnamed; `activeTab` is the first open one (`null` when none). Set after mount and after every header change (batched with the body sync). Not reflected as an attribute.",
56
+ "_neutron": {
57
+ "surface": "option"
58
+ }
59
+ }
60
+ ],
61
+ "_neutron": {
62
+ "provisions": [
63
+ {
64
+ "name": "provision",
65
+ "type": {
66
+ "text": "ContentTabsProvision",
67
+ "expanded": "{ tabType: string | null; openTabs: Array<string | number>; activeTab: string | number | null; }"
68
+ },
69
+ "description": "Open state: `{ tabType, openTabs, activeTab }`. A tab is its header's `tab-name`, or its index among this group's own headers when unnamed; `activeTab` is the first open one (`null` when none). Set after mount and after every header change (batched with the body sync). Not reflected as an attribute.",
70
+ "fieldName": "provision"
71
+ }
72
+ ],
73
+ "listens": [
74
+ {
75
+ "name": "content-tabs-header-opened",
76
+ "description": "Closes sibling headers when `tab-type` is `single` (default) or `toggle`.",
77
+ "type": {
78
+ "text": "ContentTabsHeaderOpenedEvent",
79
+ "expanded": "CustomEvent & { type: \"content-tabs-header-opened\"; detail: void; bubbles: true; cancelable: true; composed: true }"
80
+ }
81
+ }
82
+ ],
83
+ "expectedChildren": [
84
+ {
85
+ "relationship": "child",
86
+ "selector": "content-tabs-header",
87
+ "required": true,
88
+ "description": "Clickable tab headers."
89
+ },
90
+ {
91
+ "relationship": "child",
92
+ "selector": "content-tabs-body",
93
+ "required": true,
94
+ "description": "Panels shown / hidden to match their header."
95
+ }
96
+ ],
97
+ "cssClasses": [
98
+ {
99
+ "name": "file-tabs",
100
+ "description": "File-explorer look — headers render as bordered folder tabs joined to the active body."
101
+ },
102
+ {
103
+ "name": "underline",
104
+ "description": "Paper / underline look — the active header gets a bottom accent border instead of a bordered card."
105
+ }
106
+ ],
107
+ "cssAliases": [
108
+ {
109
+ "name": ":--content-tabs",
110
+ "selectors": [
111
+ "content-tabs",
112
+ ".tag-content-tabs"
113
+ ],
114
+ "kind": "element"
115
+ },
116
+ {
117
+ "name": ":--content-tab--is-open",
118
+ "selectors": [
119
+ "[is-open]",
120
+ "[aria-selected=\"true\"]"
121
+ ],
122
+ "kind": "state"
123
+ }
124
+ ]
125
+ },
126
+ "cssProperties": [
127
+ {
128
+ "name": "--content-tabs-border-color",
129
+ "description": "Border color for `.file-tabs` headers / bodies.",
130
+ "syntax": "<color>",
131
+ "default": "var(--v-muted-border-color, currentColor)"
132
+ },
133
+ {
134
+ "name": "--content-tabs-border-width",
135
+ "description": "Border width for `.file-tabs` headers / bodies.",
136
+ "syntax": "<length>",
137
+ "default": "var(--v-border-width, 2px)"
138
+ },
139
+ {
140
+ "name": "--content-tabs-color-active",
141
+ "description": "Text color for the active header in `.underline`.",
142
+ "syntax": "<color>",
143
+ "default": "var(--v-color-primary, inherit)"
144
+ },
145
+ {
146
+ "name": "--content-tabs-header-padding",
147
+ "description": "Padding for each header.",
148
+ "syntax": "<length>{1,4}",
149
+ "default": "10px 15px"
150
+ },
151
+ {
152
+ "name": "--content-tabs-body-padding",
153
+ "description": "Padding for each body in `.file-tabs`.",
154
+ "syntax": "<length>{1,4}",
155
+ "default": "var(--v-spacing, 15px)"
156
+ },
157
+ {
158
+ "name": "--content-tabs-active-indicator-height",
159
+ "description": "Height of the `.file-tabs` active-tab accent bar.",
160
+ "syntax": "<length>",
161
+ "default": "2px"
162
+ }
163
+ ]
164
+ }
165
+ ],
166
+ "exports": [
167
+ {
168
+ "kind": "js",
169
+ "name": "ContentTabs",
170
+ "declaration": {
171
+ "name": "ContentTabs",
172
+ "module": "content-tabs.ts"
173
+ }
174
+ },
175
+ {
176
+ "kind": "custom-element-definition",
177
+ "name": "content-tabs",
178
+ "declaration": {
179
+ "name": "ContentTabs",
180
+ "module": "content-tabs.ts"
181
+ }
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ "kind": "javascript-module",
187
+ "path": "content-tabs-body.ts",
188
+ "declarations": [
189
+ {
190
+ "kind": "class",
191
+ "name": "ContentTabsBody",
192
+ "customElement": true,
193
+ "tagName": "content-tabs-body",
194
+ "summary": "Tab panel — visible when its paired header is open.",
195
+ "description": "Panel paired with a `<content-tabs-header>` — shown when that header is open. Lives inside a `<content-tabs>`.",
196
+ "attributes": [
197
+ {
198
+ "name": "is-open",
199
+ "type": {
200
+ "text": "boolean"
201
+ },
202
+ "description": "Visibility, kept in sync with the paired header's `is-open`. Set directly only if this body is not paired with a header.",
203
+ "fieldName": "isOpen"
204
+ },
205
+ {
206
+ "name": "tab-name",
207
+ "type": {
208
+ "text": "string"
209
+ },
210
+ "description": "Pairs this body with the `<content-tabs-header>` sharing the same `tab-name`. Unset headers / bodies pair by position instead.",
211
+ "fieldName": "tabName"
212
+ }
213
+ ],
214
+ "members": [
215
+ {
216
+ "kind": "field",
217
+ "name": "isOpen",
218
+ "type": {
219
+ "text": "boolean"
220
+ },
221
+ "privacy": "public",
222
+ "readonly": false,
223
+ "description": "Visibility, kept in sync with the paired header's `is-open`. Set directly only if this body is not paired with a header.",
224
+ "_neutron": {
225
+ "surface": "hybrid"
226
+ }
227
+ },
228
+ {
229
+ "kind": "field",
230
+ "name": "tabName",
231
+ "type": {
232
+ "text": "string"
233
+ },
234
+ "privacy": "public",
235
+ "readonly": false,
236
+ "description": "Pairs this body with the `<content-tabs-header>` sharing the same `tab-name`. Unset headers / bodies pair by position instead.",
237
+ "_neutron": {
238
+ "surface": "option"
239
+ }
240
+ }
241
+ ],
242
+ "_neutron": {
243
+ "cssAliases": [
244
+ {
245
+ "name": ":--content-tabs-body",
246
+ "selectors": [
247
+ "content-tabs-body",
248
+ ".tag-content-tabs-body"
249
+ ],
250
+ "kind": "element"
251
+ }
252
+ ]
253
+ }
254
+ }
255
+ ],
256
+ "exports": [
257
+ {
258
+ "kind": "js",
259
+ "name": "ContentTabsBody",
260
+ "declaration": {
261
+ "name": "ContentTabsBody",
262
+ "module": "content-tabs-body.ts"
263
+ }
264
+ },
265
+ {
266
+ "kind": "custom-element-definition",
267
+ "name": "content-tabs-body",
268
+ "declaration": {
269
+ "name": "ContentTabsBody",
270
+ "module": "content-tabs-body.ts"
271
+ }
272
+ }
273
+ ]
274
+ },
275
+ {
276
+ "kind": "javascript-module",
277
+ "path": "content-tabs-header.ts",
278
+ "declarations": [
279
+ {
280
+ "kind": "class",
281
+ "name": "ContentTabsHeader",
282
+ "customElement": true,
283
+ "tagName": "content-tabs-header",
284
+ "summary": "Tab header — opens its paired body on click.",
285
+ "description": "Clickable tab header — lives inside a `<content-tabs>`. Click opens (`single` / `toggle`) or toggles (`multi`) `is-open`, which keeps the paired `<content-tabs-body>` in sync.",
286
+ "attributes": [
287
+ {
288
+ "name": "tab-name",
289
+ "type": {
290
+ "text": "string"
291
+ },
292
+ "description": "Pairs this header with the `<content-tabs-body>` sharing the same `tab-name`. Unset headers / bodies pair by position instead.",
293
+ "fieldName": "tabName"
294
+ },
295
+ {
296
+ "name": "is-open",
297
+ "type": {
298
+ "text": "boolean"
299
+ },
300
+ "description": "Open state. Click sets or toggles this depending on the parent's `tab-type`; set it directly to drive the tab programmatically.",
301
+ "fieldName": "isOpen"
302
+ }
303
+ ],
304
+ "members": [
305
+ {
306
+ "kind": "field",
307
+ "name": "tabName",
308
+ "type": {
309
+ "text": "string"
310
+ },
311
+ "privacy": "public",
312
+ "readonly": false,
313
+ "description": "Pairs this header with the `<content-tabs-body>` sharing the same `tab-name`. Unset headers / bodies pair by position instead.",
314
+ "_neutron": {
315
+ "surface": "option"
316
+ }
317
+ },
318
+ {
319
+ "kind": "field",
320
+ "name": "isOpen",
321
+ "type": {
322
+ "text": "boolean"
323
+ },
324
+ "privacy": "public",
325
+ "readonly": false,
326
+ "description": "Open state. Click sets or toggles this depending on the parent's `tab-type`; set it directly to drive the tab programmatically.",
327
+ "_neutron": {
328
+ "surface": "hybrid"
329
+ }
330
+ }
331
+ ],
332
+ "events": [
333
+ {
334
+ "name": "content-tabs-header-opened",
335
+ "description": "Whenever `is-open` is set. The parent `<content-tabs>` listens for this to close sibling headers.",
336
+ "type": {
337
+ "text": "ContentTabsHeaderOpenedEvent",
338
+ "expanded": "CustomEvent & { type: \"content-tabs-header-opened\"; detail: void; bubbles: true; cancelable: true; composed: true }"
339
+ }
340
+ }
341
+ ],
342
+ "_neutron": {
343
+ "cssAliases": [
344
+ {
345
+ "name": ":--content-tabs-header",
346
+ "selectors": [
347
+ "content-tabs-header",
348
+ ".tag-content-tabs-header"
349
+ ],
350
+ "kind": "element"
351
+ }
352
+ ]
353
+ }
354
+ }
355
+ ],
356
+ "exports": [
357
+ {
358
+ "kind": "js",
359
+ "name": "ContentTabsHeader",
360
+ "declaration": {
361
+ "name": "ContentTabsHeader",
362
+ "module": "content-tabs-header.ts"
363
+ }
364
+ },
365
+ {
366
+ "kind": "custom-element-definition",
367
+ "name": "content-tabs-header",
368
+ "declaration": {
369
+ "name": "ContentTabsHeader",
370
+ "module": "content-tabs-header.ts"
371
+ }
372
+ }
373
+ ]
374
+ }
375
+ ]
376
+ }
@@ -0,0 +1,6 @@
1
+ <content-tabs class="file-tabs">
2
+ <content-tabs-header is-open>index.ts</content-tabs-header>
3
+ <content-tabs-header>utils.ts</content-tabs-header>
4
+ <content-tabs-body is-open><pre>export const value = 1;</pre></content-tabs-body>
5
+ <content-tabs-body><pre>export const double = (n) => n * 2;</pre></content-tabs-body>
6
+ </content-tabs>
@@ -0,0 +1,6 @@
1
+ <content-tabs tab-type="multi">
2
+ <content-tabs-header is-open role="button">Shipping</content-tabs-header>
3
+ <content-tabs-header role="button">Billing</content-tabs-header>
4
+ <content-tabs-body is-open><p>Ships in 3-5 business days.</p></content-tabs-body>
5
+ <content-tabs-body><p>Charged on shipment, not on order.</p></content-tabs-body>
6
+ </content-tabs>
@@ -0,0 +1,6 @@
1
+ <content-tabs class="underline">
2
+ <content-tabs-body tab-name="b"><p>Body B — paired by name, not position.</p></content-tabs-body>
3
+ <content-tabs-body tab-name="a" is-open><p>Body A — paired by name, not position.</p></content-tabs-body>
4
+ <content-tabs-header tab-name="a" is-open>A</content-tabs-header>
5
+ <content-tabs-header tab-name="b">B</content-tabs-header>
6
+ </content-tabs>
@@ -0,0 +1,8 @@
1
+ <content-tabs class="underline">
2
+ <content-tabs-header is-open>Overview</content-tabs-header>
3
+ <content-tabs-header>Details</content-tabs-header>
4
+ <content-tabs-header>Settings</content-tabs-header>
5
+ <content-tabs-body is-open><p>Overview copy goes here.</p></content-tabs-body>
6
+ <content-tabs-body><p>Details copy goes here.</p></content-tabs-body>
7
+ <content-tabs-body><p>Settings copy goes here.</p></content-tabs-body>
8
+ </content-tabs>
@@ -0,0 +1,20 @@
1
+ # `<content-tabs-body>`
2
+
3
+ > Tab panel — visible when its paired header is open.
4
+
5
+ **Tag:** `<content-tabs-body>`
6
+
7
+ ## API
8
+
9
+ ### Attributes
10
+
11
+ | Name | Surface | Type | Default | Values | Description |
12
+ | --- | --- | --- | --- | --- | --- |
13
+ | `is-open` | hybrid | `boolean` | | | Visibility, kept in sync with the paired header's `is-open`. Set directly only if this body is not paired with a header. |
14
+ | `tab-name` | option | `string` | | | Pairs this body with the `<content-tabs-header>` sharing the same `tab-name`. Unset headers / bodies pair by position instead. |
15
+
16
+ ### CSS Aliases
17
+
18
+ | Alias | Kind | Matches | Description |
19
+ | --- | --- | --- | --- |
20
+ | `:--content-tabs-body` | element | `content-tabs-body`, `.tag-content-tabs-body` | |
@@ -0,0 +1,26 @@
1
+ # `<content-tabs-header>`
2
+
3
+ > Tab header — opens its paired body on click.
4
+
5
+ **Tag:** `<content-tabs-header>`
6
+
7
+ ## API
8
+
9
+ ### Attributes
10
+
11
+ | Name | Surface | Type | Default | Values | Description |
12
+ | --- | --- | --- | --- | --- | --- |
13
+ | `tab-name` | option | `string` | | | Pairs this header with the `<content-tabs-body>` sharing the same `tab-name`. Unset headers / bodies pair by position instead. |
14
+ | `is-open` | hybrid | `boolean` | | | Open state. Click sets or toggles this depending on the parent's `tab-type`; set it directly to drive the tab programmatically. |
15
+
16
+ ### Fires
17
+
18
+ | Name | Type | Description |
19
+ | --- | --- | --- |
20
+ | `content-tabs-header-opened` | `ContentTabsHeaderOpenedEvent` (`CustomEvent & { type: "content-tabs-header-opened"; detail: void; bubbles: true; cancelable: true; composed: true }`) | Whenever `is-open` is set. The parent `<content-tabs>` listens for this to close sibling headers. |
21
+
22
+ ### CSS Aliases
23
+
24
+ | Alias | Kind | Matches | Description |
25
+ | --- | --- | --- | --- |
26
+ | `:--content-tabs-header` | element | `content-tabs-header`, `.tag-content-tabs-header` | |
@@ -0,0 +1,183 @@
1
+ # content-tabs
2
+
3
+ Tabs and accordions — single, multi, or toggle selection, paired by name
4
+ or position.
5
+
6
+
7
+ ```html
8
+ <content-tabs class="underline">
9
+ <content-tabs-header is-open>Overview</content-tabs-header>
10
+ <content-tabs-header>Details</content-tabs-header>
11
+ <content-tabs-header>Settings</content-tabs-header>
12
+ <content-tabs-body is-open><p>Overview copy goes here.</p></content-tabs-body>
13
+ <content-tabs-body><p>Details copy goes here.</p></content-tabs-body>
14
+ <content-tabs-body><p>Settings copy goes here.</p></content-tabs-body>
15
+ </content-tabs>
16
+ ```
17
+
18
+
19
+ ## Features
20
+
21
+ - **Single / multi / toggle** Radio-like tabs, an accordion, or re-click
22
+ to close
23
+ - **Pair by name or position** Match headers to bodies with `tab-name`,
24
+ or by index when unnamed
25
+ - **Isolated nesting** Nested `<content-tabs>` groups never cross-wire
26
+ - **Bindable state** `.provision` is `{ tabType, openTabs, activeTab }` —
27
+ read the active tab from Quark with `prop("provision")`
28
+ - **Included looks** `.underline` and `.file-tabs` styles ship built in
29
+
30
+ ## Installation
31
+
32
+
33
+ `@excom/content-tabs` v0.1.0
34
+
35
+ ```bash
36
+ pnpm add @excom/content-tabs
37
+ ```
38
+
39
+ ```bash
40
+ npm install @excom/content-tabs
41
+ ```
42
+
43
+ ```bash
44
+ yarn add @excom/content-tabs
45
+ ```
46
+
47
+ ### Import
48
+
49
+ ```ts
50
+ import "@excom/content-tabs";
51
+ ```
52
+
53
+
54
+
55
+ ## Usage
56
+
57
+ Put `<content-tabs-header>` and `<content-tabs-body>` elements inside
58
+ `<content-tabs>`. A header pairs with the body sharing its `tab-name`,
59
+ or by position when both are unnamed.
60
+
61
+ ```html
62
+ <content-tabs class="underline">
63
+ <content-tabs-header is-open>Overview</content-tabs-header>
64
+ <content-tabs-header>Details</content-tabs-header>
65
+ <content-tabs-body is-open><p>Overview copy.</p></content-tabs-body>
66
+ <content-tabs-body><p>Details copy.</p></content-tabs-body>
67
+ </content-tabs>
68
+ ```
69
+
70
+ The open state is a fact, not just an event: `.provision` holds
71
+ `{ tabType, openTabs, activeTab }`, where a tab is its header's `tab-name`
72
+ (or its index when unnamed) and `activeTab` is the first open one. Quark
73
+ reads it on the group and binds it anywhere below:
74
+
75
+ ```quark
76
+ content-tabs {
77
+ $tab: prop("provision").activeTab;
78
+ h2 { content: $tab; }
79
+ }
80
+ ```
81
+
82
+ ### API Reference
83
+
84
+
85
+ #### Attributes
86
+
87
+ | Name | Surface | Type | Default | Values | Description |
88
+ | --- | --- | --- | --- | --- | --- |
89
+ | `tab-type` | option | `string` | `"single"` | `"single"` \| `"multi"` \| `"toggle"` | Selection mode. `single` opens one header at a time (radio-like); `multi` allows any number open at once (accordion); `toggle` is like `single`, but re-clicking the open header closes it. |
90
+
91
+ #### Provision
92
+
93
+ | Name | Type | Description |
94
+ | --- | --- | --- |
95
+ | `provision` | `ContentTabsProvision` (`{ tabType: string \| null; openTabs: Array<string \| number>; activeTab: string \| number \| null; }`) | Open state: `{ tabType, openTabs, activeTab }`. A tab is its header's `tab-name`, or its index among this group's own headers when unnamed; `activeTab` is the first open one (`null` when none). Set after mount and after every header change (batched with the body sync). Not reflected as an attribute. |
96
+
97
+ #### Recognized Elements
98
+
99
+ | Relationship | Selector | Required | Description |
100
+ | --- | --- | --- | --- |
101
+ | `content-tabs-header` | child | yes | Clickable tab headers. |
102
+ | `content-tabs-body` | child | yes | Panels shown / hidden to match their header. |
103
+
104
+ #### Listens for
105
+
106
+ | Name | Type | Description |
107
+ | --- | --- | --- |
108
+ | `content-tabs-header-opened` | `ContentTabsHeaderOpenedEvent` (`CustomEvent & { type: "content-tabs-header-opened"; detail: void; bubbles: true; cancelable: true; composed: true }`) | Closes sibling headers when `tab-type` is `single` (default) or `toggle`. |
109
+
110
+ #### CSS Custom Properties
111
+
112
+ | Name | Syntax | Default | Description |
113
+ | --- | --- | --- | --- |
114
+ | `--content-tabs-border-color` | `<color>` | `var(--v-muted-border-color, currentColor)` | Border color for `.file-tabs` headers / bodies. |
115
+ | `--content-tabs-border-width` | `<length>` | `var(--v-border-width, 2px)` | Border width for `.file-tabs` headers / bodies. |
116
+ | `--content-tabs-color-active` | `<color>` | `var(--v-color-primary, inherit)` | Text color for the active header in `.underline`. |
117
+ | `--content-tabs-header-padding` | `<length>{1,4}` | `10px 15px` | Padding for each header. |
118
+ | `--content-tabs-body-padding` | `<length>{1,4}` | `var(--v-spacing, 15px)` | Padding for each body in `.file-tabs`. |
119
+ | `--content-tabs-active-indicator-height` | `<length>` | `2px` | Height of the `.file-tabs` active-tab accent bar. |
120
+
121
+ #### CSS Classes
122
+
123
+ | Name | Description |
124
+ | --- | --- |
125
+ | `.file-tabs` | File-explorer look — headers render as bordered folder tabs joined to the active body. |
126
+ | `.underline` | Paper / underline look — the active header gets a bottom accent border instead of a bordered card. |
127
+
128
+ #### CSS Aliases
129
+
130
+ | Alias | Kind | Matches | Description |
131
+ | --- | --- | --- | --- |
132
+ | `:--content-tabs` | element | `content-tabs`, `.tag-content-tabs` | |
133
+ | `:--content-tab--is-open` | state | `[is-open]`, `[aria-selected="true"]` | |
134
+
135
+
136
+
137
+ ### Examples
138
+
139
+ #### File tabs
140
+
141
+ `.file-tabs` renders each header like a folder tab, joined to its body.
142
+
143
+
144
+ ```html
145
+ <content-tabs class="file-tabs">
146
+ <content-tabs-header is-open>index.ts</content-tabs-header>
147
+ <content-tabs-header>utils.ts</content-tabs-header>
148
+ <content-tabs-body is-open><pre>export const value = 1;</pre></content-tabs-body>
149
+ <content-tabs-body><pre>export const double = (n) => n * 2;</pre></content-tabs-body>
150
+ </content-tabs>
151
+ ```
152
+
153
+
154
+ #### Multi-select accordion
155
+
156
+ `tab-type="multi"` lets any number of headers stay open — clicking one does not close the others. Also displayed are the tab headers as buttons, if using Valence.css.
157
+
158
+ Useful for building your own UI toggle systems.
159
+
160
+
161
+ ```html
162
+ <content-tabs tab-type="multi">
163
+ <content-tabs-header is-open role="button">Shipping</content-tabs-header>
164
+ <content-tabs-header role="button">Billing</content-tabs-header>
165
+ <content-tabs-body is-open><p>Ships in 3-5 business days.</p></content-tabs-body>
166
+ <content-tabs-body><p>Charged on shipment, not on order.</p></content-tabs-body>
167
+ </content-tabs>
168
+ ```
169
+
170
+
171
+ #### Pair by tab-name
172
+
173
+ Give a header and body matching `tab-name` values to pair them regardless of their order in the DOM.
174
+
175
+
176
+ ```html
177
+ <content-tabs class="underline">
178
+ <content-tabs-body tab-name="b"><p>Body B — paired by name, not position.</p></content-tabs-body>
179
+ <content-tabs-body tab-name="a" is-open><p>Body A — paired by name, not position.</p></content-tabs-body>
180
+ <content-tabs-header tab-name="a" is-open>A</content-tabs-header>
181
+ <content-tabs-header tab-name="b">B</content-tabs-header>
182
+ </content-tabs>
183
+ ```