@magic-spells/tab-group 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.
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/scss/tab-group.scss +125 -0
- package/dist/scss/variables.scss +0 -0
- package/dist/tab-group.cjs.js +274 -0
- package/dist/tab-group.cjs.js.map +1 -0
- package/dist/tab-group.css +95 -0
- package/dist/tab-group.esm.js +269 -0
- package/dist/tab-group.esm.js.map +1 -0
- package/dist/tab-group.js +280 -0
- package/dist/tab-group.js.map +1 -0
- package/dist/tab-group.min.css +1 -0
- package/dist/tab-group.min.js +1 -0
- package/dist/tab-group.scss +2 -0
- package/package.json +63 -0
- package/src/index.scss +2 -0
- package/src/scss/tab-group.scss +125 -0
- package/src/scss/variables.scss +0 -0
- package/src/tab-group.js +277 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Magic Spells (Cory Schulz)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# ✨ Tab Group
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
A lightweight, accessible tab interface web component with keyboard navigation and rich customization options.
|
|
7
|
+
|
|
8
|
+
🔍 **[Live Demo](https://magic-spells.github.io/tab-group/demo/)** - See it in action!
|
|
9
|
+
|
|
10
|
+
## 🚀 Features
|
|
11
|
+
|
|
12
|
+
- **Fully Accessible** - Built following WAI-ARIA Tab pattern guidelines
|
|
13
|
+
- **Custom Events** - Listen for tab changes with detailed event data
|
|
14
|
+
- **Keyboard Navigation** - Complete keyboard support for accessibility
|
|
15
|
+
- **Auto Consistency** - Ensures tab buttons and panels stay in sync
|
|
16
|
+
- **Theme Support** - Extensive CSS variable customization
|
|
17
|
+
- **Zero Dependencies** - Lightweight and standalone
|
|
18
|
+
- **Easy Integration** - Works with any framework or vanilla JS
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# npm
|
|
24
|
+
npm install @magic-spells/tab-group
|
|
25
|
+
|
|
26
|
+
# yarn
|
|
27
|
+
yarn add @magic-spells/tab-group
|
|
28
|
+
|
|
29
|
+
# pnpm
|
|
30
|
+
pnpm add @magic-spells/tab-group
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 🔧 Usage
|
|
34
|
+
|
|
35
|
+
### Basic Implementation
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<!-- Import the component -->
|
|
39
|
+
<script type="module">
|
|
40
|
+
import '@magic-spells/tab-group';
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- Use the component -->
|
|
44
|
+
<tab-group>
|
|
45
|
+
<tab-list>
|
|
46
|
+
<tab-button>First Tab</tab-button>
|
|
47
|
+
<tab-button>Second Tab</tab-button>
|
|
48
|
+
<tab-button>Third Tab</tab-button>
|
|
49
|
+
</tab-list>
|
|
50
|
+
|
|
51
|
+
<tab-panel>
|
|
52
|
+
<h3>First Tab Content</h3>
|
|
53
|
+
<p>This is the content for the first tab.</p>
|
|
54
|
+
</tab-panel>
|
|
55
|
+
|
|
56
|
+
<tab-panel>
|
|
57
|
+
<h3>Second Tab Content</h3>
|
|
58
|
+
<p>This is the content for the second tab.</p>
|
|
59
|
+
</tab-panel>
|
|
60
|
+
|
|
61
|
+
<tab-panel>
|
|
62
|
+
<h3>Third Tab Content</h3>
|
|
63
|
+
<p>This is the content for the third tab.</p>
|
|
64
|
+
</tab-panel>
|
|
65
|
+
</tab-group>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Listening for Tab Changes
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
document
|
|
72
|
+
.querySelector('tab-group')
|
|
73
|
+
.addEventListener('tabchange', (event) => {
|
|
74
|
+
console.log('Tab changed!');
|
|
75
|
+
|
|
76
|
+
// Access detailed event data
|
|
77
|
+
const {
|
|
78
|
+
previousIndex, // Index of previous tab
|
|
79
|
+
currentIndex, // Index of current tab
|
|
80
|
+
previousTab, // Previous tab element
|
|
81
|
+
currentTab, // Current tab element
|
|
82
|
+
previousPanel, // Previous panel element
|
|
83
|
+
currentPanel, // Current panel element
|
|
84
|
+
} = event.detail;
|
|
85
|
+
|
|
86
|
+
// Do something with the data
|
|
87
|
+
console.log(
|
|
88
|
+
`Changed from tab ${previousIndex} to tab ${currentIndex}`
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🎨 Styling
|
|
94
|
+
|
|
95
|
+
### CSS Variables
|
|
96
|
+
|
|
97
|
+
The Tab Group component can be extensively customized using CSS variables:
|
|
98
|
+
|
|
99
|
+
```css
|
|
100
|
+
tab-group {
|
|
101
|
+
/* Base colors */
|
|
102
|
+
--color-background: #ffffff;
|
|
103
|
+
--color-text: #333333;
|
|
104
|
+
--color-border: #dddddd;
|
|
105
|
+
--color-border-hover: #bbbbbb;
|
|
106
|
+
--color-primary: #3366ff;
|
|
107
|
+
--color-hover: #f0f5ff;
|
|
108
|
+
--color-focus: #b3cbff;
|
|
109
|
+
|
|
110
|
+
/* Panel styling */
|
|
111
|
+
--panel-background: white;
|
|
112
|
+
--panel-border: 1px solid var(--color-border);
|
|
113
|
+
--panel-padding: 1rem;
|
|
114
|
+
--panel-radius: 0 0 0.5rem 0.5rem;
|
|
115
|
+
--panel-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
116
|
+
|
|
117
|
+
/* Tab list styling */
|
|
118
|
+
--tab-list-gap: 0.25rem;
|
|
119
|
+
--tab-list-padding: 0.5rem 0.5rem 0;
|
|
120
|
+
--tab-list-background: transparent;
|
|
121
|
+
--tab-list-border-bottom: 1px solid var(--color-border);
|
|
122
|
+
--tab-list-radius: 0.5rem 0.5rem 0 0;
|
|
123
|
+
|
|
124
|
+
/* Tab button styling */
|
|
125
|
+
--tab-button-radius: 0.25rem 0.25rem 0 0;
|
|
126
|
+
--tab-active-background: white;
|
|
127
|
+
--tab-active-color: var(--color-primary);
|
|
128
|
+
--tab-active-font-weight: 500;
|
|
129
|
+
--tab-active-shadow: none;
|
|
130
|
+
--tab-active-transform: translateY(0);
|
|
131
|
+
|
|
132
|
+
/* Tab indicator styling */
|
|
133
|
+
--tab-indicator-height: 2px;
|
|
134
|
+
--tab-indicator-color: var(--color-primary);
|
|
135
|
+
--tab-indicator-left: 0;
|
|
136
|
+
--tab-indicator-right: 0;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### SCSS Integration
|
|
141
|
+
|
|
142
|
+
This package provides multiple ways to integrate with your SCSS workflow:
|
|
143
|
+
|
|
144
|
+
```scss
|
|
145
|
+
// Option 1: Use the main entry point (recommended)
|
|
146
|
+
@use "@magic-spells/tab-group/scss" with (
|
|
147
|
+
$color-primary: #3366ff,
|
|
148
|
+
$border-radius: 0.5rem
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Option 2: Import individual files
|
|
152
|
+
@use "@magic-spells/tab-group/scss/variables" with (
|
|
153
|
+
$color-primary: #3366ff,
|
|
154
|
+
$border-radius: 0.5rem
|
|
155
|
+
);
|
|
156
|
+
@use "@magic-spells/tab-group/scss/tab-group";
|
|
157
|
+
|
|
158
|
+
// Option 3: Direct paths (if needed)
|
|
159
|
+
@use "node_modules/@magic-spells/tab-group/dist/tab-group.scss";
|
|
160
|
+
@use "node_modules/@magic-spells/tab-group/dist/scss/tab-group";
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Available SCSS Variables
|
|
164
|
+
|
|
165
|
+
You can customize the appearance by overriding these SCSS variables:
|
|
166
|
+
|
|
167
|
+
```scss
|
|
168
|
+
// Colors
|
|
169
|
+
$color-background: #ffffff !default;
|
|
170
|
+
$color-text: #333333 !default;
|
|
171
|
+
$color-border: #dddddd !default;
|
|
172
|
+
$color-border-hover: #bbbbbb !default;
|
|
173
|
+
$color-border-dark: #999999 !default;
|
|
174
|
+
$color-primary: #3366ff !default;
|
|
175
|
+
$color-hover: #f0f5ff !default;
|
|
176
|
+
$color-focus: #b3cbff !default;
|
|
177
|
+
|
|
178
|
+
// Border radius
|
|
179
|
+
$border-radius: 0.5rem !default;
|
|
180
|
+
|
|
181
|
+
// Box shadow
|
|
182
|
+
$box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !default;
|
|
183
|
+
|
|
184
|
+
// Tab list
|
|
185
|
+
$tab-list-gap: 0.25rem !default;
|
|
186
|
+
$tab-list-padding: 0.5rem 0.5rem 0 !default;
|
|
187
|
+
$tab-list-background: transparent !default;
|
|
188
|
+
$tab-list-border-bottom: 1px solid $color-border !default;
|
|
189
|
+
$tab-list-radius: $border-radius $border-radius 0 0 !default;
|
|
190
|
+
|
|
191
|
+
// Tab button
|
|
192
|
+
$tab-button-radius: 0.25rem 0.25rem 0 0 !default;
|
|
193
|
+
$tab-active-background: white !default;
|
|
194
|
+
$tab-active-color: $color-primary !default;
|
|
195
|
+
$tab-active-font-weight: 500 !default;
|
|
196
|
+
$tab-active-shadow: none !default;
|
|
197
|
+
$tab-active-transform: translateY(0) !default;
|
|
198
|
+
|
|
199
|
+
// Tab indicator
|
|
200
|
+
$tab-indicator-height: 2px !default;
|
|
201
|
+
$tab-indicator-color: $color-primary !default;
|
|
202
|
+
$tab-indicator-left: 0 !default;
|
|
203
|
+
$tab-indicator-right: 0 !default;
|
|
204
|
+
|
|
205
|
+
// Panel
|
|
206
|
+
$panel-background: white !default;
|
|
207
|
+
$panel-border: 1px solid $color-border !default;
|
|
208
|
+
$panel-padding: 1rem !default;
|
|
209
|
+
$panel-radius: 0 0 $border-radius $border-radius !default;
|
|
210
|
+
$panel-shadow: $box-shadow !default;
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## 🌈 Theme Examples
|
|
214
|
+
|
|
215
|
+
### Dark Theme
|
|
216
|
+
|
|
217
|
+
```css
|
|
218
|
+
tab-group {
|
|
219
|
+
--color-background: #2d3748;
|
|
220
|
+
--color-text: #e2e8f0;
|
|
221
|
+
--color-border: #4a5568;
|
|
222
|
+
--color-border-hover: #718096;
|
|
223
|
+
--color-primary: #4299e1;
|
|
224
|
+
--color-hover: #1a202c;
|
|
225
|
+
--color-focus: #2c5282;
|
|
226
|
+
--panel-background: #1e293b;
|
|
227
|
+
--panel-border: 1px solid #4a5568;
|
|
228
|
+
--panel-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
229
|
+
--tab-active-color: #63b3ed;
|
|
230
|
+
--tab-indicator-color: #63b3ed;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Rounded Pills Theme
|
|
235
|
+
|
|
236
|
+
```css
|
|
237
|
+
tab-group {
|
|
238
|
+
--border-radius: 0.5rem;
|
|
239
|
+
--color-primary: #9f7aea;
|
|
240
|
+
--color-focus: #e9d8fd;
|
|
241
|
+
--tab-button-radius: 999px;
|
|
242
|
+
--tab-list-padding: 0.75rem 0.5rem 0;
|
|
243
|
+
--tab-list-gap: 0.5rem;
|
|
244
|
+
--panel-radius: 1rem;
|
|
245
|
+
--panel-shadow: 0 4px 12px rgba(159, 122, 234, 0.15);
|
|
246
|
+
--panel-border: 1px solid #e9d8fd;
|
|
247
|
+
--panel-padding: 1.5rem;
|
|
248
|
+
--tab-active-background: #9f7aea;
|
|
249
|
+
--tab-active-color: white;
|
|
250
|
+
--tab-active-font-weight: 500;
|
|
251
|
+
--tab-active-transform: translateY(-3px);
|
|
252
|
+
--tab-active-shadow: 0 4px 8px rgba(159, 122, 234, 0.3);
|
|
253
|
+
--tab-indicator-height: 0;
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## ⌨️ Accessibility
|
|
258
|
+
|
|
259
|
+
The Tab Group component follows the [WAI-ARIA Tabs Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) with:
|
|
260
|
+
|
|
261
|
+
- Proper ARIA roles, states, and properties
|
|
262
|
+
- Keyboard navigation:
|
|
263
|
+
- `Tab` to focus the active tab button
|
|
264
|
+
- `←` / `↑` to move to the previous tab
|
|
265
|
+
- `→` / `↓` to move to the next tab
|
|
266
|
+
- `Home` to move to the first tab
|
|
267
|
+
- `End` to move to the last tab
|
|
268
|
+
|
|
269
|
+
## 🛠️ API Reference
|
|
270
|
+
|
|
271
|
+
### Components
|
|
272
|
+
|
|
273
|
+
| Element | Description |
|
|
274
|
+
| -------------- | -------------------------------------- |
|
|
275
|
+
| `<tab-group>` | Container for the entire tab interface |
|
|
276
|
+
| `<tab-list>` | Container for tab buttons |
|
|
277
|
+
| `<tab-button>` | Individual tab trigger |
|
|
278
|
+
| `<tab-panel>` | Content container for each tab |
|
|
279
|
+
|
|
280
|
+
### Events
|
|
281
|
+
|
|
282
|
+
| Event | Detail | Description |
|
|
283
|
+
| ----------- | --------------------------------------------------------------------------------------- | --------------------------------- |
|
|
284
|
+
| `tabchange` | `{ previousIndex, currentIndex, previousTab, currentTab, previousPanel, currentPanel }` | Fired when the active tab changes |
|
|
285
|
+
|
|
286
|
+
## 🌟 Examples
|
|
287
|
+
|
|
288
|
+
Check out more examples in the [demo directory](https://github.com/magic-spells/tab-group/tree/main/demo).
|
|
289
|
+
|
|
290
|
+
## 📄 License
|
|
291
|
+
|
|
292
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
<p align="center">
|
|
297
|
+
Made with ✨ by <a href="https://github.com/cory-schulz">Cory Schulz</a>
|
|
298
|
+
</p>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Tab Group base styles
|
|
2
|
+
tab-group {
|
|
3
|
+
display: block;
|
|
4
|
+
width: 100%;
|
|
5
|
+
font-family: var(--font-family);
|
|
6
|
+
font-size: var(--font-size-base);
|
|
7
|
+
line-height: var(--line-height);
|
|
8
|
+
color: var(--color-text);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Tab List styles
|
|
12
|
+
tab-list {
|
|
13
|
+
display: flex;
|
|
14
|
+
border-bottom: var(
|
|
15
|
+
--tab-list-border-bottom,
|
|
16
|
+
1px solid var(--color-border)
|
|
17
|
+
);
|
|
18
|
+
margin-bottom: var(--spacing-md);
|
|
19
|
+
overflow-x: auto;
|
|
20
|
+
overflow-y: hidden;
|
|
21
|
+
padding: var(--tab-list-padding, 0.5rem 0.25rem 0);
|
|
22
|
+
gap: var(--tab-list-gap, 0.25rem);
|
|
23
|
+
justify-content: var(--tab-list-justify, flex-start);
|
|
24
|
+
background-color: var(--tab-list-background, transparent);
|
|
25
|
+
background-image: var(--tab-list-background-image, none);
|
|
26
|
+
border-radius: var(--tab-list-radius, 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Tab Button styles
|
|
30
|
+
tab-button {
|
|
31
|
+
display: block;
|
|
32
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
33
|
+
margin: var(--spacing-xs);
|
|
34
|
+
border: 1px solid transparent;
|
|
35
|
+
border-bottom: none;
|
|
36
|
+
background-color: var(--color-background);
|
|
37
|
+
color: var(--color-text);
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
border-radius: var(
|
|
40
|
+
--tab-button-radius,
|
|
41
|
+
var(--border-radius) var(--border-radius) 0 0
|
|
42
|
+
);
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
user-select: none;
|
|
45
|
+
transition: all var(--transition-duration);
|
|
46
|
+
margin-bottom: -1px;
|
|
47
|
+
position: relative;
|
|
48
|
+
font-size: var(--font-size-base);
|
|
49
|
+
font-weight: var(--tab-button-font-weight, normal);
|
|
50
|
+
text-align: center;
|
|
51
|
+
min-width: 100px;
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
background-color: var(--color-hover);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&:focus {
|
|
58
|
+
outline: none;
|
|
59
|
+
box-shadow: var(
|
|
60
|
+
--tab-button-focus-shadow,
|
|
61
|
+
0 0 0 2px var(--color-primary)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&[aria-selected='true'] {
|
|
66
|
+
background-color: var(
|
|
67
|
+
--tab-active-background,
|
|
68
|
+
var(--color-background)
|
|
69
|
+
);
|
|
70
|
+
border-color: var(--color-border);
|
|
71
|
+
border-bottom: 1px solid var(--color-background);
|
|
72
|
+
font-weight: var(--tab-active-font-weight, 600);
|
|
73
|
+
color: var(--tab-active-color, var(--color-primary));
|
|
74
|
+
box-shadow: var(--tab-active-shadow, none);
|
|
75
|
+
transform: var(--tab-active-transform, none);
|
|
76
|
+
|
|
77
|
+
&::after {
|
|
78
|
+
content: '';
|
|
79
|
+
position: absolute;
|
|
80
|
+
bottom: 0;
|
|
81
|
+
left: var(--tab-indicator-left, 0);
|
|
82
|
+
right: var(--tab-indicator-right, 0);
|
|
83
|
+
height: var(--tab-indicator-height, 2px);
|
|
84
|
+
background-color: var(
|
|
85
|
+
--tab-indicator-color,
|
|
86
|
+
var(--color-primary)
|
|
87
|
+
);
|
|
88
|
+
border-radius: var(--tab-indicator-radius, 0);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Tab Panel styles
|
|
94
|
+
tab-panel {
|
|
95
|
+
display: block;
|
|
96
|
+
padding: var(--panel-padding, var(--spacing-md));
|
|
97
|
+
background-color: var(--panel-background, var(--color-background));
|
|
98
|
+
border-radius: var(
|
|
99
|
+
--panel-radius,
|
|
100
|
+
0 0 var(--border-radius) var(--border-radius)
|
|
101
|
+
);
|
|
102
|
+
border: var(--panel-border, none);
|
|
103
|
+
border-top: var(--panel-border-top, none);
|
|
104
|
+
box-shadow: var(--panel-shadow, none);
|
|
105
|
+
transition: var(--panel-transition, all 0.3s ease);
|
|
106
|
+
margin: var(--panel-margin, 0);
|
|
107
|
+
min-height: var(--panel-min-height, 150px);
|
|
108
|
+
|
|
109
|
+
// When hidden (using the hidden attribute)
|
|
110
|
+
&[hidden] {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Responsive adjustments
|
|
116
|
+
@media (max-width: 768px) {
|
|
117
|
+
tab-list {
|
|
118
|
+
flex-wrap: wrap;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
tab-button {
|
|
122
|
+
flex: 1 0 auto;
|
|
123
|
+
text-align: center;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
File without changes
|